summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 04:20:26 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 04:20:26 +0000
commit044203039cebe3c05161f8f104a039d4744ca6d0 (patch)
tree1073c2308492e6aea4c66cb7436ee92db2abfd42
parentInitial commit. (diff)
downloadlibyang2-044203039cebe3c05161f8f104a039d4744ca6d0.tar.xz
libyang2-044203039cebe3c05161f8f104a039d4744ca6d0.zip
Adding upstream version 2.1.30.upstream/2.1.30
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.gitattributes3
-rw-r--r--.github/workflows/ci.yml219
-rw-r--r--.github/workflows/cifuzz.yml24
-rw-r--r--.github/workflows/codeql.yml41
-rw-r--r--.github/workflows/devel-push.yml109
-rw-r--r--.mailmap6
-rw-r--r--CMakeLists.txt477
-rw-r--r--CMakeModules/ABICheck.cmake66
-rw-r--r--CMakeModules/FindCMocka.cmake49
-rw-r--r--CMakeModules/FindPCRE2.cmake62
-rw-r--r--CMakeModules/FindUncrustify.cmake21
-rw-r--r--CMakeModules/GenCoverage.cmake118
-rw-r--r--CMakeModules/GenDoc.cmake28
-rw-r--r--CMakeModules/SourceFormat.cmake36
-rw-r--r--CMakeModules/UseCompat.cmake75
-rw-r--r--CMakeModules/uninstall.cmake27
-rw-r--r--CONTRIBUTING.md228
-rw-r--r--Doxyfile.in2457
-rw-r--r--FindLibYANG.cmake89
-rw-r--r--LICENSE28
-rw-r--r--README.md277
-rw-r--r--codecov.yml31
-rwxr-xr-xcompat/check_includes.sh44
-rw-r--r--compat/compat.c361
-rw-r--r--compat/compat.h.in203
-rw-r--r--compat/posix-shims/libgen.h1
-rw-r--r--compat/posix-shims/strings.h9
-rw-r--r--compat/posix-shims/unistd.h78
-rw-r--r--compat/strptime.c214
-rw-r--r--distro/README.md9
-rw-r--r--distro/config/apkg.toml10
-rw-r--r--distro/pkg/deb/README.Debian38
-rw-r--r--distro/pkg/deb/README.source66
-rw-r--r--distro/pkg/deb/changelog5
-rw-r--r--distro/pkg/deb/compat1
-rw-r--r--distro/pkg/deb/control75
-rw-r--r--distro/pkg/deb/copyright79
-rw-r--r--distro/pkg/deb/gbp.conf4
-rw-r--r--distro/pkg/deb/libyang2-dev.install3
-rw-r--r--distro/pkg/deb/libyang2-tools.examples1
-rw-r--r--distro/pkg/deb/libyang2-tools.install3
-rw-r--r--distro/pkg/deb/libyang2.install2
-rw-r--r--distro/pkg/deb/libyang2.symbols351
-rwxr-xr-xdistro/pkg/deb/rules12
-rw-r--r--distro/pkg/deb/source/format1
-rw-r--r--distro/pkg/deb/tests/control3
-rwxr-xr-xdistro/pkg/deb/tests/yanglint18
-rw-r--r--distro/pkg/deb/watch4
-rw-r--r--distro/pkg/rpm/libyang.spec94
-rwxr-xr-xdistro/scripts/make-archive.sh17
-rwxr-xr-xdistro/scripts/upstream-version.sh7
-rw-r--r--distro/tests/control1
-rwxr-xr-xdistro/tests/test-pkg-config.sh5
-rwxr-xr-xdistro/tests/test-yanglint.sh5
-rw-r--r--doc/build.dox139
-rw-r--r--doc/cesnet-style.css106
-rw-r--r--doc/compat_report.html7645
-rw-r--r--doc/logo.pngbin0 -> 2792 bytes
-rw-r--r--doc/transition.dox408
-rw-r--r--libyang.pc.in11
-rw-r--r--models/ietf-datastores@2018-02-14.h226
-rw-r--r--models/ietf-datastores@2018-02-14.yang117
-rw-r--r--models/ietf-inet-types@2013-07-15.h1189
-rw-r--r--models/ietf-inet-types@2013-07-15.yang457
-rw-r--r--models/ietf-yang-library@2019-01-04.h1497
-rw-r--r--models/ietf-yang-library@2019-01-04.yang544
-rw-r--r--models/ietf-yang-metadata@2016-08-05.h220
-rw-r--r--models/ietf-yang-metadata@2016-08-05.yang80
-rw-r--r--models/ietf-yang-schema-mount@2019-01-14.h651
-rw-r--r--models/ietf-yang-schema-mount@2019-01-14.yang224
-rw-r--r--models/ietf-yang-structure-ext@2020-06-17.h635
-rw-r--r--models/ietf-yang-structure-ext@2020-06-17.yang206
-rw-r--r--models/ietf-yang-types@2013-07-15.h1292
-rw-r--r--models/ietf-yang-types@2013-07-15.yang474
-rw-r--r--models/yang@2022-06-16.h483
-rw-r--r--models/yang@2022-06-16.yang194
-rw-r--r--src/common.c770
-rw-r--r--src/common.h626
-rw-r--r--src/config.h.in74
-rw-r--r--src/context.c1277
-rw-r--r--src/context.h668
-rw-r--r--src/dict.h122
-rw-r--r--src/diff.c2151
-rw-r--r--src/diff.h62
-rw-r--r--src/hash_table.c880
-rw-r--r--src/hash_table.h279
-rw-r--r--src/in.c329
-rw-r--r--src/in.h252
-rw-r--r--src/in_internal.h50
-rw-r--r--src/json.c1047
-rw-r--r--src/json.h139
-rw-r--r--src/libyang.h167
-rw-r--r--src/log.c773
-rw-r--r--src/log.h404
-rw-r--r--src/lyb.c125
-rw-r--r--src/lyb.h199
-rw-r--r--src/out.c768
-rw-r--r--src/out.h307
-rw-r--r--src/out_internal.h110
-rw-r--r--src/parser_common.c3567
-rw-r--r--src/parser_data.h461
-rw-r--r--src/parser_internal.h392
-rw-r--r--src/parser_json.c1819
-rw-r--r--src/parser_lyb.c1792
-rw-r--r--src/parser_schema.h179
-rw-r--r--src/parser_xml.c1816
-rw-r--r--src/parser_yang.c4827
-rw-r--r--src/parser_yin.c4012
-rw-r--r--src/path.c1193
-rw-r--r--src/path.h263
-rw-r--r--src/plugins.c550
-rw-r--r--src/plugins.h94
-rw-r--r--src/plugins_exts.c680
-rw-r--r--src/plugins_exts.h1048
-rw-r--r--src/plugins_exts/metadata.c243
-rw-r--r--src/plugins_exts/metadata.h66
-rw-r--r--src/plugins_exts/nacm.c223
-rw-r--r--src/plugins_exts/schema_mount.c1332
-rw-r--r--src/plugins_exts/structure.c558
-rw-r--r--src/plugins_exts/yangdata.c277
-rw-r--r--src/plugins_internal.h85
-rw-r--r--src/plugins_types.c1043
-rw-r--r--src/plugins_types.h1214
-rw-r--r--src/plugins_types/binary.c466
-rw-r--r--src/plugins_types/bits.c510
-rw-r--r--src/plugins_types/boolean.c165
-rw-r--r--src/plugins_types/date_and_time.c339
-rw-r--r--src/plugins_types/decimal64.c239
-rw-r--r--src/plugins_types/empty.c103
-rw-r--r--src/plugins_types/enumeration.c202
-rw-r--r--src/plugins_types/identityref.c352
-rw-r--r--src/plugins_types/instanceid.c382
-rw-r--r--src/plugins_types/instanceid_keys.c229
-rw-r--r--src/plugins_types/integer.c585
-rw-r--r--src/plugins_types/ipv4_address.c377
-rw-r--r--src/plugins_types/ipv4_address_no_zone.c221
-rw-r--r--src/plugins_types/ipv4_prefix.c337
-rw-r--r--src/plugins_types/ipv6_address.c378
-rw-r--r--src/plugins_types/ipv6_address_no_zone.c312
-rw-r--r--src/plugins_types/ipv6_prefix.c351
-rw-r--r--src/plugins_types/leafref.c140
-rw-r--r--src/plugins_types/node_instanceid.c320
-rw-r--r--src/plugins_types/string.c109
-rw-r--r--src/plugins_types/union.c585
-rw-r--r--src/plugins_types/xpath1.0.c521
-rw-r--r--src/printer_data.c159
-rw-r--r--src/printer_data.h196
-rw-r--r--src/printer_internal.h218
-rw-r--r--src/printer_json.c1010
-rw-r--r--src/printer_lyb.c1335
-rw-r--r--src/printer_schema.c222
-rw-r--r--src/printer_schema.h232
-rw-r--r--src/printer_tree.c4673
-rw-r--r--src/printer_xml.c607
-rw-r--r--src/printer_yang.c2657
-rw-r--r--src/printer_yin.c1501
-rw-r--r--src/schema_compile.c1798
-rw-r--r--src/schema_compile.h397
-rw-r--r--src/schema_compile_amend.c2547
-rw-r--r--src/schema_compile_amend.h181
-rw-r--r--src/schema_compile_node.c4218
-rw-r--r--src/schema_compile_node.h202
-rw-r--r--src/schema_features.c714
-rw-r--r--src/schema_features.h67
-rw-r--r--src/set.c247
-rw-r--r--src/set.h181
-rw-r--r--src/tree.h250
-rw-r--r--src/tree_data.c2943
-rw-r--r--src/tree_data.h2601
-rw-r--r--src/tree_data_common.c1626
-rw-r--r--src/tree_data_free.c241
-rw-r--r--src/tree_data_hash.c237
-rw-r--r--src/tree_data_internal.h590
-rw-r--r--src/tree_data_new.c1914
-rw-r--r--src/tree_edit.h306
-rw-r--r--src/tree_schema.c2178
-rw-r--r--src/tree_schema.h2248
-rw-r--r--src/tree_schema_common.c2617
-rw-r--r--src/tree_schema_free.c1737
-rw-r--r--src/tree_schema_free.h221
-rw-r--r--src/tree_schema_internal.h735
-rw-r--r--src/validation.c2029
-rw-r--r--src/validation.h107
-rw-r--r--src/version.h.in23
-rw-r--r--src/xml.c1402
-rw-r--r--src/xml.h209
-rw-r--r--src/xpath.c9883
-rw-r--r--src/xpath.h517
-rw-r--r--tests/CMakeLists.txt53
-rwxr-xr-xtests/cstr.sh503
-rw-r--r--tests/fuzz/CMakeLists.txt28
-rw-r--r--tests/fuzz/README.md64
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull114381
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull120312
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull12691
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull1269_21
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull12801
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number1
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings1
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull13481
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull14601
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull1460_21
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull15711
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull15851
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull16261
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull16931
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull1696_11
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_json/pull1696_21
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/issue10744
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/issue1131bin0 -> 119 bytes
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/issue11321
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2119
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_318
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1bin0 -> 202 bytes
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_21
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/pull15291
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/pull15371
-rw-r--r--tests/fuzz/corpus/lyd_parse_mem_xml/pull15621
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1004.yang10
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1025.yang16
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1027.yang9
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1040.yang13
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1041.yang34
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang9
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang13
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang13
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue1043.yang31
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue722.yang16
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue723.yang17
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue724.yang22
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue728.yang14
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue733.yang13
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue734.yang17
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue735.yang17
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue739.yang11
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue740.yang14
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue741.yang16
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue742.yang15
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue769.yang31
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue771.yangbin0 -> 16 bytes
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue772.yang54
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue773.yangbin0 -> 462 bytes
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue774.yang55
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue777.yang16
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue780.yangbin0 -> 212 bytes
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue788.yang8
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue789.yang10
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue791.yang3
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue791_2.yang13
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue795.yang7
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue804.yang7
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue805.yang7
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue807.yang9
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue826.yang12
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue827.yang10
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue872.yang7
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue874.yang28
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue970.yang18
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue973.yang10
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue975.yang28
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue976_a.yang12
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue976_b.yang32
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue979_a.yang41
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/issue979_b.yang13
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/pull1524.yang1
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/pull1568.yang4
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/pull1592.yang67
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/pull958.yang8
-rw-r--r--tests/fuzz/corpus/lys_parse_mem/pull960.yang9
-rw-r--r--tests/fuzz/fuzz_regression_test.c70
-rw-r--r--tests/fuzz/lyd_parse_mem_json.c86
-rw-r--r--tests/fuzz/lyd_parse_mem_xml.c86
-rw-r--r--tests/fuzz/lys_parse_mem.c37
-rw-r--r--tests/fuzz/main.c47
-rw-r--r--tests/fuzz/yang_parse_module.c39
-rw-r--r--tests/ld.supp8
-rw-r--r--tests/modules/yang/iana-if-type@2014-05-08.yang1547
-rw-r--r--tests/modules/yang/ietf-interfaces@2014-05-08.yang725
-rw-r--r--tests/modules/yang/ietf-ip@2014-06-16.yang758
-rw-r--r--tests/modules/yang/ietf-netconf-acm@2018-02-14.yang464
-rw-r--r--tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang385
-rw-r--r--tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang140
-rw-r--r--tests/modules/yang/ietf-netconf@2011-06-01.yang934
-rw-r--r--tests/modules/yang/ietf-origin@2018-02-14.yang147
-rw-r--r--tests/modules/yang/ietf-restconf@2017-01-26.yang278
-rw-r--r--tests/modules/yang/notifications@2008-07-14.yang95
-rw-r--r--tests/modules/yang/sm-extension.yang17
-rw-r--r--tests/modules/yang/sm-mod.yang9
-rw-r--r--tests/modules/yang/sm-modp.yang24
-rw-r--r--tests/modules/yang/sm-rpcnotif.yang25
-rw-r--r--tests/modules/yang/sm.yang39
-rw-r--r--tests/perf/CMakeLists.txt14
-rw-r--r--tests/perf/perf.c814
-rw-r--r--tests/perf/perf.yang27
-rw-r--r--tests/plugins/CMakeLists.txt19
-rw-r--r--tests/plugins/invalid.c41
-rw-r--r--tests/plugins/simple.c92
-rw-r--r--tests/style/CMakeLists.txt12
-rw-r--r--tests/style/cpp_compat.c95
-rw-r--r--tests/tests_config.h.in27
-rw-r--r--tests/utests/CMakeLists.txt77
-rw-r--r--tests/utests/basic/test_common.c416
-rw-r--r--tests/utests/basic/test_context.c1087
-rw-r--r--tests/utests/basic/test_hash_table.c271
-rw-r--r--tests/utests/basic/test_inout.c401
-rw-r--r--tests/utests/basic/test_json.c794
-rw-r--r--tests/utests/basic/test_plugins.c109
-rw-r--r--tests/utests/basic/test_set.c287
-rw-r--r--tests/utests/basic/test_xml.c690
-rw-r--r--tests/utests/basic/test_xpath.c1071
-rw-r--r--tests/utests/basic/test_yanglib.c144
-rw-r--r--tests/utests/data/test_diff.c1221
-rw-r--r--tests/utests/data/test_lyb.c2841
-rw-r--r--tests/utests/data/test_merge.c756
-rw-r--r--tests/utests/data/test_new.c446
-rw-r--r--tests/utests/data/test_parser_json.c793
-rw-r--r--tests/utests/data/test_parser_xml.c836
-rw-r--r--tests/utests/data/test_printer_xml.c343
-rw-r--r--tests/utests/data/test_tree_data.c597
-rw-r--r--tests/utests/data/test_validation.c1460
-rw-r--r--tests/utests/extensions/test_metadata.c211
-rw-r--r--tests/utests/extensions/test_nacm.c124
-rw-r--r--tests/utests/extensions/test_schema_mount.c1566
-rw-r--r--tests/utests/extensions/test_structure.c255
-rw-r--r--tests/utests/extensions/test_yangdata.c273
-rw-r--r--tests/utests/node/list.c1632
-rw-r--r--tests/utests/restriction/test_pattern.c397
-rw-r--r--tests/utests/restriction/test_range.c431
-rw-r--r--tests/utests/schema/test_printer_tree.c2405
-rw-r--r--tests/utests/schema/test_schema.c1887
-rw-r--r--tests/utests/schema/test_tree_schema_compile.c3837
-rw-r--r--tests/utests/schema/test_yang.c1750
-rw-r--r--tests/utests/schema/test_yin.c3584
-rw-r--r--tests/utests/types/binary.c269
-rw-r--r--tests/utests/types/bits.c1117
-rw-r--r--tests/utests/types/boolean.c111
-rw-r--r--tests/utests/types/decimal64.c125
-rw-r--r--tests/utests/types/empty.c108
-rw-r--r--tests/utests/types/enumeration.c114
-rw-r--r--tests/utests/types/identityref.c123
-rw-r--r--tests/utests/types/inet_types.c149
-rw-r--r--tests/utests/types/instanceid.c292
-rw-r--r--tests/utests/types/instanceid_keys.c77
-rw-r--r--tests/utests/types/int16.c75
-rw-r--r--tests/utests/types/int32.c75
-rw-r--r--tests/utests/types/int64.c83
-rw-r--r--tests/utests/types/int8.c1765
-rw-r--r--tests/utests/types/leafref.c222
-rw-r--r--tests/utests/types/string.c1410
-rw-r--r--tests/utests/types/uint16.c75
-rw-r--r--tests/utests/types/uint32.c75
-rw-r--r--tests/utests/types/uint64.c83
-rw-r--r--tests/utests/types/uint8.c77
-rw-r--r--tests/utests/types/union.c136
-rw-r--r--tests/utests/types/yang_types.c223
-rw-r--r--tests/utests/utests.h1505
-rw-r--r--tools/CMakeLists.txt15
-rw-r--r--tools/config.h.in20
-rw-r--r--tools/lint/CMakeLists.txt90
-rw-r--r--tools/lint/cmd.c259
-rw-r--r--tools/lint/cmd.h69
-rw-r--r--tools/lint/cmd_add.c176
-rw-r--r--tools/lint/cmd_clear.c99
-rw-r--r--tools/lint/cmd_data.c328
-rw-r--r--tools/lint/cmd_feature.c135
-rw-r--r--tools/lint/cmd_list.c89
-rw-r--r--tools/lint/cmd_load.c139
-rw-r--r--tools/lint/cmd_print.c264
-rw-r--r--tools/lint/cmd_searchpath.c90
-rw-r--r--tools/lint/common.c868
-rw-r--r--tools/lint/common.h257
-rw-r--r--tools/lint/completion.c379
-rw-r--r--tools/lint/completion.h25
-rw-r--r--tools/lint/configuration.c125
-rw-r--r--tools/lint/configuration.h36
-rw-r--r--tools/lint/examples/README.md471
-rw-r--r--tools/lint/examples/action-reply.xml8
-rw-r--r--tools/lint/examples/action.xml8
-rw-r--r--tools/lint/examples/config-acm.xml24
-rw-r--r--tools/lint/examples/config-missing-key.xml24
-rw-r--r--tools/lint/examples/config-unknown-element.xml27
-rw-r--r--tools/lint/examples/data-acm.xml27
-rw-r--r--tools/lint/examples/data-ip.xml12
-rw-r--r--tools/lint/examples/data-malformed-xml.xml27
-rw-r--r--tools/lint/examples/data-malformed-xml2.xml26
-rw-r--r--tools/lint/examples/data-missing-key.xml26
-rw-r--r--tools/lint/examples/data-out-of-range-value.xml27
-rw-r--r--tools/lint/examples/datastore.xml29
-rw-r--r--tools/lint/examples/iana-if-type.yang1547
-rw-r--r--tools/lint/examples/ietf-interfaces.yang725
-rw-r--r--tools/lint/examples/ietf-ip.yang758
-rw-r--r--tools/lint/examples/ietf-netconf-acm-when.yang412
-rw-r--r--tools/lint/examples/ietf-netconf-acm-when.yin447
-rw-r--r--tools/lint/examples/ietf-netconf-acm-when2.yin447
-rw-r--r--tools/lint/examples/ietf-netconf-acm.yang411
-rw-r--r--tools/lint/examples/module1.yang5
-rw-r--r--tools/lint/examples/module1b.yang5
-rw-r--r--tools/lint/examples/module2.yang5
-rw-r--r--tools/lint/examples/module2.yin10
-rw-r--r--tools/lint/examples/module3.yang8
-rw-r--r--tools/lint/examples/module4.yang52
-rw-r--r--tools/lint/examples/nested-notification.xml8
-rw-r--r--tools/lint/examples/notification.xml3
-rw-r--r--tools/lint/examples/rpc-reply.xml5
-rw-r--r--tools/lint/examples/rpc.xml3
-rw-r--r--tools/lint/examples/sm-context-extension.xml64
-rw-r--r--tools/lint/examples/sm-context-main.xml54
-rw-r--r--tools/lint/examples/sm-data.xml19
-rw-r--r--tools/lint/examples/sm-extension.yang39
-rw-r--r--tools/lint/examples/sm-main.yang32
-rw-r--r--tools/lint/examples/sm-mod.yang21
-rw-r--r--tools/lint/linenoise/LICENSE25
-rw-r--r--tools/lint/linenoise/linenoise.c1218
-rw-r--r--tools/lint/linenoise/linenoise.h94
-rw-r--r--tools/lint/main.c102
-rw-r--r--tools/lint/main_ni.c1027
-rw-r--r--tools/lint/main_ni_only.c22
-rw-r--r--tools/lint/tests/expect/common.exp68
-rwxr-xr-xtools/lint/tests/expect/completion.exp60
-rwxr-xr-xtools/lint/tests/expect/feature.exp20
-rwxr-xr-xtools/lint/tests/expect/list.exp20
-rwxr-xr-xtools/lint/tests/shunit2/feature.sh19
-rwxr-xr-xtools/lint/tests/shunit2/list.sh28
-rw-r--r--tools/lint/yanglint.1136
-rw-r--r--tools/re/CMakeLists.txt20
-rw-r--r--tools/re/main.c309
-rw-r--r--tools/re/yangre.1118
-rw-r--r--uncrustify.cfg3566
428 files changed, 193679 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..8036b4c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# the tests check that LF is kept as a LF in the checkout
+tests/modules/yang/ietf-netconf@*.yang -text
+tests/modules/yang/ietf-netconf-nmda@*.yang -text
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..210b5be
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,219 @@
+name: libyang CI
+on:
+ push:
+ branches:
+ - master
+ - devel
+ pull_request:
+ branches:
+ - master
+ - devel
+
+jobs:
+ build-unix:
+ name: ${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: "Release, gcc",
+ os: "ubuntu-22.04",
+ build-type: "Release",
+ cc: "gcc",
+ options: "-DENABLE_TESTS=ON",
+ packager: "sudo apt-get",
+ # no expect because stdout seems to be redirected
+ packages: "libcmocka-dev shunit2",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+ - {
+ name: "Release, clang",
+ os: "ubuntu-22.04",
+ build-type: "Release",
+ cc: "clang",
+ options: "-DENABLE_TESTS=ON",
+ packager: "sudo apt-get",
+ packages: "libcmocka-dev shunit2",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+ - {
+ name: "Debug, gcc",
+ os: "ubuntu-22.04",
+ build-type: "Debug",
+ cc: "gcc",
+ options: "",
+ packager: "sudo apt-get",
+ packages: "libcmocka-dev valgrind shunit2",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+ - {
+ name: "Debug, clang",
+ os: "ubuntu-22.04",
+ build-type: "Debug",
+ cc: "clang",
+ options: "",
+ packager: "sudo apt-get",
+ # no valgrind because it does not support DWARF5 yet generated by clang 14
+ packages: "libcmocka-dev shunit2",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+ - {
+ name: "Release, macOS 11, clang",
+ os: "macos-11",
+ build-type: "Release",
+ cc: "clang",
+ options: "-DENABLE_TESTS=ON -DPATH_EXPECT=",
+ packager: "brew",
+ packages: "cmocka shunit2",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+ - {
+ name: "ASAN and UBSAN",
+ os: "ubuntu-22.04",
+ build-type: "Debug",
+ cc: "clang",
+ options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_TESTS=ON -DENABLE_VALGRIND_TESTS=OFF",
+ packager: "sudo apt-get",
+ packages: "libcmocka-dev",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+ - {
+ name: "ABI Check",
+ os: "ubuntu-22.04",
+ build-type: "ABICheck",
+ cc: "gcc",
+ options: "",
+ packager: "sudo apt-get",
+ packages: "libcmocka-dev abi-dumper abi-compliance-checker",
+ snaps: "core universal-ctags",
+ make-prepend: "",
+ make-target: "abi-check"
+ }
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Deps-packages
+ shell: bash
+ run: |
+ ${{ matrix.config.packager }} update
+ if ${{ matrix.config.packages != '' }}
+ then ${{ matrix.config.packager }} install ${{ matrix.config.packages }}
+ fi
+ if ${{ matrix.config.snaps != '' }}
+ then sudo snap install ${{ matrix.config.snaps }}
+ fi
+
+ - name: Deps-uncrustify
+ shell: bash
+ working-directory: ${{ github.workspace }}
+ run: |
+ git clone --branch uncrustify-0.75.1 https://github.com/uncrustify/uncrustify
+ cd uncrustify
+ mkdir build
+ cd build
+ CC=${{ matrix.config.cc }} cmake ..
+ make
+ sudo make install
+ if: ${{ matrix.config.name == 'Debug, gcc' }}
+
+ - name: Configure
+ shell: bash
+ working-directory: ${{ github.workspace }}
+ run: |
+ mkdir build
+ cd build
+ CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} ..
+
+ - name: Build
+ shell: bash
+ working-directory: ${{ github.workspace }}/build
+ run: |
+ export LC_ALL=C.UTF-8
+ export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH
+ ${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }}
+
+ - name: Test
+ shell: bash
+ working-directory: ${{ github.workspace }}/build
+ run: ctest --output-on-failure
+
+ build-windows:
+ name: ${{ matrix.name }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - name: "Windows 2022 MSVC 16 LF"
+ os: windows-2022
+ triplet: x64-windows
+ build_type: Release
+ generators: "Visual Studio 17 2022"
+ autocrlf: input
+ eol: lf
+
+ - name: "Windows 2022 MSVC 16 no autoCRLF"
+ os: windows-2022
+ triplet: x64-windows
+ build_type: Release
+ generators: "Visual Studio 17 2022"
+
+ steps:
+ - name: Unix line endings in git
+ if: matrix.autocrlf
+ run: |
+ git config --global core.autocrlf ${{ matrix.autocrlf }}
+
+ - name: Unix line endings in git
+ if: matrix.eol
+ run: |
+ git config --global core.eol ${{ matrix.eol }}
+
+ - uses: actions/checkout@v3
+
+ - name: Get number of CPU cores
+ id: cpu-cores
+ uses: SimenB/github-actions-cpu-cores@v1
+
+ - name: Install Windows dependencies
+ run: vcpkg install --triplet=${{ matrix.triplet }} pcre2 pthreads dirent dlfcn-win32 cmocka getopt
+
+ - name: Configure
+ shell: bash
+ run: |
+ cmake \
+ -S '${{ github.workspace }}/' \
+ -B '${{ github.workspace }}/'../build \
+ -G '${{ matrix.generators }}' \
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
+ -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
+ -DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT//\\//}/scripts/buildsystems/vcpkg.cmake \
+ -DENABLE_TESTS=ON \
+ '-DCMAKE_INSTALL_PREFIX:PATH=${{ github.workspace }}'/../target
+
+ - name: Build
+ working-directory: '${{ github.workspace }}/../build'
+ run: cmake --build . -j${{ steps.cpu-cores.outputs.count }} --config ${{ matrix.build_type }}
+
+ - name: Test
+ working-directory: '${{ github.workspace }}/../build'
+ run: ctest --output-on-failure -j${{ steps.cpu-cores.outputs.count }} --build-config ${{ matrix.build_type }}
+
+ - name: Install
+ working-directory: '${{ github.workspace }}/../build'
+ run: cmake --install . --strip
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
new file mode 100644
index 0000000..89b7bbd
--- /dev/null
+++ b/.github/workflows/cifuzz.yml
@@ -0,0 +1,24 @@
+name: CIFuzz
+on: [pull_request]
+jobs:
+ Fuzzing:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ id: build
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libyang'
+ dry-run: false
+ - name: Run Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libyang'
+ fuzz-seconds: 300
+ dry-run: false
+ - name: Upload Crash
+ uses: actions/upload-artifact@v3
+ if: failure() && steps.build.outcome == 'success'
+ with:
+ name: artifacts
+ path: ./out/artifacts
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..264faa8
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,41 @@
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ "master", "devel" ]
+ pull_request:
+ branches: [ "devel" ]
+ schedule:
+ - cron: "38 17 * * 4"
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ cpp ]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ queries: +security-and-quality
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
+ with:
+ category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/devel-push.yml b/.github/workflows/devel-push.yml
new file mode 100644
index 0000000..7dddc13
--- /dev/null
+++ b/.github/workflows/devel-push.yml
@@ -0,0 +1,109 @@
+name: libyang devel push
+on:
+ push:
+ branches:
+ - devel
+
+env:
+ COVERITY_PROJECT: CESNET%2Flibyang
+
+jobs:
+ build:
+ name: ${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: "Coverity",
+ os: "ubuntu-latest",
+ build-type: "Debug",
+ cc: "clang",
+ options: "",
+ packager: "sudo apt-get",
+ packages: "",
+ snaps: "",
+ make-prepend: "cov-build --dir cov-int",
+ make-target: ""
+ }
+ - {
+ name: "Codecov",
+ os: "ubuntu-latest",
+ build-type: "Debug",
+ cc: "gcc",
+ options: "-DENABLE_COVERAGE=ON",
+ packager: "sudo apt-get",
+ packages: "libcmocka-dev lcov",
+ snaps: "",
+ make-prepend: "",
+ make-target: ""
+ }
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Deps-packages
+ shell: bash
+ run: |
+ ${{ matrix.config.packager }} update
+ if ${{ matrix.config.packages != '' }}
+ then ${{ matrix.config.packager }} install ${{ matrix.config.packages }}
+ fi
+ if ${{ matrix.config.snaps != '' }}
+ then sudo snap install ${{ matrix.config.snaps }}
+ fi
+
+ - name: Deps-coverity
+ shell: bash
+ working-directory: ${{ github.workspace }}
+ run: |
+ wget -q https://scan.coverity.com/download/linux64 --post-data "token=$TOKEN&project=$COVERITY_PROJECT" -O coverity-tools.tar.gz
+ mkdir coverity-tools
+ tar xzf coverity-tools.tar.gz --strip 1 -C coverity-tools
+ env:
+ TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
+ if: ${{ matrix.config.name == 'Coverity' }}
+
+ - name: Configure
+ shell: bash
+ working-directory: ${{ github.workspace }}
+ run: |
+ mkdir build
+ cd build
+ CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} ..
+
+ - name: Build
+ shell: bash
+ working-directory: ${{ github.workspace }}/build
+ run: |
+ export LC_ALL=C.UTF-8
+ export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH
+ ${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }}
+
+ - name: Test
+ shell: bash
+ working-directory: ${{ github.workspace }}/build
+ run: ctest --output-on-failure
+
+ - name: Upload to Coverity.com
+ shell: bash
+ working-directory: ${{ github.workspace }}/build
+ run: |
+ tar czvf libyang.tgz cov-int
+ curl \
+ --form token=$TOKEN \
+ --form email=mvasko@cesnet.cz \
+ --form file=@libyang.tgz \
+ --form version="`./yanglint -v | cut -d\" \" -f2`" \
+ --form description="libyang YANG library" \
+ https://scan.coverity.com/builds?project=$COVERITY_PROJECT
+ env:
+ TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
+ if: ${{ matrix.config.name == 'Coverity' }}
+
+ - name: Upload to Codecov.io
+ shell: bash
+ working-directory: ${{ github.workspace }}/build
+ run: bash <(curl -s https://codecov.io/bash)
+ if: ${{ matrix.config.name == 'Codecov' }}
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..0618d05
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,6 @@
+David Sedlák <xsedla1d@stud.fit.vutbr.cz>
+FredGan <ganshaolong@vip.qq.com>
+Juraj Vijtiuk <juraj.vijtiuk@sartura.hr> <vijtiuk.juraj@gmail.com> <30860583+jvijtiuk@users.noreply.github.com>
+Juraj Vijtiuk <juraj.vijtiuk@sartura.hr> <30860583+jvijtiuk@users.noreply.github.com>
+Michal Vasko <mvasko@cesnet.cz>
+Radek Krejci <rkrejci@cesnet.cz>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..6470ce3
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,477 @@
+if(WIN32)
+ cmake_minimum_required(VERSION 3.22.0)
+else()
+ cmake_minimum_required(VERSION 2.8.12)
+endif()
+
+# force out-of-source build
+if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
+ message(FATAL_ERROR "In-source build is not allowed. Please make a standalone build directory and run CMake from there. You may need to remove CMakeCache.txt.")
+endif()
+
+project(libyang C)
+
+# include custom Modules
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/")
+
+include(GNUInstallDirs)
+include(CheckSymbolExists)
+include(UseCompat)
+include(ABICheck)
+include(SourceFormat)
+include(GenDoc)
+include(GenCoverage)
+
+# set default build type if not specified by user
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Debug)
+endif()
+# normalize build type string
+# see https://github.com/CESNET/libyang/pull/1692 for why CMAKE_C_FLAGS_<type> are not used directly
+string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_UPPER)
+if ("${BUILD_TYPE_UPPER}" STREQUAL "RELEASE")
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE)
+ set(CMAKE_C_FLAGS "-DNDEBUG -O2 ${CMAKE_C_FLAGS}")
+elseif("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
+ set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build Type" FORCE)
+ set(CMAKE_C_FLAGS "-g3 -O0 ${CMAKE_C_FLAGS}")
+elseif("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBINFO")
+ set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build Type" FORCE)
+elseif("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBUG")
+ set(CMAKE_BUILD_TYPE "RelWithDebug" CACHE STRING "Build Type" FORCE)
+elseif("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
+ set(CMAKE_BUILD_TYPE "ABICheck" CACHE STRING "Build Type" FORCE)
+ set(CMAKE_C_FLAGS "-g -Og ${CMAKE_C_FLAGS}")
+elseif("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY")
+ set(CMAKE_BUILD_TYPE "DocOnly" CACHE STRING "Build Type" FORCE)
+endif()
+
+#
+# variables
+#
+
+set(LIBYANG_DESCRIPTION "libyang is YANG data modelling language parser and toolkit written (and providing API) in C.")
+
+# Correct RPATH usage on OS X
+set(CMAKE_MACOSX_RPATH TRUE)
+
+# keep all binaries in the build directory
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+
+# set version of the project
+set(LIBYANG_MAJOR_VERSION 2)
+set(LIBYANG_MINOR_VERSION 1)
+set(LIBYANG_MICRO_VERSION 30)
+set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_MICRO_VERSION})
+# set version of the library
+set(LIBYANG_MAJOR_SOVERSION 2)
+set(LIBYANG_MINOR_SOVERSION 29)
+set(LIBYANG_MICRO_SOVERSION 2)
+set(LIBYANG_SOVERSION_FULL ${LIBYANG_MAJOR_SOVERSION}.${LIBYANG_MINOR_SOVERSION}.${LIBYANG_MICRO_SOVERSION})
+set(LIBYANG_SOVERSION ${LIBYANG_MAJOR_SOVERSION})
+
+if(WIN32)
+ set(C_STANDARD 11)
+ set(C_STANDARD_REQUIRED ON)
+ set(CMAKE_C_FLAGS "/Zc:preprocessor /W3 /wd4711 /w14013 /utf-8 ${CMAKE_C_FLAGS}")
+else()
+ # global C flags
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -std=c11")
+endif()
+
+include_directories(${PROJECT_BINARY_DIR}/src ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/plugins_exts)
+
+# type plugins are separate because they have their documentation generated
+set(type_plugins
+ src/plugins_types/binary.c
+ src/plugins_types/bits.c
+ src/plugins_types/boolean.c
+ src/plugins_types/decimal64.c
+ src/plugins_types/empty.c
+ src/plugins_types/enumeration.c
+ src/plugins_types/identityref.c
+ src/plugins_types/instanceid.c
+ src/plugins_types/instanceid_keys.c
+ src/plugins_types/integer.c
+ src/plugins_types/leafref.c
+ src/plugins_types/string.c
+ src/plugins_types/union.c
+ src/plugins_types/ipv4_address.c
+ src/plugins_types/ipv4_address_no_zone.c
+ src/plugins_types/ipv6_address.c
+ src/plugins_types/ipv6_address_no_zone.c
+ src/plugins_types/ipv4_prefix.c
+ src/plugins_types/ipv6_prefix.c
+ src/plugins_types/date_and_time.c
+ src/plugins_types/xpath1.0.c
+ src/plugins_types/node_instanceid.c)
+
+set(libsrc
+ src/common.c
+ src/log.c
+ src/hash_table.c
+ src/set.c
+ src/path.c
+ src/diff.c
+ src/context.c
+ src/json.c
+ src/tree_data.c
+ src/tree_data_free.c
+ src/tree_data_common.c
+ src/tree_data_hash.c
+ src/tree_data_new.c
+ src/parser_xml.c
+ src/parser_json.c
+ src/parser_lyb.c
+ src/out.c
+ src/printer_data.c
+ src/printer_xml.c
+ src/printer_json.c
+ src/printer_lyb.c
+ src/schema_compile.c
+ src/schema_compile_node.c
+ src/schema_compile_amend.c
+ src/schema_features.c
+ src/tree_schema.c
+ src/tree_schema_free.c
+ src/tree_schema_common.c
+ src/in.c
+ src/lyb.c
+ src/parser_common.c
+ src/parser_yang.c
+ src/parser_yin.c
+ src/printer_schema.c
+ src/printer_yang.c
+ src/printer_yin.c
+ src/printer_tree.c
+ src/plugins.c
+ src/plugins_types.c
+ src/plugins_exts.c
+ src/plugins_exts/metadata.c
+ src/plugins_exts/nacm.c
+ src/plugins_exts/yangdata.c
+ src/plugins_exts/schema_mount.c
+ src/plugins_exts/structure.c
+ src/xml.c
+ src/xpath.c
+ src/validation.c
+ ${type_plugins})
+
+set(headers
+ src/context.h
+ src/dict.h
+ src/in.h
+ src/libyang.h
+ src/log.h
+ src/out.h
+ src/parser_data.h
+ src/parser_schema.h
+ src/plugins.h
+ src/plugins_exts.h
+ src/plugins_exts/metadata.h
+ src/plugins_types.h
+ src/printer_data.h
+ src/printer_schema.h
+ src/set.h
+ src/tree.h
+ src/tree_data.h
+ src/tree_edit.h
+ src/tree_schema.h)
+
+set(internal_headers
+ src/common.h
+ src/diff.h
+ src/hash_table.h
+ src/in_internal.h
+ src/json.h
+ src/lyb.h
+ src/out_internal.h
+ src/parser_internal.h
+ src/path.h
+ src/plugins_internal.h
+ src/printer_internal.h
+ src/schema_compile.h
+ src/schema_compile_amend.h
+ src/schema_compile_node.h
+ src/schema_features.h
+ src/tree_data_internal.h
+ src/tree_schema_internal.h
+ src/validation.h
+ src/xml.h
+ src/xpath.h)
+
+set(gen_headers
+ src/version.h
+ src/config.h)
+
+# files to generate doxygen from
+set(doxy_files
+ doc/build.dox
+ doc/transition.dox
+ ${headers}
+ ${PROJECT_BINARY_DIR}/src/version.h
+ ${type_plugins})
+
+# project (doxygen) logo
+set(project_logo
+ doc/logo.png)
+
+# source files to be covered by the 'format' target
+set(format_sources
+ compat/*.c
+ compat/*.h*
+ src/*.c
+ src/*.h
+ src/plugins_exts/*
+ src/plugins_types/*)
+#
+# options
+#
+
+if(("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") OR ("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBINFO"))
+ option(ENABLE_TESTS "Build tests" ON)
+ option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" ON)
+else()
+ option(ENABLE_TESTS "Build tests" OFF)
+ option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" OFF)
+endif()
+option(ENABLE_PERF_TESTS "Build performance tests" OFF)
+option(ENABLE_COVERAGE "Build code coverage report from tests" OFF)
+option(ENABLE_FUZZ_TARGETS "Build target programs suitable for fuzzing with AFL" OFF)
+option(ENABLE_INTERNAL_DOCS "Generate doxygen documentation also from internal headers" OFF)
+set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/libyang" CACHE STRING "Directory where to copy the YANG modules to")
+
+if(ENABLE_INTERNAL_DOCS)
+ set(doxy_files ${doxy_files} ${internal_headers})
+ set(INTERNAL_DOCS YES)
+else()
+ set(INTERNAL_DOCS NO)
+endif()
+
+set(LYD_VALUE_SIZE "24" CACHE STRING "Maximum size in bytes of data node values that do not need to be allocated dynamically, minimum is 8")
+if(LYD_VALUE_SIZE LESS 8)
+ message(FATAL_ERROR "Data node value size \"${LYD_VALUE_SIZE}\" is not valid.")
+endif()
+set(PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libyang" CACHE STRING "Directory with libyang plugins (extensions and user types)")
+set(PLUGINS_DIR_EXTENSIONS "${PLUGINS_DIR}/extensions" CACHE STRING "Directory with libyang user extensions plugins")
+set(PLUGINS_DIR_TYPES "${PLUGINS_DIR}/types" CACHE STRING "Directory with libyang user types plugins")
+
+# by default build shared library
+# static build requires static libpcre2 library
+option(ENABLE_STATIC "Build static (.a) library" OFF)
+
+#
+# checks
+#
+if(ENABLE_STATIC)
+ message(STATUS "Disabling tests for static build")
+ set(ENABLE_TESTS OFF)
+ set(ENABLE_VALGRIND_TESTS OFF)
+endif()
+
+if(ENABLE_VALGRIND_TESTS)
+ if(NOT ENABLE_TESTS)
+ message(WARNING "Tests are disabled! Disabling memory leak tests.")
+ set(ENABLE_VALGRIND_TESTS OFF)
+ else()
+ find_program(VALGRIND_FOUND valgrind)
+ if(NOT VALGRIND_FOUND)
+ message(WARNING "valgrind executable not found! Disabling memory leak tests.")
+ set(ENABLE_VALGRIND_TESTS OFF)
+ endif()
+ endif()
+endif()
+
+if(ENABLE_TESTS)
+ find_package(CMocka 1.0.1)
+ if(NOT CMOCKA_FOUND)
+ message(STATUS "Disabling tests because of missing CMocka")
+ set(ENABLE_TESTS OFF)
+ endif()
+endif()
+
+if(ENABLE_PERF_TESTS)
+ find_path(VALGRIND_INCLUDE_DIR
+ NAMES
+ valgrind/callgrind.h
+ PATHS
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include)
+ if(VALGRIND_INCLUDE_DIR)
+ set(HAVE_CALLGRIND 1)
+ else()
+ message(STATUS "Disabling callgrind macros in performance tests because of missing valgrind headers")
+ endif()
+endif()
+
+if(ENABLE_COVERAGE)
+ gen_coverage_enable(${ENABLE_TESTS})
+endif()
+
+if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
+ # enable before adding tests to let them detect that format checking is available - one of the tests is format checking
+ source_format_enable(0.76)
+endif()
+
+# generate files
+configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/src/config.h @ONLY)
+configure_file(${PROJECT_SOURCE_DIR}/src/version.h.in ${PROJECT_BINARY_DIR}/src/version.h @ONLY)
+
+# DOC-only target with no extra dependencies
+if("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY")
+ gen_doc("${doxy_files}" ${LIBYANG_VERSION} ${LIBYANG_DESCRIPTION} ${project_logo})
+ return()
+endif()
+
+#
+# targets
+#
+
+# link compat
+use_compat()
+
+# create static libyang library
+if(ENABLE_STATIC)
+ add_definitions(-DSTATIC)
+ set(CMAKE_EXE_LINKER_FLAGS -static)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
+ set(CMAKE_LINK_SEARCH_START_STATIC TRUE)
+ set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS) # remove -Wl,-Bdynamic
+ set(CMAKE_EXE_LINK_DYNAMIC_CXX_FLAGS)
+ add_library(yang STATIC ${libsrc} ${compatsrc})
+else()
+ set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
+ add_library(yangobj OBJECT ${libsrc} ${compatsrc})
+ if(NOT WIN32)
+ set_target_properties(yangobj PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
+ endif()
+ target_compile_definitions(yangobj PRIVATE LIBYANG_BUILD)
+ add_library(yang SHARED $<TARGET_OBJECTS:yangobj>)
+
+ if(WIN32)
+ find_package(dlfcn-win32 REQUIRED)
+ set(CMAKE_DL_LIBS dlfcn-win32::dl)
+ endif()
+
+ #link dl
+ target_link_libraries(yang ${CMAKE_DL_LIBS})
+endif()
+
+if(WIN32)
+ find_path(DIRENT_INCLUDE_DIR NAMES dirent.h REQUIRED)
+ message(STATUS "Found <dirent.h> at ${DIRENT_INCLUDE_DIR}")
+
+ set(COMPAT_POSIX_INCLUDES
+ ${CMAKE_CURRENT_SOURCE_DIR}/compat/posix-shims
+ ${DIRENT_INCLUDE_DIR})
+
+ if(TARGET yangobj)
+ target_include_directories(yangobj PRIVATE ${COMPAT_POSIX_INCLUDES})
+ endif()
+ target_include_directories(yang PRIVATE ${COMPAT_POSIX_INCLUDES})
+ include_directories(${COMPAT_POSIX_INCLUDES})
+
+ find_package(pthreads REQUIRED)
+ set(COMPAT_WIN_LIBRARIES PThreads4W::PThreads4W shlwapi.lib ws2_32)
+ target_link_libraries(yang ${COMPAT_WIN_LIBRARIES})
+endif()
+
+set_target_properties(yang PROPERTIES VERSION ${LIBYANG_SOVERSION_FULL} SOVERSION ${LIBYANG_SOVERSION})
+
+if(NOT WIN32)
+ # link math
+ target_link_libraries(yang m)
+endif()
+
+# find pthreads
+set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
+find_package(Threads REQUIRED)
+if(ENABLE_STATIC)
+ target_link_libraries(yang -Wl,--whole-archive ${CMAKE_THREAD_LIBS_INIT} -Wl,--no-whole-archive)
+else()
+ target_link_libraries(yang ${CMAKE_THREAD_LIBS_INIT})
+endif()
+
+# find PCRE2 library
+unset(PCRE2_LIBRARY CACHE)
+find_package(PCRE2 10.21 REQUIRED)
+include_directories(${PCRE2_INCLUDE_DIRS})
+target_link_libraries(yang ${PCRE2_LIBRARIES})
+
+# generated header list
+foreach(h IN LISTS gen_headers)
+ list(APPEND g_headers ${PROJECT_BINARY_DIR}/${h})
+endforeach()
+
+# install the modules
+install(DIRECTORY "${PROJECT_SOURCE_DIR}/models/" DESTINATION ${YANG_MODULE_DIR} FILES_MATCHING PATTERN "*.yang")
+
+# install all library files
+install(TARGETS yang DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(FILES ${headers} ${g_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libyang)
+
+find_package(PkgConfig)
+if(PKG_CONFIG_FOUND)
+ # generate and install pkg-config file
+ configure_file("libyang.pc.in" "libyang.pc" @ONLY)
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libyang.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+ # check that pkg-config includes the used path
+ execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable pc_path pkg-config RESULT_VARIABLE RETURN OUTPUT_VARIABLE PC_PATH ERROR_QUIET)
+ if(RETURN EQUAL 0)
+ string(STRIP "${PC_PATH}" PC_PATH)
+ set(PC_PATH "${PC_PATH}:$ENV{PKG_CONFIG_PATH}")
+ string(REGEX MATCH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig" SUBSTR "${PC_PATH}")
+ string(LENGTH "${SUBSTR}" SUBSTR_LEN)
+ if(SUBSTR_LEN EQUAL 0)
+ message(WARNING "pkg-config will not detect the new package after installation, adjust PKG_CONFIG_PATH using \"export PKG_CONFIG_PATH=\${PKG_CONFIG_PATH}:${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig\".")
+ endif()
+ endif()
+endif()
+
+# tests
+if(ENABLE_TESTS OR ENABLE_PERF_TESTS)
+ enable_testing()
+ add_subdirectory(tests)
+endif()
+
+if(ENABLE_FUZZ_TARGETS)
+ set(FUZZER "AFL" CACHE STRING "fuzzer type")
+ if(FUZZER STREQUAL "LibFuzzer")
+ if (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ message(FATAL_ERROR "LibFuzzer works only with clang")
+ endif()
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined -fno-omit-frame-pointer")
+ endif()
+endif()
+
+# create coverage target for generating coverage reports
+gen_coverage("utest_.*" "utest_.*_valgrind")
+
+# tools - yanglint, yangre
+add_subdirectory(tools)
+
+# generate doxygen documentation for libyang API
+gen_doc("${doxy_files}" ${LIBYANG_VERSION} ${LIBYANG_DESCRIPTION} ${project_logo})
+
+# generate API/ABI report
+if ("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
+ lib_abi_check(yang "${headers}" ${LIBYANG_SOVERSION_FULL} 003fa46e190930912e4d3f7b178c671c0662f671)
+endif()
+
+# source code format target for Makefile
+# - add it after tests which may also update list of sources to format
+source_format(${format_sources})
+
+# uninstall
+add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake")
+
+# clean cmake cache
+add_custom_target(cclean
+ COMMAND make clean
+ COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -not -path './CMakeModules*' -exec rm -rf {} +
+ COMMAND rm -rf Makefile Doxyfile
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/CMakeModules/ABICheck.cmake b/CMakeModules/ABICheck.cmake
new file mode 100644
index 0000000..d5f305b
--- /dev/null
+++ b/CMakeModules/ABICheck.cmake
@@ -0,0 +1,66 @@
+# generate API/ABI report
+macro(LIB_ABI_CHECK LIB_TARGET LIB_HEADERS LIB_SOVERSION_FULL ABI_BASE_HASH)
+ # get short hash
+ string(SUBSTRING "${ABI_BASE_HASH}" 0 8 ABI_BASE_HASH_SHORT)
+
+ # find abi-dumper
+ find_program(ABI_DUMPER abi-dumper)
+ find_package_handle_standard_args(abi-dumper DEFAULT_MSG ABI_DUMPER)
+ if(NOT ABI_DUMPER)
+ message(FATAL_ERROR "Program abi-dumper not found!")
+ endif()
+
+ # find abi-checker
+ find_program(ABI_CHECKER abi-compliance-checker)
+ find_package_handle_standard_args(abi-compliance-checker DEFAULT_MSG ABI_CHECKER)
+ if(NOT ABI_CHECKER)
+ message(FATAL_ERROR "Program abi-compliance-checker not found!")
+ endif()
+
+ # abi-dump target - generating an ABI dump
+ set(PUBLIC_HEADERS ${LIB_HEADERS})
+ string(PREPEND PUBLIC_HEADERS "${CMAKE_SOURCE_DIR}/")
+ string(REPLACE ";" "\n${CMAKE_SOURCE_DIR}/" PUBLIC_HEADERS "${PUBLIC_HEADERS}")
+ file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/public_headers CONTENT "${PUBLIC_HEADERS}")
+ add_custom_target(abi-dump
+ COMMAND ${ABI_DUMPER} ./lib${LIB_TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}
+ -o lib${LIB_TARGET}.${LIB_SOVERSION_FULL}.dump
+ -lver ${LIB_SOVERSION_FULL} -public-headers ${CMAKE_BINARY_DIR}/public_headers
+ DEPENDS ${LIB_TARGET}
+ BYPRODUCTS ${CMAKE_BINARY_DIR}/lib${LIB_TARGET}.${LIB_SOVERSION_FULL}.dump
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ COMMENT "Dumping ABI information of version ${LIB_SOVERSION_FULL} for abi-check")
+
+ # get URL for fetching origin
+ execute_process(COMMAND git remote get-url origin OUTPUT_VARIABLE ORIGIN_URL OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ # generate script for generating the base ABI dump
+ file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/abibase.sh CONTENT "#!/bin/sh
+if [ ! -d abibase ]; then mkdir abibase; fi
+cd abibase
+if [ ! -f build/lib${LIB_TARGET}.*.dump ]; then
+ if [ -d .git ] && [ \"${ABI_BASE_HASH}\" != \"`git log --pretty=oneline | cut -d' ' -f1`\" ]; then rm -rf .* 2> /dev/null; fi
+ if [ ! -d .git ]; then
+ git init --initial-branch=master
+ git remote add origin ${ORIGIN_URL}
+ git fetch origin --depth 1 ${ABI_BASE_HASH}
+ git reset --hard FETCH_HEAD
+ fi
+ if [ ! -d build ]; then mkdir build; fi
+ cd build
+ cmake -DCMAKE_BUILD_TYPE=ABICheck ..
+ make abi-dump
+fi
+")
+
+ # abi-check target - check ABI compatibility of current version and the base hash version
+ add_custom_target(abi-check
+ COMMAND bash ./abibase.sh
+ COMMAND ${ABI_CHECKER} -l lib${LIB_TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}
+ -old abibase/build/lib${LIB_TARGET}.*.dump
+ -new ./lib${LIB_TARGET}.${LIB_SOVERSION_FULL}.dump
+ DEPENDS ${LIB_TARGET} abi-dump
+ BYPRODUCTS ${CMAKE_BINARY_DIR}/compat_reports/lib${LIB_TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}/*_to_${LIB_SOVERSION_FULL}/compat_report.html
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ COMMENT "Checking ABI compatibility of version ${LIB_SOVERSION_FULL} and revision ${ABI_BASE_HASH_SHORT}")
+endmacro()
diff --git a/CMakeModules/FindCMocka.cmake b/CMakeModules/FindCMocka.cmake
new file mode 100644
index 0000000..2dd9fc5
--- /dev/null
+++ b/CMakeModules/FindCMocka.cmake
@@ -0,0 +1,49 @@
+# - Try to find CMocka
+# Once done this will define
+#
+# CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka
+#
+# Read-Only variables:
+# CMOCKA_FOUND - system has CMocka
+# CMOCKA_INCLUDE_DIR - the CMocka include directory
+# CMOCKA_LIBRARIES - Link these to use CMocka
+# CMOCKA_DEFINITIONS - Compiler switches required for using CMocka
+#
+#=============================================================================
+# Copyright (c) 2011-2012 Andreas Schneider <asn@cryptomilk.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+#
+
+find_path(CMOCKA_INCLUDE_DIR
+ NAMES
+ cmocka.h
+ PATHS
+ ${CMOCKA_ROOT_DIR}/include
+)
+
+find_library(CMOCKA_LIBRARY
+ NAMES
+ cmocka
+ PATHS
+ ${CMOCKA_ROOT_DIR}/include
+)
+
+if (CMOCKA_LIBRARY)
+ set(CMOCKA_LIBRARIES
+ ${CMOCKA_LIBRARIES}
+ ${CMOCKA_LIBRARY}
+ )
+endif (CMOCKA_LIBRARY)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR)
+
+# show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view
+mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES)
diff --git a/CMakeModules/FindPCRE2.cmake b/CMakeModules/FindPCRE2.cmake
new file mode 100644
index 0000000..19af7b7
--- /dev/null
+++ b/CMakeModules/FindPCRE2.cmake
@@ -0,0 +1,62 @@
+# - Find pcre
+# Find the native PCRE2 headers and libraries.
+#
+# PCRE2_INCLUDE_DIRS - where to find pcre.h, etc.
+# PCRE2_LIBRARIES - List of libraries when using pcre.
+# PCRE2_FOUND - True if pcre found.
+include(FindPackageHandleStandardArgs)
+
+if(PCRE2_LIBRARIES AND PCRE2_INCLUDE_DIRS)
+ # in cache already
+ set(PCRE2_FOUND TRUE)
+else()
+ find_path(PCRE2_INCLUDE_DIR
+ NAMES
+ pcre2.h
+ PATHS
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include)
+
+ # Look for the library.
+ find_library(PCRE2_LIBRARY
+ NAMES
+ libpcre2.a
+ pcre2-8
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ /sw/lib
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib)
+
+ if(PCRE2_INCLUDE_DIR AND PCRE2_LIBRARY)
+ # learn pcre2 version
+ file(STRINGS ${PCRE2_INCLUDE_DIR}/pcre2.h PCRE2_VERSION_MAJOR
+ REGEX "#define[ ]+PCRE2_MAJOR[ ]+[0-9]+")
+ string(REGEX MATCH " [0-9]+" PCRE2_VERSION_MAJOR ${PCRE2_VERSION_MAJOR})
+ string(STRIP "${PCRE2_VERSION_MAJOR}" PCRE2_VERSION_MAJOR)
+
+ file(STRINGS ${PCRE2_INCLUDE_DIR}/pcre2.h PCRE2_VERSION_MINOR
+ REGEX "#define[ ]+PCRE2_MINOR[ ]+[0-9]+")
+ string(REGEX MATCH " [0-9]+" PCRE2_VERSION_MINOR ${PCRE2_VERSION_MINOR})
+ string(STRIP "${PCRE2_VERSION_MINOR}" PCRE2_VERSION_MINOR)
+
+ set(PCRE2_VERSION ${PCRE2_VERSION_MAJOR}.${PCRE2_VERSION_MINOR})
+ endif()
+
+ set(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
+ set(PCRE2_LIBRARIES ${PCRE2_LIBRARY})
+ mark_as_advanced(PCRE2_INCLUDE_DIRS PCRE2_LIBRARIES)
+
+ # Handle the QUIETLY and REQUIRED arguments and set PCRE2_FOUND to TRUE if all listed variables are TRUE.
+ find_package_handle_standard_args(PCRE2 FOUND_VAR PCRE2_FOUND
+ REQUIRED_VARS PCRE2_LIBRARY PCRE2_INCLUDE_DIR
+ VERSION_VAR PCRE2_VERSION)
+endif()
diff --git a/CMakeModules/FindUncrustify.cmake b/CMakeModules/FindUncrustify.cmake
new file mode 100644
index 0000000..3b013e8
--- /dev/null
+++ b/CMakeModules/FindUncrustify.cmake
@@ -0,0 +1,21 @@
+# - Find uncrustify
+# Find the uncrustify binary.
+#
+# UNCRUSTIFY - path ot the binary
+# UNCRUSTIFY_VERSION - found version
+# UNCRUSTIFY_FOUND - True if uncrustify found.
+include(FindPackageHandleStandardArgs)
+
+find_program(UNCRUSTIFY uncrustify)
+if(UNCRUSTIFY)
+ execute_process(COMMAND ${UNCRUSTIFY} --version OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE VERSION)
+ string(FIND ${VERSION} "-" START_IDX)
+ math(EXPR START_IDX "${START_IDX} + 1")
+ string(SUBSTRING "${VERSION}" ${START_IDX} -1 VERSION)
+
+ string(FIND ${VERSION} "-" LEN)
+ string(SUBSTRING "${VERSION}" 0 ${LEN} UNCRUSTIFY_VERSION)
+endif()
+
+# Handle the QUIETLY and REQUIRED arguments and set UNCRUSTIFY_FOUND to TRUE if all listed variables are TRUE.
+find_package_handle_standard_args(Uncrustify REQUIRED_VARS UNCRUSTIFY VERSION_VAR UNCRUSTIFY_VERSION)
diff --git a/CMakeModules/GenCoverage.cmake b/CMakeModules/GenCoverage.cmake
new file mode 100644
index 0000000..fe26d9e
--- /dev/null
+++ b/CMakeModules/GenCoverage.cmake
@@ -0,0 +1,118 @@
+# generate test code coverage report
+
+# check that coverage tools are available - always use before GEN_COVERAGE
+macro(GEN_COVERAGE_ENABLE ENABLE_TESTS)
+ # make into normal variable
+ set(TESTS_ENABLED ${ENABLE_TESTS})
+
+ set(GEN_COVERAGE_ENABLED ON)
+ if(NOT TESTS_ENABLED)
+ message(WARNING "You cannot generate coverage when tests are disabled. Enable test by additing parameter -DENABLE_TESTS=ON or run cmake with Debug build target.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ find_program(PATH_GCOV NAMES gcov)
+ if(NOT PATH_GCOV)
+ message(WARNING "gcov executable not found! Disabling building code coverage report.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ find_program(PATH_LCOV NAMES lcov)
+ if(NOT PATH_LCOV)
+ message(WARNING "lcov executable not found! Disabling building code coverage report.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ find_program(PATH_GENHTML NAMES genhtml)
+ if(NOT PATH_GENHTML)
+ message(WARNING "genhtml executable not found! Disabling building code coverage report.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ if(NOT CMAKE_COMPILER_IS_GNUCC)
+ message(WARNING "Compiler is not gcc! Coverage may break the tests!")
+ endif()
+
+ execute_process(
+ COMMAND bash "-c" "${CMAKE_C_COMPILER} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\""
+ OUTPUT_VARIABLE GCC_VERSION_FULL
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ execute_process(
+ COMMAND bash "-c" "${PATH_GCOV} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\""
+ OUTPUT_VARIABLE GCOV_VERSION_FULL
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if(NOT GCC_VERSION_FULL STREQUAL GCOV_VERSION_FULL)
+ message(WARNING "gcc and gcov versions do not match! Generating coverage may fail with errors.")
+ endif()
+
+ # add specific required compile flags
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
+ endif()
+endmacro()
+
+# tests are always expected to be in ${CMAKE_SOURCE_DIR}/tests
+function(GEN_COVERAGE MATCH_TEST_REGEX EXCLUDE_TEST_REGEX)
+ if(NOT GEN_COVERAGE_ENABLED)
+ return()
+ endif()
+
+ # destination
+ set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/code_coverage/")
+ set(COVERAGE_FILE_RAW "${CMAKE_BINARY_DIR}/coverage_raw.info")
+ set(COVERAGE_FILE_CLEAN "${CMAKE_BINARY_DIR}/coverage_clean.info")
+
+ # test match/exclude
+ if(MATCH_TEST_REGEX)
+ set(MATCH_TEST_ARGS -R \"${MATCH_TEST_REGEX}\")
+ endif()
+ if(EXCLUDE_TEST_REGEX)
+ set(EXCLUDE_TEST_ARGS -E \"${EXCLUDE_TEST_REGEX}\")
+ endif()
+
+ # coverage target
+ add_custom_target(coverage
+ COMMENT "Generating code coverage..."
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ # Cleanup code counters
+ COMMAND "${PATH_LCOV}" --directory . --zerocounters --quiet
+
+ # Run tests
+ COMMAND "${CMAKE_CTEST_COMMAND}" --quiet ${MATCH_TEST_ARGS} ${EXCLUDE_TEST_ARGS}
+
+ # Capture the counters
+ COMMAND "${PATH_LCOV}"
+ --directory .
+ --rc lcov_branch_coverage=1
+ --rc 'lcov_excl_line=assert'
+ --capture --quiet
+ --output-file "${COVERAGE_FILE_RAW}"
+ # Remove coverage of tests, system headers, etc.
+ COMMAND "${PATH_LCOV}"
+ --remove "${COVERAGE_FILE_RAW}" '${CMAKE_SOURCE_DIR}/tests/*'
+ --rc lcov_branch_coverage=1
+ --quiet --output-file "${COVERAGE_FILE_CLEAN}"
+ # Generate HTML report
+ COMMAND "${PATH_GENHTML}"
+ --branch-coverage --function-coverage --quiet --title "${PROJECT_NAME}"
+ --legend --show-details --output-directory "${COVERAGE_DIR}"
+ "${COVERAGE_FILE_CLEAN}"
+ # Delete the counters
+ COMMAND "${CMAKE_COMMAND}" -E remove
+ ${COVERAGE_FILE_RAW} ${COVERAGE_FILE_CLEAN}
+ )
+
+ add_custom_command(TARGET coverage POST_BUILD
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
+ COMMENT "To see the code coverage report, open ${COVERAGE_DIR}index.html"
+ COMMAND ;
+ )
+endfunction()
diff --git a/CMakeModules/GenDoc.cmake b/CMakeModules/GenDoc.cmake
new file mode 100644
index 0000000..ee879d0
--- /dev/null
+++ b/CMakeModules/GenDoc.cmake
@@ -0,0 +1,28 @@
+# Prepare building doxygen documentation
+macro(GEN_DOC INPUT_FILES PROJECT_VERSION PROJECT_DESCRIPTION DOC_LOGO)
+ find_package(Doxygen)
+ if(DOXYGEN_FOUND)
+ find_program(DOT_PATH dot PATH_SUFFIXES graphviz2.38/bin graphviz/bin)
+ if(DOT_PATH)
+ set(HAVE_DOT "YES")
+ else()
+ set(HAVE_DOT "NO")
+ message(AUTHOR_WARNING "Doxygen: to generate UML diagrams please install graphviz")
+ endif()
+
+ # target doc
+ add_custom_target(doc
+ COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+ # generate list with spaces as separators
+ string(REPLACE ";" " " DOXY_INPUT "${INPUT_FILES}")
+
+ # make other arguments into variables
+ set(PROJECT_VERSION ${PROJECT_VERSION})
+ set(PROJECT_DESCRIPTION ${PROJECT_DESCRIPTION})
+ set(DOC_LOGO ${DOC_LOGO})
+
+ configure_file(Doxyfile.in Doxyfile)
+ endif()
+endmacro()
diff --git a/CMakeModules/SourceFormat.cmake b/CMakeModules/SourceFormat.cmake
new file mode 100644
index 0000000..fd1e63b
--- /dev/null
+++ b/CMakeModules/SourceFormat.cmake
@@ -0,0 +1,36 @@
+# format source files with uncrustify
+
+# check that format checking is available - always use before SOURCE_FORMAT
+macro(SOURCE_FORMAT_ENABLE)
+ if(NOT ${ARGC} EQUAL 1)
+ message(FATAL_ERROR "source_format_enable() needs the required Uncrustify version!")
+ endif()
+
+ find_package(Uncrustify ${ARGV0})
+ if(UNCRUSTIFY_FOUND)
+ set(SOURCE_FORMAT_ENABLED TRUE)
+ else()
+ set(SOURCE_FORMAT_ENABLED FALSE)
+ endif()
+endmacro()
+
+# files are expected to be a list and relative paths are resolved wtih respect to CMAKE_SOURCE DIR
+macro(SOURCE_FORMAT)
+ if(NOT ${ARGC})
+ message(FATAL_ERROR "source_format() needs a list of files to format!")
+ endif()
+
+ if(SOURCE_FORMAT_ENABLED)
+ add_custom_target(format
+ COMMAND ${UNCRUSTIFY} -c ${CMAKE_SOURCE_DIR}/uncrustify.cfg --no-backup --replace ${ARGN}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ COMMENT "Formating sources with ${UNCRUSTIFY} ...")
+
+ add_custom_target(format-check
+ COMMAND ${UNCRUSTIFY} -c ${CMAKE_SOURCE_DIR}/uncrustify.cfg --check ${ARGN}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ COMMENT "Checking format of the sources with ${UNCRUSTIFY} ...")
+
+ set(SOURCE_FORMAT_ENABLED TRUE)
+ endif()
+endmacro()
diff --git a/CMakeModules/UseCompat.cmake b/CMakeModules/UseCompat.cmake
new file mode 100644
index 0000000..c1befd7
--- /dev/null
+++ b/CMakeModules/UseCompat.cmake
@@ -0,0 +1,75 @@
+# - Use compat library providing various functions and macros that may be missing on some systems
+# Once done this will define
+#
+# compatsrc - sources to add to compilation
+#
+# Additionally, "compat.h" include directory is added and can be included.
+#
+# Author Michal Vasko <mvasko@cesnet.cz>
+# Copyright (c) 2021 CESNET, z.s.p.o.
+#
+# This source code is licensed under BSD 3-Clause License (the "License").
+# You may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://opensource.org/licenses/BSD-3-Clause
+#
+include(CheckSymbolExists)
+include(CheckFunctionExists)
+include(CheckIncludeFile)
+include(TestBigEndian)
+if(POLICY CMP0075)
+ cmake_policy(SET CMP0075 NEW)
+endif()
+
+macro(USE_COMPAT)
+ # compatibility checks
+ set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L)
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__BSD_VISIBLE=1)
+ set(CMAKE_REQUIRED_LIBRARIES pthread)
+
+ check_symbol_exists(vdprintf "stdio.h;stdarg.h" HAVE_VDPRINTF)
+ check_symbol_exists(asprintf "stdio.h" HAVE_ASPRINTF)
+ check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF)
+ check_symbol_exists(getline "stdio.h" HAVE_GETLINE)
+
+ check_symbol_exists(strndup "string.h" HAVE_STRNDUP)
+ check_symbol_exists(strnstr "string.h" HAVE_STRNSTR)
+ check_symbol_exists(strdupa "string.h" HAVE_STRDUPA)
+ check_symbol_exists(strchrnul "string.h" HAVE_STRCHRNUL)
+
+ check_symbol_exists(get_current_dir_name "unistd.h" HAVE_GET_CURRENT_DIR_NAME)
+
+ check_function_exists(pthread_mutex_timedlock HAVE_PTHREAD_MUTEX_TIMEDLOCK)
+
+ TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
+
+ check_include_file("stdatomic.h" HAVE_STDATOMIC)
+
+ include(CheckStructHasMember)
+ check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
+ check_symbol_exists(timezone time.h HAVE_TIME_H_TIMEZONE)
+
+ check_symbol_exists(realpath "stdlib.h" HAVE_REALPATH)
+ check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R)
+ check_symbol_exists(gmtime_r "time.h" HAVE_GMTIME_R)
+ check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
+ check_symbol_exists(mmap "sys/mman.h" HAVE_MMAP)
+ check_symbol_exists(dirname "libgen.h" HAVE_DIRNAME)
+ check_symbol_exists(setenv "stdlib.h" HAVE_SETENV)
+
+ unset(CMAKE_REQUIRED_DEFINITIONS)
+ unset(CMAKE_REQUIRED_LIBRARIES)
+
+ # header and source file (adding the source directly allows for hiding its symbols)
+ configure_file(${PROJECT_SOURCE_DIR}/compat/compat.h.in ${PROJECT_BINARY_DIR}/compat/compat.h @ONLY)
+ include_directories(${PROJECT_BINARY_DIR}/compat)
+ set(compatsrc ${PROJECT_SOURCE_DIR}/compat/compat.c)
+ if(WIN32)
+ include_directories(${PROJECT_SOURCE_DIR}/compat/posix-shims)
+ endif()
+ if(NOT HAVE_STRPTIME)
+ set(compatsrc ${compatsrc} ${PROJECT_SOURCE_DIR}/compat/strptime.c)
+ endif()
+endmacro()
diff --git a/CMakeModules/uninstall.cmake b/CMakeModules/uninstall.cmake
new file mode 100644
index 0000000..b9618a2
--- /dev/null
+++ b/CMakeModules/uninstall.cmake
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.0.2)
+
+set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
+
+if(NOT EXISTS ${MANIFEST})
+ message(FATAL_ERROR "Cannot find install manifest: ${MANIFEST}")
+endif()
+
+file(STRINGS ${MANIFEST} files)
+foreach(file ${files})
+ if(EXISTS ${file} OR IS_SYMLINK ${file})
+ message(STATUS "Removing: ${file}")
+
+ execute_process(COMMAND rm -f ${file}
+ RESULT_VARIABLE result
+ OUTPUT_QUIET
+ ERROR_VARIABLE stderr
+ ERROR_STRIP_TRAILING_WHITESPACE
+ )
+
+ if(NOT ${result} EQUAL 0)
+ message(FATAL_ERROR "${stderr}")
+ endif()
+ else()
+ message(STATUS "Does-not-exist: ${file}")
+ endif()
+endforeach(file)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..b150904
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,228 @@
+# Contributing to libyang
+
+First of all, thanks for thinking about contribution to libyang! Not all of us
+are C guru, but believe that helping us with docs, tests, helping other users to
+solve their issues / answer ther questions or even letting us to know via
+[issue tracker](https://github.com/CESNET/libyang/issues) that something
+can be done in a better or just different way is really appreciated.
+
+If you are willing to contribute, you will definitely have to build and install
+libyang first. To do it, please check dependencies and follow build and install
+instructions provided in [README](README.md).
+
+If you have something what you believe should be part of the libyang repository,
+add it via [Github Pull Request mechanism](https://help.github.com/articles/about-pull-requests/).
+Remember to explain what you wish to add and why. The best approach is to start
+with creating an issue and discuss possible approaches there. Pull requests can be
+then connected with such issues.
+
+## Branches
+
+There are 2 main branches in libyang project. The default branch is named `master`. It is the
+most stable and tested code which you get by default when cloning the Git repository. The
+`devel` branch introduces new features, API changes or even bugfixes in case `master` and
+`devel` differs significantly at that moment and the fix affects the changed code. There are
+some more branches for work-in-progress features and special `coverity` branch for submitting
+code for the [analysis in the Coverity tool](https://scan.coverity.com/projects/5259) usign
+[Travis CI build](https://travis-ci.org/CESNET/libyang/branches).
+
+When you create pull request, think carefully about the branch where the patch belongs to.
+In most cases (if not all), it is the `devel` branch.
+
+## Issue Ticketing
+
+All the communication with the developers is done via [issue tracker](https://github.com/CESNET/libyang/issues).
+You can send us an email, but in case you will ask a question we would think that someone else
+could ask in future, our answer will be just **use the issue tracker**. Private emails are not visible
+for others and we don't want to answer the same questions.
+
+So when you are goingto submit a new issue, **please:**
+* check that the issue you are having is not already solved in the devel branch,
+* go through the present issues (in case of question, it can be already a closed issue) in the tracker,
+* give it as descriptive title as possible,
+* separate topics - solving multiple issues in one ticket hides the issues from others,
+* provide as much relevant information as possible (versions, logs, input data, etc.).
+
+## libyang Coding Style
+
+When you are going to contribute C code, please follow these coding style guidelines.
+
+### Basics
+
+- Use space instead of tabs for indentations.
+- There is no strict limit for the line length, However, try to keep lines in a
+ reasonable length (120 characters).
+- Avoid trailing spaces on lines.
+- Put one blank line between function definitions.
+- Don't mix declarations and code within a block. Similarly, don't use
+ declarations in iteration statements.
+
+### Naming
+
+Use underscores to separate words in an identifier: `multi_word_name`.
+
+Use lowercase for most names. Use uppercase for macros, macro parameters and
+members of enumerations.
+
+Do not use names that begin with `_`. If you need a name for "internal use
+only", use `__` as a suffix instead of a prefix.
+
+### Comments
+
+Avoid `//` comments. Use `/* ... */` comments, write block comments with the
+leading asterisk on each line. You may put the `/*` and `*/` on the same line as
+comment text if you prefer.
+
+```c
+/*
+ * comment text
+ */
+```
+
+### Functions
+
+Put the return type, function name, and the braces that surround the function's
+code on separate lines, all starting in column 0.
+
+```c
+static int
+foo(int arg)
+{
+ ...
+}
+```
+
+When you need to put the function parameters on multiple lines, start new line
+at column after the opening parenthesis from the initial line.
+
+```c
+static int
+my_function(struct my_struct *p1, struct another_struct *p2,
+ int size)
+{
+ ...
+}
+```
+
+In the absence of good reasons for another order, the following parameter order
+is preferred. One notable exception is that data parameters and their
+corresponding size parameters should be paired.
+
+1. The primary object being manipulated, if any (equivalent to the "this"
+ pointer in C++).
+2. Input-only parameters.
+3. Input/output parameters.
+4. Output-only parameters.
+5. Status parameter.
+
+Functions that destroy an instance of a dynamically-allocated type should accept
+and ignore a null pointer argument. Code that calls such a function (including
+the C standard library function `free()`) should omit a null-pointer check. We
+find that this usually makes code easier to read.
+
+#### Function Prototypes
+
+Put the return type and function name on the same line in a function prototype:
+
+```c
+static const struct int foo(int arg);
+```
+
+### Statements
+
+- Indent each level of code with 4 spaces.
+- Put single space between `if`, `while`, `for`, etc. statements and the
+ expression that follow them. On the other hand, function calls has no space
+ between the function name and opening parenthesis.
+- Opening code block brace is kept at the same line with the `if`, `while`,
+ `for` or `switch` statements.
+
+```c
+if (a) {
+ x = exp(a);
+} else {
+ return 1;
+}
+```
+
+- Start switch's cases at the same column as the switch.
+
+```c
+switch (conn->state) {
+case 0:
+ return "data found";
+case 1:
+ return "data not found";
+default:
+ return "unknown error";
+}
+```
+
+- Do not put gratuitous parentheses around the expression in a return statement,
+that is, write `return 0;` and not `return(0);`
+
+### Types
+
+Use typedefs sparingly. Code is clearer if the actual type is visible at the
+point of declaration. Do not, in general, declare a typedef for a struct, union,
+or enum. Do not declare a typedef for a pointer type, because this can be very
+confusing to the reader.
+
+Use the `int<N>_t` and `uint<N>_t` types from `<stdint.h>` for exact-width
+integer types. Use the `PRId<N>`, `PRIu<N>`, and `PRIx<N>` macros from
+`<inttypes.h>` for formatting them with `printf()` and related functions.
+
+Pointer declarators bind to the variable name, not the type name. Write
+`int *x`, not `int* x` and definitely not `int * x`.
+
+### Expresions
+
+Put one space on each side of infix binary and ternary operators:
+
+```c
+* / % + - << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>=
+```
+
+Do not put any white space around postfix, prefix, or grouping operators with
+one exception - `sizeof`, see the note below.
+
+```c
+() [] -> . ! ~ ++ -- + - * &
+```
+
+The "sizeof" operator is unique among C operators in that it accepts two very
+different kinds of operands: an expression or a type. In general, prefer to
+specify an expression
+```c
+int *x = calloc(1, sizeof *x);
+```
+When the operand of sizeof is an expression, there is no need to parenthesize
+that operand, and please don't. There is an exception to this rule when you need
+to work with partially compatible structures:
+
+```c
+struct a_s {
+ uint8_t type;
+}
+
+struct b_s {
+ uint8_t type;
+ char *str;
+}
+
+struct c_s {
+ uint8_t type;
+ uint8_t *u8;
+}
+...
+struct a_s *a;
+
+switch (type) {
+case 1:
+ a = (struct a_s *)calloc(1, sizeof(struct b_s));
+ break;
+case 2:
+ a = (struct a_s *)calloc(1, sizeof(struct c_s));
+ break;
+ ...
+```
diff --git a/Doxyfile.in b/Doxyfile.in
new file mode 100644
index 0000000..6ac2071
--- /dev/null
+++ b/Doxyfile.in
@@ -0,0 +1,2457 @@
+# Doxyfile 1.8.11
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = @PROJECT_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @PROJECT_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "@PROJECT_DESCRIPTION@"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO = @DOC_LOGO@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ./doc
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES = asterisk=*
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = YES
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = @INTERNAL_DOCS@
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = @DOXY_INPUT@
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,
+# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.f90 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf \
+ *.as \
+ *.js
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = ./doc/cesnet-style.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = @HAVE_DOT@
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = YES
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 50
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = YES
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = svg
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = YES
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 100
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/FindLibYANG.cmake b/FindLibYANG.cmake
new file mode 100644
index 0000000..89cd93a
--- /dev/null
+++ b/FindLibYANG.cmake
@@ -0,0 +1,89 @@
+# - Try to find LibYANG
+# Once done this will define
+#
+# LIBYANG_FOUND - system has LibYANG
+# LIBYANG_INCLUDE_DIRS - the LibYANG include directory
+# LIBYANG_LIBRARIES - Link these to use LibYANG
+# LIBYANG_VERSION - SO version of the found libyang library
+#
+# Author Michal Vasko <mvasko@cesnet.cz>
+# Copyright (c) 2021 CESNET, z.s.p.o.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+include(FindPackageHandleStandardArgs)
+
+if(LIBYANG_LIBRARIES AND LIBYANG_INCLUDE_DIRS)
+ # in cache already
+ set(LIBYANG_FOUND TRUE)
+else()
+ find_path(LIBYANG_INCLUDE_DIR
+ NAMES
+ libyang/libyang.h
+ PATHS
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(LIBYANG_LIBRARY
+ NAMES
+ yang
+ libyang
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ /sw/lib
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if(LIBYANG_INCLUDE_DIR)
+ find_path(LY_VERSION_PATH "libyang/version.h" HINTS ${LIBYANG_INCLUDE_DIR})
+ if(LY_VERSION_PATH)
+ file(READ "${LY_VERSION_PATH}/libyang/version.h" LY_VERSION_FILE)
+ else()
+ find_path(LY_HEADER_PATH "libyang/libyang.h" HINTS ${LIBYANG_INCLUDE_DIR})
+ file(READ "${LY_HEADER_PATH}/libyang/libyang.h" LY_VERSION_FILE)
+ endif()
+ string(REGEX MATCH "#define LY_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" LY_VERSION_MACRO "${LY_VERSION_FILE}")
+ string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LIBYANG_VERSION "${LY_VERSION_MACRO}")
+ endif()
+
+ set(LIBYANG_INCLUDE_DIRS ${LIBYANG_INCLUDE_DIR})
+ set(LIBYANG_LIBRARIES ${LIBYANG_LIBRARY})
+ mark_as_advanced(LIBYANG_INCLUDE_DIRS LIBYANG_LIBRARIES)
+
+ # handle the QUIETLY and REQUIRED arguments and set LIBYANG_FOUND to TRUE
+ # if all listed variables are TRUE
+ find_package_handle_standard_args(LibYANG FOUND_VAR LIBYANG_FOUND
+ REQUIRED_VARS LIBYANG_LIBRARY LIBYANG_INCLUDE_DIR
+ VERSION_VAR LIBYANG_VERSION)
+endif()
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..72e0e09
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2015-2021, CESNET
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of CESNET nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..05e55d2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,277 @@
+# libyang
+
+[![BSD license](https://img.shields.io/badge/License-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
+[![Build](https://github.com/CESNET/libyang/workflows/libyang%20CI/badge.svg)](https://github.com/CESNET/libyang/actions?query=workflow%3A%22libyang+CI%22)
+[![Docs](https://img.shields.io/badge/docs-link-blue)](https://netopeer.liberouter.org/doc/libyang/)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/5259/badge.svg)](https://scan.coverity.com/projects/5259)
+[![codecov.io](https://codecov.io/github/CESNET/libyang/coverage.svg?branch=master)](https://codecov.io/github/CESNET/libyang?branch=master)
+[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/libyang.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:libyang)
+[![Ohloh Project Status](https://www.openhub.net/p/libyang/widgets/project_thin_badge.gif)](https://www.openhub.net/p/libyang)
+
+libyang is a YANG data modelling language parser and toolkit written (and
+providing API) in C. The library is used e.g. in [libnetconf2](https://github.com/CESNET/libnetconf2),
+[Netopeer2](https://github.com/CESNET/Netopeer2) or [sysrepo](https://github.com/sysrepo/sysrepo) projects.
+
+If you are interested in future plans announcements, please subscribe to the
+[Future Plans issue](https://github.com/CESNET/libyang/issues/880).
+
+## Branches
+
+The project uses 2 main branches `master` and `devel`. Other branches should not be cloned. In `master` there are files of the
+last official *release*. Any latest improvements and changes, which were tested at least briefly are found in `devel`. On every
+new *release*, `devel` is merged into `master`.
+
+This means that when only stable official releases are to be used, either `master` can be used or specific *releases* downloaded.
+If all the latest bugfixes should be applied, `devel` branch is the one to be used. Note that whenever **a new issue is created**
+and it occurs on the `master` branch, the **first response will likely be** to use `devel` before any further provided support.
+
+## Migration from libyang version 1 or older
+
+Look into the documentation and the section `Transition Manual`. That should help with basic migration and the
+ability to compile a project. But to actually make use of the new features, it is required to read through
+the whole documentation and the API.
+
+## Provided Features
+
+* Parsing (and validating) schemas in YANG format.
+* Parsing (and validating) schemas in YIN format.
+* Parsing, validating and printing instance data in XML format.
+* Parsing, validating and printing instance data in JSON format
+ ([RFC 7951](https://tools.ietf.org/html/rfc7951)).
+* Manipulation with the instance data.
+* Support for default values in the instance data ([RFC 6243](https://tools.ietf.org/html/rfc6243)).
+* Support for YANG extensions.
+* Support for YANG Metadata ([RFC 7952](https://tools.ietf.org/html/rfc7952)).
+* Support for YANG Schema Mount ([RFC 8528](https://tools.ietf.org/html/rfc8528)).
+* Support for YANG Structure ([RFC 8791](https://tools.ietf.org/html/rfc8791)).
+* [yanglint](#yanglint) - feature-rich YANG tool.
+
+Current implementation covers YANG 1.0 ([RFC 6020](https://tools.ietf.org/html/rfc6020))
+as well as YANG 1.1 ([RFC 7950](https://tools.ietf.org/html/rfc7950)).
+
+## Packages
+
+Binary RPM or DEB packages of the latest release can be built locally using `apkg`, look into `README` in
+the `distro` directory.
+
+## Requirements
+
+### Unix Build Requirements
+
+* C compiler
+* cmake >= 2.8.12
+* libpcre2 >= 10.21 (including devel package)
+ * note, that PCRE is supposed to be compiled with unicode support (configure's options
+ `--enable-utf` and `--enable-unicode-properties`)
+
+#### Optional
+
+* doxygen (for generating documentation)
+* cmocka >= 1.0.1 (for [tests](#Tests))
+* valgrind (for enhanced testing)
+* gcov (for code coverage)
+* lcov (for code coverage)
+* genhtml (for code coverage)
+
+### Unix Runtime Requirements
+
+* libpcre2 >= 10.21
+
+### Windows Build Requirements
+
+* Visual Studio 17 (2022)
+* cmake >= 3.22.0
+* libpcre2 (same considerations as on POSIX)
+* [`pthreads-win32`](https://sourceware.org/pthreads-win32/)
+* [`dirent`](https://github.com/tronkko/dirent)
+* [`dlfcn-win32`](https://github.com/dlfcn-win32/dlfcn-win32)
+* [`getopt-win32`](https://github.com/libimobiledevice-win32/getopt)
+
+The Windows version [does not support plugins](https://github.com/CESNET/libyang/commit/323c31221645052e13db83f7d0e6e51c3ce9d802), and the `yanglint` works in a [non-interactive mode](https://github.com/CESNET/libyang/commit/2e3f935ed6f4a47e65b31de5aeebcd8877d5a09b) only.
+On Windows, all YANG date-and-time values are first converted to UTC (if TZ offset was specified), and then returned with "unspecified timezone".
+
+## Building
+
+```
+$ mkdir build; cd build
+$ cmake ..
+$ make
+# make install
+```
+
+### Useful CMake Options
+
+#### Changing Compiler
+
+Set `CC` variable:
+
+```
+$ CC=/usr/bin/clang cmake ..
+```
+
+#### Changing Install Path
+
+To change the prefix where the library, headers and any other files are installed,
+set `CMAKE_INSTALL_PREFIX` variable:
+```
+$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr ..
+```
+
+Default prefix is `/usr/local`.
+
+#### Build Modes
+
+There are two build modes:
+* Release.
+ This generates library for the production use without any debug information.
+* Debug.
+ This generates library with the debug information and disables optimization
+ of the code.
+
+The `Debug` mode is currently used as the default one. to switch to the
+`Release` mode, enter at the command line:
+```
+$ cmake -D CMAKE_BUILD_TYPE:String="Release" ..
+```
+
+#### Changing Extensions Plugins Directory
+
+As for YANG extensions, libyang allows loading extension plugins. By default, the
+directory to store the plugins is LIBDIR/libyang. To change it, use the following
+cmake option with the value specifying the desired directory:
+
+```
+$ cmake -DPLUGINS_DIR:PATH=`pwd`"/src/extensions/" ..
+```
+
+The directory path can be also changed runtime via environment variable, e.g.:
+
+```
+$ LIBYANG_EXTENSIONS_PLUGINS_DIR=`pwd`/my/relative/path yanglint
+```
+
+Note that plugins are [not available on Windows](https://github.com/CESNET/libyang/commit/323c31221645052e13db83f7d0e6e51c3ce9d802).
+
+#### Optimizations
+
+Whenever the latest revision of a schema is supposed to be loaded (import without specific revision),
+it is performed in the standard way, the first time. By default, every other time when the latest
+revision of the same schema is needed, the one initially loaded is reused. If you know this can cause
+problems meaning the latest available revision of a schema can change during operation, you can force
+libyang to always search for the schema anew by:
+
+```
+$ cmake -DENABLE_LATEST_REVISIONS=OFF ..
+```
+
+### CMake Notes
+
+Note that, with CMake, if you want to change the compiler or its options after
+you already ran CMake, you need to clear its cache first - the most simple way
+to do it is to remove all content from the 'build' directory.
+
+## Usage
+
+All libyang functions are available via the main header:
+```
+#include <libyang/libyang.h>
+```
+
+To compile your program with libyang, it is necessary to link it with libyang using the
+following linker parameters:
+```
+-lyang
+```
+
+Note, that it may be necessary to call `ldconfig(8)` after library installation and if the
+library was installed into a non-standard path, the path to libyang must be specified to the
+linker. To help with setting all the compiler's options, there is `libyang.pc` file for
+`pkg-config(1)` available in the source tree. The file is installed with the library.
+
+If you are using `cmake` in you project, it is also possible to use the provided
+`FindLibYANG.cmake` file to detect presence of the libyang library in the system.
+
+## Bindings
+
+There are no bindings for other languages directly in this project but they are
+available separately.
+
+* [Python](https://github.com/CESNET/libyang-python/)
+* [C++](https://github.com/CESNET/libyang-cpp/)
+* [Rust](https://github.com/rwestphal/yang2-rs/)
+
+## yanglint
+
+libyang project includes a feature-rich tool called `yanglint(1)` for validation
+and conversion of the schemas and YANG modeled data. The source codes are
+located at [`/tools/lint`](./tools/lint) and can be used to explore how an
+application is supposed to use the libyang library. `yanglint(1)` binary as
+well as its man page are installed together with the library itself.
+
+There is also [README](./tools/lint/examples/README.md) describing some examples of
+using `yanglint`.
+
+## Tests
+
+libyang includes several tests built with [cmocka](https://cmocka.org/). The tests
+can be found in `tests` subdirectory and they are designed for checking library
+functionality after code changes. Additional regression tests done with
+a corpus of fuzzing inputs that previously caused crashes are done.
+Those are available in `tests/fuzz` and are built automatically with the
+cmocka unit tests.
+
+
+The tests are by default built in the `Debug` build mode by running
+```
+$ make
+```
+
+In case of the `Release` mode, the tests are not built by default (it requires
+additional dependency), but they can be enabled via cmake option:
+```
+$ cmake -DENABLE_TESTS=ON ..
+```
+
+Note that if the necessary [cmocka](https://cmocka.org/) headers are not present
+in the system include paths, tests are not available despite the build mode or
+cmake's options.
+
+Tests can be run by the make's `test` target:
+```
+$ make test
+```
+
+### Perf
+
+There is a performance measurement tool included that prints information about
+the time required to execute common use-cases of working with YANG instance data.
+
+To enable this test, use an option and to get representative results, enable Release build type:
+```
+$ cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_PERF_TESTS=ON ..
+```
+and to run the test with seeing its output run:
+```
+$ make
+$ ctest -V -R ly_perf
+```
+
+### Code Coverage
+
+Based on the tests run, it is possible to generate code coverage report. But
+it must be enabled and these commands are needed to generate the report:
+```
+$ cmake -DENABLE_COVERAGE=ON ..
+$ make
+$ make coverage
+```
+
+## Fuzzing
+
+Multiple YANG fuzzing targets and fuzzing instructions are available in the
+`tests/fuzz` directory.
+
+All of the targets can be fuzzed with LLVM's LibFuzzer and AFL, and new targets
+can easily be added.
+Asciinema examples which describe the fuzzing setup for both AFL (https://asciinema.org/a/311060)
+and LibFuzzer (https://asciinema.org/a/311035) are available.
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..3ce0684
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,31 @@
+comment:
+ layout: header, changes, diff
+
+coverage:
+ precision: 2
+ round: nearest
+
+ ignore:
+ - compat/.*
+ - tests/.*
+
+ status:
+ project:
+ default:
+ target: auto
+ if_no_uploads: error
+
+ patch:
+ default:
+ if_no_uploads: error
+
+ changes: true
+
+ parsers:
+ gcov:
+ branch_detection:
+ macro: no
+ loop: no
+ conditional: no
+ method: no
+
diff --git a/compat/check_includes.sh b/compat/check_includes.sh
new file mode 100755
index 0000000..0090007
--- /dev/null
+++ b/compat/check_includes.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+RETVAL=0
+
+# params - paths to the source files to search
+SRC="$*"
+
+# param FUNC - name of the function in compat to check
+check_compat_func () {
+ FILES=`grep -rE "([^[:alnum:]]|^)$1\([^\)]+\)" --include=\*.{c,h} $SRC | cut -d: -f1 | uniq`
+ for f in $FILES; do
+ grep -q "#include \"compat.h\"" $f
+ if [ $? -ne 0 ]; then
+ echo "Missing #include \"compat.h\" in file $f for function $1()"
+ RETVAL=$((RETVAL+1))
+ fi
+ done
+}
+
+check_compat_macro () {
+ FILES=`grep -rE "([^[:alnum:]]|^)$1([^[:alnum:]]|$)" --include=\*.{c,h} $SRC | cut -d: -f1 | uniq`
+ for f in $FILES; do
+ grep -q "#include \"compat.h\"" $f
+ if [ $? -ne 0 ]; then
+ echo "Missing #include \"compat.h\" in file $f for macro $1"
+ RETVAL=$((RETVAL+1))
+ fi
+ done
+}
+
+check_compat_func vdprintf
+check_compat_func asprintf
+check_compat_func vasprintf
+check_compat_func getline
+check_compat_func strndup
+check_compat_func strnstr
+check_compat_func strdupa
+check_compat_func strchrnul
+check_compat_func get_current_dir_name
+check_compat_func pthread_mutex_timedlock
+check_compat_func UNUSED
+check_compat_macro _PACKED
+
+exit $RETVAL
diff --git a/compat/compat.c b/compat/compat.c
new file mode 100644
index 0000000..5fb2be8
--- /dev/null
+++ b/compat/compat.c
@@ -0,0 +1,361 @@
+/**
+ * @file compat.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief compatibility functions
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _POSIX_C_SOURCE 200809L /* fdopen, _POSIX_PATH_MAX, strdup */
+#define _ISOC99_SOURCE /* vsnprintf */
+
+#include "compat.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _MSC_VER
+#include <sys/time.h>
+#endif
+#include <time.h>
+#include <unistd.h>
+
+#ifndef HAVE_VDPRINTF
+int
+vdprintf(int fd, const char *format, va_list ap)
+{
+ FILE *stream;
+ int count = 0;
+
+ stream = fdopen(dup(fd), "a+");
+ if (stream) {
+ count = vfprintf(stream, format, ap);
+ fclose(stream);
+ }
+ return count;
+}
+
+#endif
+
+#ifndef HAVE_ASPRINTF
+int
+asprintf(char **strp, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vasprintf(strp, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+#endif
+
+#ifndef HAVE_VASPRINTF
+int
+vasprintf(char **strp, const char *fmt, va_list ap)
+{
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ int l = vsnprintf(0, 0, fmt, ap2);
+
+ va_end(ap2);
+
+ if ((l < 0) || !(*strp = malloc(l + 1U))) {
+ return -1;
+ }
+
+ return vsnprintf(*strp, l + 1U, fmt, ap);
+}
+
+#endif
+
+#ifndef HAVE_GETLINE
+ssize_t
+getline(char **lineptr, size_t *n, FILE *stream)
+{
+ static char line[256];
+ char *ptr;
+ ssize_t len;
+
+ if (!lineptr || !n) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ferror(stream) || feof(stream)) {
+ return -1;
+ }
+
+ if (!fgets(line, 256, stream)) {
+ return -1;
+ }
+
+ ptr = strchr(line, '\n');
+ if (ptr) {
+ *ptr = '\0';
+ }
+
+ len = strlen(line);
+
+ if (len + 1 < 256) {
+ ptr = realloc(*lineptr, 256);
+ if (!ptr) {
+ return -1;
+ }
+ *lineptr = ptr;
+ *n = 256;
+ }
+
+ strcpy(*lineptr, line);
+ return len;
+}
+
+#endif
+
+#ifndef HAVE_STRNDUP
+char *
+strndup(const char *s, size_t n)
+{
+ char *buf;
+ size_t len = 0;
+
+ /* strnlen */
+ for ( ; (len < n) && (s[len] != '\0'); ++len) {}
+
+ if (!(buf = malloc(len + 1U))) {
+ return NULL;
+ }
+
+ memcpy(buf, s, len);
+ buf[len] = '\0';
+ return buf;
+}
+
+#endif
+
+#ifndef HAVE_STRNSTR
+char *
+strnstr(const char *s, const char *find, size_t slen)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != '\0') {
+ len = strlen(find);
+ do {
+ do {
+ if ((slen-- < 1) || ((sc = *s++) == '\0')) {
+ return NULL;
+ }
+ } while (sc != c);
+ if (len > slen) {
+ return NULL;
+ }
+ } while (strncmp(s, find, len));
+ s--;
+ }
+ return (char *)s;
+}
+
+#endif
+
+#ifndef HAVE_STRCHRNUL
+char *
+strchrnul(const char *s, int c)
+{
+ char *p = strchr(s, c);
+
+ return p ? p : (char *)s + strlen(s);
+}
+
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *
+get_current_dir_name(void)
+{
+ char tmp[_POSIX_PATH_MAX];
+ char *retval = NULL;
+
+ if (getcwd(tmp, sizeof(tmp))) {
+ retval = strdup(tmp);
+ if (!retval) {
+ errno = ENOMEM;
+ }
+ }
+
+ return retval;
+}
+
+#endif
+
+#ifndef _MSC_VER
+#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
+int
+pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+ int64_t nsec_diff;
+ int32_t diff;
+ struct timespec cur, dur;
+ int rc;
+
+ /* try to acquire the lock and, if we fail, sleep for 5ms. */
+ while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
+ /* get real time */
+#ifdef CLOCK_REALTIME
+ clock_gettime(CLOCK_REALTIME, &cur);
+#else
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ cur.tv_sec = (time_t)tv.tv_sec;
+ cur.tv_nsec = 1000L * (long)tv.tv_usec;
+#endif
+
+ /* get time diff */
+ nsec_diff = 0;
+ nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
+ nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec);
+ diff = (nsec_diff ? nsec_diff / 1000000L : 0);
+
+ if (diff < 1) {
+ /* timeout */
+ break;
+ } else if (diff < 5) {
+ /* sleep until timeout */
+ dur.tv_sec = 0;
+ dur.tv_nsec = (long)diff * 1000000;
+ } else {
+ /* sleep 5 ms */
+ dur.tv_sec = 0;
+ dur.tv_nsec = 5000000;
+ }
+
+ nanosleep(&dur, NULL);
+ }
+
+ return rc;
+}
+
+#endif
+#endif
+
+#ifndef HAVE_REALPATH
+#ifdef _WIN32
+char *
+realpath(const char *path, char *resolved_path)
+{
+ char *resolved = _fullpath(resolved_path, path, PATH_MAX);
+
+ if ((_access(resolved, 0) == -1) && (errno == ENOENT)) {
+ return NULL;
+ }
+ return resolved;
+}
+
+#elif defined (__NetBSD__)
+char *
+realpath(const char *path, char *resolved_path)
+{
+ ssize_t nbytes;
+
+ nbytes = readlink(path, resolved_path, PATH_MAX);
+ if (nbytes == -1) {
+ return NULL;
+ }
+ return resolved_path;
+}
+
+#else
+#error No realpath() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+#ifdef _WIN32
+struct tm *
+localtime_r(const time_t *timep, struct tm *result)
+{
+ errno_t res = localtime_s(result, timep);
+
+ if (res) {
+ return NULL;
+ } else {
+ return result;
+ }
+}
+
+#else
+#error No localtime_r() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_GMTIME_R
+#ifdef _WIN32
+struct tm *
+gmtime_r(const time_t *timep, struct tm *result)
+{
+ errno_t res = gmtime_s(result, timep);
+
+ if (res) {
+ return NULL;
+ } else {
+ return result;
+ }
+}
+
+#else
+#error No gmtime_r() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_DIRNAME
+#ifdef _WIN32
+#include <shlwapi.h>
+char *
+dirname(char *path)
+{
+ PathRemoveFileSpecA(path);
+ return path;
+}
+
+#else
+#error No dirname() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_SETENV
+#ifdef _WIN32
+int
+setenv(const char *name, const char *value, int overwrite)
+{
+ int errcode = 0;
+
+ if (!overwrite) {
+ size_t envsize = 0;
+
+ errcode = getenv_s(&envsize, NULL, 0, name);
+ if (errcode || envsize) {
+ return errcode;
+ }
+ }
+ return _putenv_s(name, value);
+}
+
+#else
+#error No setenv() implementation for this platform is available.
+#endif
+#endif
diff --git a/compat/compat.h.in b/compat/compat.h.in
new file mode 100644
index 0000000..c697d6c
--- /dev/null
+++ b/compat/compat.h.in
@@ -0,0 +1,203 @@
+/**
+ * @file compat.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief compatibility functions header
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef _COMPAT_H_
+#define _COMPAT_H_
+
+#ifdef _WIN32
+/* headers are broken on Windows, which means that some of them simply *have* to come first */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#endif
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef __WORDSIZE
+# if defined __x86_64__ && !defined __ILP32__
+# define __WORDSIZE 64
+# else
+# define __WORDSIZE 32
+# endif
+#endif
+
+#ifndef __INT64_C
+# if __WORDSIZE == 64
+# define __INT64_C(c) c ## L
+# define __UINT64_C(c) c ## UL
+# else
+# define __INT64_C(c) c ## LL
+# define __UINT64_C(c) c ## ULL
+# endif
+#endif
+
+#if defined (__GNUC__) || defined (__llvm__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+# define _PACKED __attribute__((__packed__))
+#else
+# define UNUSED(x) UNUSED_ ## x
+# define _PACKED
+#endif
+
+#cmakedefine HAVE_VDPRINTF
+#cmakedefine HAVE_ASPRINTF
+#cmakedefine HAVE_VASPRINTF
+#cmakedefine HAVE_GETLINE
+#cmakedefine HAVE_STRNDUP
+#cmakedefine HAVE_STRNSTR
+#cmakedefine HAVE_STRDUPA
+#cmakedefine HAVE_STRCHRNUL
+#cmakedefine HAVE_GET_CURRENT_DIR_NAME
+#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK
+#cmakedefine HAVE_TM_GMTOFF
+#cmakedefine HAVE_TIME_H_TIMEZONE
+#cmakedefine HAVE_REALPATH
+#cmakedefine HAVE_LOCALTIME_R
+#cmakedefine HAVE_GMTIME_R
+#cmakedefine HAVE_STRPTIME
+#cmakedefine HAVE_MMAP
+#cmakedefine HAVE_DIRNAME
+#cmakedefine HAVE_STRCASECMP
+#cmakedefine HAVE_SETENV
+
+#ifndef bswap64
+#define bswap64(val) \
+ ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \
+ (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \
+ (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \
+ (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) )
+#endif
+
+#undef le64toh
+#undef htole64
+
+#cmakedefine IS_BIG_ENDIAN
+
+#ifdef IS_BIG_ENDIAN
+# define le64toh(x) bswap64(x)
+# define htole64(x) bswap64(x)
+#else
+# define le64toh(x) (x)
+# define htole64(x) (x)
+#endif
+
+#cmakedefine HAVE_STDATOMIC
+
+#ifdef HAVE_STDATOMIC
+# include <stdatomic.h>
+
+# define ATOMIC_T atomic_uint_fast32_t
+# define ATOMIC_T_MAX UINT_FAST32_MAX
+
+# define ATOMIC_STORE_RELAXED(var, x) atomic_store_explicit(&(var), x, memory_order_relaxed)
+# define ATOMIC_LOAD_RELAXED(var) atomic_load_explicit(&(var), memory_order_relaxed)
+# define ATOMIC_INC_RELAXED(var) atomic_fetch_add_explicit(&(var), 1, memory_order_relaxed)
+# define ATOMIC_ADD_RELAXED(var, x) atomic_fetch_add_explicit(&(var), x, memory_order_relaxed)
+# define ATOMIC_DEC_RELAXED(var) atomic_fetch_sub_explicit(&(var), 1, memory_order_relaxed)
+# define ATOMIC_SUB_RELAXED(var, x) atomic_fetch_sub_explicit(&(var), x, memory_order_relaxed)
+#else
+# include <stdint.h>
+
+# define ATOMIC_T uint32_t
+# define ATOMIC_T_MAX UINT32_MAX
+
+# define ATOMIC_STORE_RELAXED(var, x) ((var) = (x))
+# define ATOMIC_LOAD_RELAXED(var) (var)
+# ifndef _WIN32
+# define ATOMIC_INC_RELAXED(var) __sync_fetch_and_add(&(var), 1)
+# define ATOMIC_ADD_RELAXED(var, x) __sync_fetch_and_add(&(var), x)
+# define ATOMIC_DEC_RELAXED(var) __sync_fetch_and_sub(&(var), 1)
+# define ATOMIC_SUB_RELAXED(var, x) __sync_fetch_and_sub(&(var), x)
+# else
+# include <windows.h>
+# define ATOMIC_INC_RELAXED(var) InterlockedExchangeAdd(&(var), 1)
+# define ATOMIC_ADD_RELAXED(var, x) InterlockedExchangeAdd(&(var), x)
+# define ATOMIC_DEC_RELAXED(var) InterlockedExchangeAdd(&(var), -1)
+# define ATOMIC_SUB_RELAXED(var, x) InterlockedExchangeAdd(&(var), -(x))
+# endif
+#endif
+
+#ifndef HAVE_VDPRINTF
+int vdprintf(int fd, const char *format, va_list ap);
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...);
+#endif
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **strp, const char *fmt, va_list ap);
+#endif
+
+#ifndef HAVE_GETLINE
+ssize_t getline(char **lineptr, size_t *n, FILE *stream);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRNSTR
+char *strnstr(const char *s, const char *find, size_t slen);
+#endif
+
+#ifndef HAVE_STRDUPA
+#define strdupa(s) ( \
+{ \
+ char *buf; \
+ size_t len = strlen(s); \
+ buf = alloca(len + 1); \
+ buf[len] = '\0'; \
+ (char *)memcpy(buf, s, len); \
+})
+#endif
+
+#ifndef HAVE_STRCHRNUL
+char *strchrnul(const char *s, int c);
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *get_current_dir_name(void);
+#endif
+
+#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
+int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime);
+#endif
+
+#ifndef HAVE_REALPATH
+char *realpath(const char *path, char *resolved_path);
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+#endif
+
+#ifndef HAVE_STRPTIME
+char *strptime(const char *s, const char *format, struct tm *tm);
+#endif
+
+#if defined (_WIN32)
+# define strtok_r strtok_s
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite);
+#endif
+
+#endif /* _COMPAT_H_ */
diff --git a/compat/posix-shims/libgen.h b/compat/posix-shims/libgen.h
new file mode 100644
index 0000000..014bb07
--- /dev/null
+++ b/compat/posix-shims/libgen.h
@@ -0,0 +1 @@
+char *dirname(char *path);
diff --git a/compat/posix-shims/strings.h b/compat/posix-shims/strings.h
new file mode 100644
index 0000000..c917a66
--- /dev/null
+++ b/compat/posix-shims/strings.h
@@ -0,0 +1,9 @@
+#include <compat.h>
+
+#ifndef HAVE_STRCASECMP
+#ifdef _MSC_VER
+#define strcasecmp _stricmp
+#else
+#error No strcasecmp() implementation for this platform is available.
+#endif
+#endif
diff --git a/compat/posix-shims/unistd.h b/compat/posix-shims/unistd.h
new file mode 100644
index 0000000..d7679c5
--- /dev/null
+++ b/compat/posix-shims/unistd.h
@@ -0,0 +1,78 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H 1
+
+/* headers are broken on Windows, which means that some of them simply *have* to come first */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+
+/* This is intended as a drop-in replacement for unistd.h on Windows.
+ * Please add functionality as neeeded.
+ * https://stackoverflow.com/a/826027/1202830
+ */
+
+#include <stdlib.h>
+#include <io.h>
+#include <process.h> /* for getpid() and the exec..() family */
+#include <direct.h> /* for _getcwd() and _chdir() */
+
+#define srandom srand
+#define random rand
+
+/* Values for the second argument to access.
+ These may be OR'd together. */
+#define R_OK 4 /* Test for read permission. */
+#define W_OK 2 /* Test for write permission. */
+#define X_OK 0 /* jkt: unsupported on Windows, so we don't really care */
+#define F_OK 0 /* Test for existence. */
+
+#define access _access
+#define dup2 _dup2
+#define execve _execve
+#define ftruncate _chsize
+#define unlink _unlink
+#define fileno _fileno
+#define getcwd _getcwd
+#define chdir _chdir
+#define isatty _isatty
+#define lseek _lseek
+#define fsync _commit
+#define timegm _mkgmtime
+/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */
+
+#define ssize_t SSIZE_T
+
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+/* should be in some equivalent to <sys/types.h> */
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#include <windows.h>
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 256
+#endif
+
+#ifndef S_ISREG
+# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif
+#ifndef S_IRUSR
+# define S_IRUSR _S_IREAD
+#endif
+#ifndef S_IWUSR
+# define S_IWUSR _S_IWRITE
+#endif
+
+#endif /* unistd.h */
diff --git a/compat/strptime.c b/compat/strptime.c
new file mode 100644
index 0000000..4044fc9
--- /dev/null
+++ b/compat/strptime.c
@@ -0,0 +1,214 @@
+/*
+ * This comes from the musl C library which has been licensed under the permissive MIT license.
+ *
+ * Downloaded from https://git.musl-libc.org/cgit/musl/plain/src/time/strptime.c,
+ * commit 98e688a9da5e7b2925dda17a2d6820dddf1fb287.
+ *
+ * Lobotomized to remove references to nl_langinfo().
+ * Adjusted coding style to fit libyang's uncrustify rules.
+ * */
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+char *
+strptime(const char * restrict s, const char * restrict f, struct tm * restrict tm)
+{
+ int i, w, neg, adj, min, range, *dest, dummy;
+ int want_century = 0, century = 0, relyear = 0;
+
+ while (*f) {
+ if (*f != '%') {
+ if (isspace(*f)) {
+ for ( ; *s && isspace(*s); s++) {}
+ } else if (*s != *f) {
+ return 0;
+ } else {
+ s++;
+ }
+ f++;
+ continue;
+ }
+ f++;
+ if (*f == '+') {
+ f++;
+ }
+ if (isdigit(*f)) {
+ char *new_f;
+
+ w = strtoul(f, &new_f, 10);
+ f = new_f;
+ } else {
+ w = -1;
+ }
+ adj = 0;
+ switch (*f++) {
+ case 'a':
+ case 'A':
+ case 'b':
+ case 'B':
+ case 'h':
+ case 'c':
+ goto fail_nl_langinfo;
+ case 'C':
+ dest = &century;
+ if (w < 0) {
+ w = 2;
+ }
+ want_century |= 2;
+ goto numeric_digits;
+ case 'd':
+ case 'e':
+ dest = &tm->tm_mday;
+ min = 1;
+ range = 31;
+ goto numeric_range;
+ case 'D':
+ s = strptime(s, "%m/%d/%y", tm);
+ if (!s) {
+ return 0;
+ }
+ break;
+ case 'H':
+ dest = &tm->tm_hour;
+ min = 0;
+ range = 24;
+ goto numeric_range;
+ case 'I':
+ dest = &tm->tm_hour;
+ min = 1;
+ range = 12;
+ goto numeric_range;
+ case 'j':
+ dest = &tm->tm_yday;
+ min = 1;
+ range = 366;
+ adj = 1;
+ goto numeric_range;
+ case 'm':
+ dest = &tm->tm_mon;
+ min = 1;
+ range = 12;
+ adj = 1;
+ goto numeric_range;
+ case 'M':
+ dest = &tm->tm_min;
+ min = 0;
+ range = 60;
+ goto numeric_range;
+ case 'n':
+ case 't':
+ for ( ; *s && isspace(*s); s++) {}
+ break;
+ case 'p':
+ case 'r':
+ goto fail_nl_langinfo;
+ case 'R':
+ s = strptime(s, "%H:%M", tm);
+ if (!s) {
+ return 0;
+ }
+ break;
+ case 'S':
+ dest = &tm->tm_sec;
+ min = 0;
+ range = 61;
+ goto numeric_range;
+ case 'T':
+ s = strptime(s, "%H:%M:%S", tm);
+ if (!s) {
+ return 0;
+ }
+ break;
+ case 'U':
+ case 'W':
+ /* Throw away result, for now. (FIXME?) */
+ dest = &dummy;
+ min = 0;
+ range = 54;
+ goto numeric_range;
+ case 'w':
+ dest = &tm->tm_wday;
+ min = 0;
+ range = 7;
+ goto numeric_range;
+ case 'x':
+ case 'X':
+ goto fail_nl_langinfo;
+ case 'y':
+ dest = &relyear;
+ w = 2;
+ want_century |= 1;
+ goto numeric_digits;
+ case 'Y':
+ dest = &tm->tm_year;
+ if (w < 0) {
+ w = 4;
+ }
+ adj = 1900;
+ want_century = 0;
+ goto numeric_digits;
+ case '%':
+ if (*s++ != '%') {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+numeric_range:
+ if (!isdigit(*s)) {
+ return 0;
+ }
+ *dest = 0;
+ for (i = 1; i <= min + range && isdigit(*s); i *= 10) {
+ *dest = *dest * 10 + *s++ - '0';
+ }
+ if (*dest - min >= range) {
+ return 0;
+ }
+ *dest -= adj;
+ switch ((char *)dest - (char *)tm) {
+ case offsetof(struct tm, tm_yday):
+ ;
+ }
+ goto update;
+numeric_digits:
+ neg = 0;
+ if (*s == '+') {
+ s++;
+ } else if (*s == '-') {
+ neg = 1, s++;
+ }
+ if (!isdigit(*s)) {
+ return 0;
+ }
+ for (*dest = i = 0; i < w && isdigit(*s); i++) {
+ *dest = *dest * 10 + *s++ - '0';
+ }
+ if (neg) {
+ *dest = -*dest;
+ }
+ *dest -= adj;
+ goto update;
+update:
+ // FIXME
+ ;
+ }
+ }
+ if (want_century) {
+ tm->tm_year = relyear;
+ if (want_century & 2) {
+ tm->tm_year += century * 100 - 1900;
+ } else if (tm->tm_year <= 68) {
+ tm->tm_year += 100;
+ }
+ }
+ return (char *)s;
+fail_nl_langinfo:
+ fprintf(stderr, "strptime: nl_langinfo not available");
+ return NULL;
+}
diff --git a/distro/README.md b/distro/README.md
new file mode 100644
index 0000000..995cb53
--- /dev/null
+++ b/distro/README.md
@@ -0,0 +1,9 @@
+# upstream packaging
+
+## RPM-based system (Fedora, CentOS, SUSE, ...) quickstart
+
+sudo dnf install -y git rpm-build python3-pip
+pip3 install apkg
+apkg build -i
+
+See apkg docs: https://pkg.labs.nic.cz/pages/apkg/
diff --git a/distro/config/apkg.toml b/distro/config/apkg.toml
new file mode 100644
index 0000000..1eaa087
--- /dev/null
+++ b/distro/config/apkg.toml
@@ -0,0 +1,10 @@
+[project]
+name = "libyang"
+make_archive_script = "distro/scripts/make-archive.sh"
+
+[upstream]
+archive_url = "https://github.com/CESNET/libyang/archive/v{{ version }}/libyang-{{ version }}.tar.gz"
+version_script = "distro/scripts/upstream-version.sh"
+
+[apkg]
+compat = 2
diff --git a/distro/pkg/deb/README.Debian b/distro/pkg/deb/README.Debian
new file mode 100644
index 0000000..fc680d0
--- /dev/null
+++ b/distro/pkg/deb/README.Debian
@@ -0,0 +1,38 @@
+Debian packaging for libyang
+============================
+
+Where to file issues
+--------------------
+
+Please file issues on the Debian BTS as usual. You could also open issues
+on github, but if it's something about the Debian packaging it's better to
+stick with the proper Debian ways. The Debian BTS is where other people
+involved with Debian go look for bugs regarding a package, so that's where
+they should be.
+
+Building straight off git
+-------------------------
+
+Just the normal:
+
+```
+git clone https://github.com/CESNET/libyang -b debian/master
+cd libyang
+dpkg-buildpackage
+```
+
+Building a Debian .dsc
+----------------------
+
+Again, pretty much the normal:
+
+```
+git clone https://github.com/CESNET/libyang -b debian/master
+wget -Olibyang_0.16.105.orig.tar.gz https://github.com/CESNET/libyang/archive/v0.16-r3.tar.gz
+cd libyang
+dpkg-source -b .
+```
+
+(Note the diverging release numbering though.)
+
+ -- Ondřej Surý <Ondřej Surý <ondrej@debian.org>>, Fri, 22 May 2020 11:10:55 +0200
diff --git a/distro/pkg/deb/README.source b/distro/pkg/deb/README.source
new file mode 100644
index 0000000..b1d916d
--- /dev/null
+++ b/distro/pkg/deb/README.source
@@ -0,0 +1,66 @@
+This module uses gbp and upstream git repository
+
+To update this package, first import the changes from upstream:
+- git fetch --all
+
+Find the latest version:
+VERSION=$(git describe --tags $(git rev-list '--tags=v2*' --max-count=1) | sed 's/^v//')
+echo $VERSION
+
+Update debian/sid branch
+- git checkout debian/master
+- git merge v$VERSION
+
+Update the debian/copyright file:
+- cme update dpkg-copyright
+See also https://github.com/dod38fr/config-model/wiki/Updating-debian-copyright-file-with-cme
+
+Check patches (and cleanup if necessary):
+- gbp pq rebase --commit --drop
+
+Download upstream tarball:
+- uscan --download --verbose --download-version $VERSION
+
+Add pristine-tar:
+- pristine-tar commit ../libyang_$VERSION.orig.tar.gz v$VERSION
+
+Generate a temporary changelog:
+- gbp dch --new-version=$VERSION-1 --snapshot --auto --commit debian/
+
+Test the first build::
+- gbp buildpackage --git-ignore-new --git-pristine-tar --git-no-purge
+
+Regenerate the symbol file (See https://qt-kde-team.pages.debian.net/symbolfiles.html)
+- pkgkde-symbolshelper batchpatch -v $VERSION < <path_to>/buildlog
+
+See https://www.debian.org/doc/manuals/maint-guide/advanced.en.html#librarysymbols
+and dpkg-gensymbols man page
+
+Update the changelog:
+- gbp dch -Ra -c
+
+Once everything is fine, build a source package and tag:
+- gbp buildpackage -S --git-tag
+
+Push on salsa:
+- gbp push
+
+For more details, see
+https://honk.sigxcpu.org/projects/git-buildpackage/manual-html/gbp.import.upstream-git.html#gbp.import.upstream.git.notarball
+
+Maintainer Notes (moved from README.md)
+=======================================
+
+* the project version number is actually the SO ABI version. The release
+ point numbers (0.16-r3) isn't used for Debian.
+
+* it's intentional that the SONAME is libyang.so.0.16 and not libyang.so.0.
+ ABI compatibility is indicated by the first two numbers being equal;
+ the third number is incremented for compatible changes. cf.
+ CESNET/libyang#656
+
+* the watch file doesn't work yet but the libyang people agreed to make
+ future release tags the same as the internal version number. At that point
+ the watch file will work.
+
+ -- Ondřej Surý <Ondřej Surý <ondrej@debian.org>>, Tue, 21 Jul 2020 16:31:52 +0200
diff --git a/distro/pkg/deb/changelog b/distro/pkg/deb/changelog
new file mode 100644
index 0000000..8a9ead5
--- /dev/null
+++ b/distro/pkg/deb/changelog
@@ -0,0 +1,5 @@
+libyang2 ({{ version }}-{{ release }}) unstable; urgency=medium
+
+ * upstream packaging
+
+ -- Ondřej Surý <ondrej@debian.org> Tue, 04 May 2021 22:20:03 +0200
diff --git a/distro/pkg/deb/compat b/distro/pkg/deb/compat
new file mode 100644
index 0000000..f599e28
--- /dev/null
+++ b/distro/pkg/deb/compat
@@ -0,0 +1 @@
+10
diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control
new file mode 100644
index 0000000..bdcbd19
--- /dev/null
+++ b/distro/pkg/deb/control
@@ -0,0 +1,75 @@
+Source: libyang2
+Section: libs
+Homepage: https://github.com/CESNET/libyang/
+Maintainer: Ondřej Surý <ondrej@debian.org>
+Priority: optional
+Standards-Version: 4.5.0
+Build-Depends: cmake,
+ debhelper (>= 10),
+ libcmocka-dev <!nocheck>,
+ libpcre2-dev,
+ pkg-config
+Vcs-Browser: https://github.com/CESNET/libyang/tree/master
+Vcs-Git: https://github.com/CESNET/libyang.git
+
+Package: libyang2
+Depends: ${misc:Depends},
+ ${shlibs:Depends}
+Architecture: any
+Multi-Arch: same
+Description: parser toolkit for IETF YANG data modeling - runtime
+ Libyang implements functions to process schemas expressed in the
+ YANG data modeling language defined by the IETF in RFCs 6020/7950.
+ Schemas expressed in this language primarily describe configuration
+ used by larger network equipment like routers and switches.
+ .
+ In addition to handling the schemas itself, the library also provides
+ functions to process data described by the schemas.
+ .
+ The library is implemented in C and provides an API for other software
+ to use in processing configurations.
+
+Package: libyang2-dev
+Depends: libpcre2-dev,
+ libyang2 (= ${binary:Version}),
+ ${misc:Depends}
+Conflicts: libyang-dev
+Section: libdevel
+Architecture: any
+Multi-Arch: same
+Description: parser toolkit for IETF YANG data modeling - development files
+ Libyang implements functions to process schemas expressed in the
+ YANG data modeling language defined by the IETF in RFCs 6020/7950.
+ Schemas expressed in this language primarily describe configuration
+ used by larger network equipment like routers and switches.
+ .
+ In addition to handling the schemas itself, the library also provides
+ functions to process data described by the schemas.
+ .
+ This package contains the C headers, a pkgconfig file, and .so entry point
+ for libyang.
+
+Package: libyang2-tools
+Depends: libyang2 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends}
+Breaks: libyang-tools (<< ${source:Version})
+Replaces: libyang-tools (<< ${source:Version})
+Section: devel
+Architecture: any
+Multi-Arch: foreign
+Description: parser toolkit for IETF YANG data modeling - executable tools
+ This package provides the "yanglint" and "yangre" tools which can be used
+ during the creation of IETF YANG schemas. The tools are not generally
+ useful for normal operation where libyang primarily processes configuration
+ data, not schemas.
+
+Package: libyang-tools
+Depends: libyang2-tools (>= ${source:Version}),
+ ${misc:Depends}
+Section: oldlibs
+Architecture: all
+Multi-Arch: foreign
+Description: parser toolkit for IETF YANG data modeling [dummy package]
+ This is empty dependency package to pull the executable tools. It's
+ safe to remove.
diff --git a/distro/pkg/deb/copyright b/distro/pkg/deb/copyright
new file mode 100644
index 0000000..03af3c3
--- /dev/null
+++ b/distro/pkg/deb/copyright
@@ -0,0 +1,79 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: libyang
+License: BSD-3-clause
+
+Files: *
+Copyright: 2015-2021 by CESNET, z.s.p.o.
+License: BSD-3-clause
+
+Files: */iana-*.yin */iana-*.yang */ietf-*.yin */ietf-*.yang */ietf-*.h
+Copyright: 2011-2018 by the IETF Trust and the persons identified as authors
+License: IETF-BSD-3-clause
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+ .
+ This version of this YANG module is part of RFC 6536; see
+ the RFC itself for full legal notices.
+
+Files: tools/lint/linenoise/*
+Copyright:
+ Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+License: BSD-2-clause
+ All rights reserved.
+ .
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ .
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ .
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Files: debian/*
+Copyright: 2018 by David Lamparter
+ 2020-2021 Ondřej Surý
+License: BSD-3-clause
+
+License: BSD-3-clause
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ .
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ .
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ .
+ * Neither the name of libyang nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/distro/pkg/deb/gbp.conf b/distro/pkg/deb/gbp.conf
new file mode 100644
index 0000000..23917b9
--- /dev/null
+++ b/distro/pkg/deb/gbp.conf
@@ -0,0 +1,4 @@
+[DEFAULT]
+pristine-tar = False
+debian-branch = master
+upstream-tree = SLOPPY
diff --git a/distro/pkg/deb/libyang2-dev.install b/distro/pkg/deb/libyang2-dev.install
new file mode 100644
index 0000000..452d7e0
--- /dev/null
+++ b/distro/pkg/deb/libyang2-dev.install
@@ -0,0 +1,3 @@
+usr/include/libyang/*.h
+usr/lib/*/*.so
+usr/lib/*/pkgconfig/*.pc
diff --git a/distro/pkg/deb/libyang2-tools.examples b/distro/pkg/deb/libyang2-tools.examples
new file mode 100644
index 0000000..63c4cb1
--- /dev/null
+++ b/distro/pkg/deb/libyang2-tools.examples
@@ -0,0 +1 @@
+tools/lint/examples/*
diff --git a/distro/pkg/deb/libyang2-tools.install b/distro/pkg/deb/libyang2-tools.install
new file mode 100644
index 0000000..04f859b
--- /dev/null
+++ b/distro/pkg/deb/libyang2-tools.install
@@ -0,0 +1,3 @@
+usr/bin/yanglint
+usr/bin/yangre
+usr/share/man/man1
diff --git a/distro/pkg/deb/libyang2.install b/distro/pkg/deb/libyang2.install
new file mode 100644
index 0000000..e5f2c1c
--- /dev/null
+++ b/distro/pkg/deb/libyang2.install
@@ -0,0 +1,2 @@
+usr/lib/*/*.so.*
+usr/share/yang
diff --git a/distro/pkg/deb/libyang2.symbols b/distro/pkg/deb/libyang2.symbols
new file mode 100644
index 0000000..f461b83
--- /dev/null
+++ b/distro/pkg/deb/libyang2.symbols
@@ -0,0 +1,351 @@
+# SymbolsHelper-Confirmed: 2.0.0~ amd64
+libyang.so.2 libyang2 #MINVER#
+ ly_ctx_compile@Base 2.0.0~
+ ly_ctx_destroy@Base 2.0.0~
+ ly_ctx_get_change_count@Base 2.0.0~
+ ly_ctx_get_module@Base 2.0.0~
+ ly_ctx_get_module_imp_clb@Base 2.0.0~
+ ly_ctx_get_module_implemented@Base 2.0.0~
+ ly_ctx_get_module_implemented_ns@Base 2.0.0~
+ ly_ctx_get_module_iter@Base 2.0.0~
+ ly_ctx_get_module_latest@Base 2.0.0~
+ ly_ctx_get_module_latest_ns@Base 2.0.0~
+ ly_ctx_get_module_ns@Base 2.0.0~
+ ly_ctx_get_options@Base 2.0.0~
+ ly_ctx_get_searchdirs@Base 2.0.0~
+ ly_ctx_get_submodule2@Base 2.0.0~
+ ly_ctx_get_submodule2_latest@Base 2.0.0~
+ ly_ctx_get_submodule@Base 2.0.0~
+ ly_ctx_get_submodule_latest@Base 2.0.0~
+ ly_ctx_get_yanglib_data@Base 2.0.0~
+ ly_ctx_internal_modules_count@Base 2.0.0~
+ ly_ctx_load_module@Base 2.0.0~
+ ly_ctx_new@Base 2.0.0~
+ ly_ctx_new_yldata@Base 2.1.4~
+ ly_ctx_new_ylmem@Base 2.0.0~
+ ly_ctx_new_ylpath@Base 2.0.0~
+ ly_ctx_reset_latests@Base 2.0.0~
+ ly_ctx_set_ext_data_clb@Base 2.1.4~
+ ly_ctx_set_module_imp_clb@Base 2.0.0~
+ ly_ctx_set_options@Base 2.0.0~
+ ly_ctx_set_searchdir@Base 2.0.0~
+ ly_ctx_unset_options@Base 2.0.0~
+ ly_ctx_unset_searchdir@Base 2.0.0~
+ ly_ctx_unset_searchdir_last@Base 2.0.0~
+ ly_err_clean@Base 2.0.0~
+ ly_err_first@Base 2.0.0~
+ ly_err_free@Base 2.0.0~
+ ly_err_last@Base 2.0.0~
+ ly_err_new@Base 2.0.0~
+ ly_err_print@Base 2.0.0~
+ ly_errapptag@Base 2.0.0~
+ ly_errcode@Base 2.0.0~
+ ly_errmsg@Base 2.0.0~
+ ly_errpath@Base 2.0.0~
+ ly_get_log_clb@Base 2.0.0~
+ ly_in_fd@Base 2.0.0~
+ ly_in_file@Base 2.0.0~
+ ly_in_filepath@Base 2.0.0~
+ ly_in_free@Base 2.0.0~
+ ly_in_memory@Base 2.0.0~
+ ly_in_new_fd@Base 2.0.0~
+ ly_in_new_file@Base 2.0.0~
+ ly_in_new_filepath@Base 2.0.0~
+ ly_in_new_memory@Base 2.0.0~
+ ly_in_parsed@Base 2.0.0~
+ ly_in_reset@Base 2.0.0~
+ ly_in_type@Base 2.0.0~
+ ly_log_dbg_groups@Base 2.0.0~
+ ly_log_level@Base 2.0.0~
+ ly_log_options@Base 2.0.0~
+ ly_out_clb@Base 2.0.0~
+ ly_out_clb_arg@Base 2.0.0~
+ ly_out_fd@Base 2.0.0~
+ ly_out_file@Base 2.0.0~
+ ly_out_filepath@Base 2.0.0~
+ ly_out_free@Base 2.0.0~
+ ly_out_new_clb@Base 2.0.0~
+ ly_out_new_fd@Base 2.0.0~
+ ly_out_new_file@Base 2.0.0~
+ ly_out_new_filepath@Base 2.0.0~
+ ly_out_new_memory@Base 2.0.0~
+ ly_out_printed@Base 2.0.0~
+ ly_out_reset@Base 2.0.0~
+ ly_out_type@Base 2.0.0~
+ ly_print@Base 2.0.0~
+ ly_print_flush@Base 2.0.0~
+ ly_set_add@Base 2.0.0~
+ ly_set_clean@Base 2.0.0~
+ ly_set_contains@Base 2.0.0~
+ ly_set_dup@Base 2.0.0~
+ ly_set_erase@Base 2.0.0~
+ ly_set_free@Base 2.0.0~
+ ly_set_log_clb@Base 2.0.0~
+ ly_set_merge@Base 2.0.0~
+ ly_set_new@Base 2.0.0~
+ ly_set_rm@Base 2.0.0~
+ ly_set_rm_index@Base 2.0.0~
+ ly_time_str2time@Base 2.0.7~
+ ly_time_str2ts@Base 2.0.7~
+ ly_time_time2str@Base 2.0.7~
+ ly_time_ts2str@Base 2.0.7~
+ ly_vecode@Base 2.0.0~
+ ly_write@Base 2.0.0~
+ lyd_any_copy_value@Base 2.0.0~
+ lyd_any_value_str@Base 2.0.0~
+ lyd_change_meta@Base 2.0.0~
+ lyd_change_term@Base 2.0.0~
+ lyd_change_term_bin@Base 2.0.0~
+ lyd_change_term_canon@Base 2.0.0~
+ lyd_child_no_keys@Base 2.0.0~
+ lyd_compare_meta@Base 2.0.0~
+ lyd_compare_siblings@Base 2.0.0~
+ lyd_compare_single@Base 2.0.0~
+ lyd_diff_apply_all@Base 2.0.0~
+ lyd_diff_apply_module@Base 2.0.0~
+ lyd_diff_merge_all@Base 2.0.0~
+ lyd_diff_merge_module@Base 2.0.0~
+ lyd_diff_merge_tree@Base 2.0.0~
+ lyd_diff_reverse_all@Base 2.0.0~
+ lyd_diff_siblings@Base 2.0.0~
+ lyd_diff_tree@Base 2.0.0~
+ lyd_dup_meta_single@Base 2.0.0~
+ lyd_dup_siblings@Base 2.0.0~
+ lyd_dup_siblings_to_ctx@Base 2.1.4~
+ lyd_dup_single@Base 2.0.0~
+ lyd_dup_single_to_ctx@Base 2.1.4~
+ lyd_eval_xpath2@Base 2.0.112~
+ lyd_eval_xpath3@Base 2.1.4~
+ lyd_eval_xpath@Base 2.0.112~
+ lyd_find_meta@Base 2.0.0~
+ lyd_find_path@Base 2.0.0~
+ lyd_find_sibling_dup_inst_set@Base 2.0.0~
+ lyd_find_sibling_first@Base 2.0.0~
+ lyd_find_sibling_opaq_next@Base 2.0.0~
+ lyd_find_sibling_val@Base 2.0.0~
+ lyd_find_target@Base 2.0.112~
+ lyd_find_xpath2@Base 2.0.112~
+ lyd_find_xpath3@Base 2.1.4~
+ lyd_find_xpath4@Base 2.1.4~
+ lyd_find_xpath@Base 2.0.0~
+ lyd_first_sibling@Base 2.0.0~
+ lyd_free_all@Base 2.0.0~
+ lyd_free_attr_siblings@Base 2.0.0~
+ lyd_free_attr_single@Base 2.0.0~
+ lyd_free_meta_siblings@Base 2.0.0~
+ lyd_free_meta_single@Base 2.0.0~
+ lyd_free_siblings@Base 2.0.0~
+ lyd_free_tree@Base 2.0.0~
+ lyd_insert_after@Base 2.0.0~
+ lyd_insert_before@Base 2.0.0~
+ lyd_insert_child@Base 2.0.0~
+ lyd_insert_sibling@Base 2.0.0~
+ lyd_is_default@Base 2.0.0~
+ lyd_list_pos@Base 2.0.0~
+ lyd_lyb_data_length@Base 2.0.0~
+ lyd_merge_module@Base 2.0.7~
+ lyd_merge_siblings@Base 2.0.0~
+ lyd_merge_tree@Base 2.0.0~
+ lyd_new_any@Base 2.0.0~
+ lyd_new_attr2@Base 2.0.0~
+ lyd_new_attr@Base 2.0.0~
+ lyd_new_ext_any@Base 2.0.0~
+ lyd_new_ext_inner@Base 2.0.0~
+ lyd_new_ext_list@Base 2.0.0~
+ lyd_new_ext_path@Base 2.0.0~
+ lyd_new_ext_term@Base 2.0.0~
+ lyd_new_implicit_all@Base 2.0.0~
+ lyd_new_implicit_module@Base 2.0.0~
+ lyd_new_implicit_tree@Base 2.0.0~
+ lyd_new_inner@Base 2.0.0~
+ lyd_new_list2@Base 2.0.0~
+ lyd_new_list@Base 2.0.0~
+ lyd_new_list_bin@Base 2.0.112~
+ lyd_new_list_canon@Base 2.0.112~
+ lyd_new_meta2@Base 2.0.0~
+ lyd_new_meta@Base 2.0.0~
+ lyd_new_opaq2@Base 2.0.0~
+ lyd_new_opaq@Base 2.0.0~
+ lyd_new_path2@Base 2.0.0~
+ lyd_new_path@Base 2.0.0~
+ lyd_new_term@Base 2.0.0~
+ lyd_new_term_bin@Base 2.0.0~
+ lyd_new_term_canon@Base 2.0.0~
+ lyd_node_should_print@Base 2.1.4~
+ lyd_owner_module@Base 2.0.0~
+ lyd_parse_data@Base 2.0.0~
+ lyd_parse_data_fd@Base 2.0.0~
+ lyd_parse_data_mem@Base 2.0.0~
+ lyd_parse_data_path@Base 2.0.0~
+ lyd_parse_ext_data@Base 2.0.0~
+ lyd_parse_ext_op@Base 2.0.0~
+ lyd_parse_op@Base 2.0.0~
+ lyd_parse_opaq_error@Base 2.1.4~
+ lyd_path@Base 2.0.0~
+ lyd_print_all@Base 2.0.0~
+ lyd_print_clb@Base 2.0.0~
+ lyd_print_fd@Base 2.0.0~
+ lyd_print_file@Base 2.0.0~
+ lyd_print_mem@Base 2.0.0~
+ lyd_print_path@Base 2.0.0~
+ lyd_print_tree@Base 2.0.0~
+ lyd_target@Base 2.0.0~
+ lyd_unlink_siblings@Base 2.0.112~
+ lyd_unlink_tree@Base 2.0.0~
+ lyd_validate_all@Base 2.0.0~
+ lyd_validate_module@Base 2.0.0~
+ lyd_validate_op@Base 2.0.0~
+ lyd_value_compare@Base 2.0.0~
+ lyd_value_get_canonical@Base 2.0.0~
+ lyd_value_validate@Base 2.0.0~
+ lydict_insert@Base 2.0.0~
+ lydict_insert_zc@Base 2.0.0~
+ lydict_remove@Base 2.0.0~
+ lyplg_add@Base 2.0.0~
+ lyplg_ext_cfree_instance_substatements@Base 2.1.4~
+ lyplg_ext_compile_extension_instance@Base 2.1.4~
+ lyplg_ext_compile_get_ctx@Base 2.1.4~
+ lyplg_ext_compile_get_cur_mod@Base 2.1.4~
+ lyplg_ext_compile_get_options@Base 2.1.4~
+ lyplg_ext_compile_get_pmod@Base 2.1.4~
+ lyplg_ext_compile_log@Base 2.1.4~
+ lyplg_ext_compile_log_path@Base 2.1.4~
+ lyplg_ext_get_data@Base 2.1.4~
+ lyplg_ext_get_storage@Base 2.1.4~
+ lyplg_ext_insert@Base 2.1.4~
+ lyplg_ext_nodetype2stmt@Base 2.1.4~
+ lyplg_ext_parse_extension_instance@Base 2.1.4~
+ lyplg_ext_parse_get_cur_pmod@Base 2.1.4~
+ lyplg_ext_parse_log@Base 2.1.4~
+ lyplg_ext_parsed_get_storage@Base 2.1.4~
+ lyplg_ext_pfree_instance_substatements@Base 2.1.4~
+ lyplg_ext_print_get_level@Base 2.1.4~
+ lyplg_ext_print_get_options@Base 2.1.4~
+ lyplg_ext_print_get_out@Base 2.1.4~
+ lyplg_ext_print_info_extension_instance@Base 2.1.4~
+ lyplg_ext_schema_mount_create_context@Base 2.1.4~
+ lyplg_ext_stmt2str@Base 2.1.4~
+ lyplg_type_bits_bitmap_size@Base 2.0.7~
+ lyplg_type_bits_is_bit_set@Base 2.0.7~
+ lyplg_type_check_hints@Base 2.0.0~
+ lyplg_type_check_status@Base 2.0.112~
+ lyplg_type_compare_binary@Base 2.0.7~
+ lyplg_type_compare_bits@Base 2.0.7~
+ lyplg_type_compare_boolean@Base 2.0.7~
+ lyplg_type_compare_decimal64@Base 2.0.7~
+ lyplg_type_compare_identityref@Base 2.0.0~
+ lyplg_type_compare_instanceid@Base 2.0.0~
+ lyplg_type_compare_int@Base 2.0.7~
+ lyplg_type_compare_leafref@Base 2.0.0~
+ lyplg_type_compare_simple@Base 2.0.0~
+ lyplg_type_compare_uint@Base 2.0.7~
+ lyplg_type_compare_union@Base 2.0.0~
+ lyplg_type_dup_binary@Base 2.0.7~
+ lyplg_type_dup_bits@Base 2.0.0~
+ lyplg_type_dup_instanceid@Base 2.0.0~
+ lyplg_type_dup_leafref@Base 2.0.0~
+ lyplg_type_dup_simple@Base 2.0.0~
+ lyplg_type_dup_union@Base 2.0.0~
+ lyplg_type_dup_xpath10@Base 2.0.0~
+ lyplg_type_free_binary@Base 2.0.7~
+ lyplg_type_free_bits@Base 2.0.0~
+ lyplg_type_free_instanceid@Base 2.0.0~
+ lyplg_type_free_leafref@Base 2.0.0~
+ lyplg_type_free_simple@Base 2.0.0~
+ lyplg_type_free_union@Base 2.0.0~
+ lyplg_type_free_xpath10@Base 2.0.0~
+ lyplg_type_get_prefix@Base 2.0.0~
+ lyplg_type_identity_isderived@Base 2.0.0~
+ lyplg_type_identity_module@Base 2.0.0~
+ lyplg_type_lypath_check_status@Base 2.0.112~
+ lyplg_type_lypath_free@Base 2.0.0~
+ lyplg_type_lypath_new@Base 2.0.0~
+ lyplg_type_make_implemented@Base 2.0.0~
+ lyplg_type_parse_dec64@Base 2.0.0~
+ lyplg_type_parse_int@Base 2.0.0~
+ lyplg_type_parse_uint@Base 2.0.0~
+ lyplg_type_prefix_data_dup@Base 2.0.0~
+ lyplg_type_prefix_data_free@Base 2.0.0~
+ lyplg_type_prefix_data_new@Base 2.0.0~
+ lyplg_type_print_binary@Base 2.0.7~
+ lyplg_type_print_bits@Base 2.0.7~
+ lyplg_type_print_boolean@Base 2.0.7~
+ lyplg_type_print_decimal64@Base 2.0.7~
+ lyplg_type_print_enum@Base 2.0.7~
+ lyplg_type_print_identityref@Base 2.0.0~
+ lyplg_type_print_instanceid@Base 2.0.0~
+ lyplg_type_print_int@Base 2.0.7~
+ lyplg_type_print_leafref@Base 2.0.0~
+ lyplg_type_print_simple@Base 2.0.0~
+ lyplg_type_print_uint@Base 2.0.7~
+ lyplg_type_print_union@Base 2.0.0~
+ lyplg_type_print_xpath10@Base 2.0.0~
+ lyplg_type_print_xpath10_value@Base 2.1.4~
+ lyplg_type_resolve_leafref@Base 2.0.0~
+ lyplg_type_store_binary@Base 2.0.0~
+ lyplg_type_store_bits@Base 2.0.0~
+ lyplg_type_store_boolean@Base 2.0.0~
+ lyplg_type_store_decimal64@Base 2.0.0~
+ lyplg_type_store_empty@Base 2.0.0~
+ lyplg_type_store_enum@Base 2.0.0~
+ lyplg_type_store_identityref@Base 2.0.0~
+ lyplg_type_store_instanceid@Base 2.0.0~
+ lyplg_type_store_int@Base 2.0.0~
+ lyplg_type_store_leafref@Base 2.0.0~
+ lyplg_type_store_string@Base 2.0.0~
+ lyplg_type_store_uint@Base 2.0.0~
+ lyplg_type_store_union@Base 2.0.0~
+ lyplg_type_store_xpath10@Base 2.0.0~
+ lyplg_type_validate_instanceid@Base 2.0.0~
+ lyplg_type_validate_leafref@Base 2.0.0~
+ lyplg_type_validate_patterns@Base 2.0.0~
+ lyplg_type_validate_range@Base 2.0.0~
+ lyplg_type_validate_union@Base 2.0.0~
+ lyplg_type_xpath10_print_token@Base 2.1.4~
+ lys_feature_value@Base 2.0.0~
+ lys_find_child@Base 2.0.0~
+ lys_find_expr_atoms@Base 2.0.0~
+ lys_find_lypath_atoms@Base 2.0.0~
+ lys_find_path@Base 2.0.0~
+ lys_find_path_atoms@Base 2.0.0~
+ lys_find_xpath@Base 2.0.0~
+ lys_find_xpath_atoms@Base 2.0.0~
+ lys_getnext@Base 2.0.0~
+ lys_getnext_ext@Base 2.0.0~
+ lys_identity_iffeature_value@Base 2.0.112~
+ lys_nodetype2str@Base 2.0.0~
+ lys_parse@Base 2.0.0~
+ lys_parse_fd@Base 2.0.0~
+ lys_parse_mem@Base 2.0.0~
+ lys_parse_path@Base 2.0.0~
+ lys_print_clb@Base 2.0.0~
+ lys_print_fd@Base 2.0.0~
+ lys_print_file@Base 2.0.0~
+ lys_print_mem@Base 2.0.0~
+ lys_print_module@Base 2.0.0~
+ lys_print_node@Base 2.0.0~
+ lys_print_path@Base 2.0.0~
+ lys_print_submodule@Base 2.0.0~
+ lys_search_localfile@Base 2.0.0~
+ lys_set_implemented@Base 2.0.0~
+ lysc_data_node@Base 2.1.4~
+ lysc_has_when@Base 2.0.0~
+ lysc_iffeature_value@Base 2.0.0~
+ lysc_module_dfs_full@Base 2.0.0~
+ lysc_node_actions@Base 2.0.0~
+ lysc_node_child@Base 2.0.0~
+ lysc_node_musts@Base 2.0.0~
+ lysc_node_notifs@Base 2.0.0~
+ lysc_node_when@Base 2.0.0~
+ lysc_owner_module@Base 2.0.112~
+ lysc_path@Base 2.0.0~
+ lysc_tree_dfs_full@Base 2.0.0~
+ lysp_feature_next@Base 2.0.0~
+ lysp_node_actions@Base 2.0.0~
+ lysp_node_child@Base 2.0.0~
+ lysp_node_groupings@Base 2.0.0~
+ lysp_node_notifs@Base 2.0.0~
+ lysp_node_typedefs@Base 2.0.0~
+ lyxp_get_expr@Base 2.0.0~
+ lyxp_vars_free@Base 2.0.112~
+ lyxp_vars_set@Base 2.0.112~
diff --git a/distro/pkg/deb/rules b/distro/pkg/deb/rules
new file mode 100755
index 0000000..58f845d
--- /dev/null
+++ b/distro/pkg/deb/rules
@@ -0,0 +1,12 @@
+#!/usr/bin/make -f
+#export DH_VERBOSE=1
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+
+include /usr/share/dpkg/default.mk
+
+%:
+ dh $@
+
+override_dh_auto_configure:
+ dh_auto_configure -- \
+ -DCMAKE_BUILD_TYPE:String="Release"
diff --git a/distro/pkg/deb/source/format b/distro/pkg/deb/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/distro/pkg/deb/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/distro/pkg/deb/tests/control b/distro/pkg/deb/tests/control
new file mode 100644
index 0000000..e4613e6
--- /dev/null
+++ b/distro/pkg/deb/tests/control
@@ -0,0 +1,3 @@
+Tests: yanglint
+Depends: gzip,
+ libyang2-tools
diff --git a/distro/pkg/deb/tests/yanglint b/distro/pkg/deb/tests/yanglint
new file mode 100755
index 0000000..eda5573
--- /dev/null
+++ b/distro/pkg/deb/tests/yanglint
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+set -e
+
+# Setup the test file
+trap 'rm -f "${TESTFILE}"; rmdir "${TESTDIR}"' EXIT
+TESTDIR=$(mktemp -d /tmp/yanglint.XXXXXX)
+TESTFILE="${TESTDIR}/ietf-interfaces.yang"
+
+# Unpack or copy the test file
+if test -f /usr/share/doc/libyang2-tools/examples/ietf-interfaces.yang.gz; then
+ gunzip -c < /usr/share/doc/libyang2-tools/examples/ietf-interfaces.yang.gz > "${TESTFILE}"
+else
+ cp /usr/share/doc/libyang2-tools/examples/ietf-interfaces.yang "${TESTFILE}"
+fi
+
+# Lint the test file
+yanglint "${TESTFILE}"
diff --git a/distro/pkg/deb/watch b/distro/pkg/deb/watch
new file mode 100644
index 0000000..b391882
--- /dev/null
+++ b/distro/pkg/deb/watch
@@ -0,0 +1,4 @@
+version=4
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%libyang-$1.tar.gz%" \
+ https://github.com/CESNET/libyang/releases \
+ (?:.*?/)?v?(\d[\d.]*)(?:-r\d+)?\.tar\.gz debian uupdate
diff --git a/distro/pkg/rpm/libyang.spec b/distro/pkg/rpm/libyang.spec
new file mode 100644
index 0000000..374fc4e
--- /dev/null
+++ b/distro/pkg/rpm/libyang.spec
@@ -0,0 +1,94 @@
+Name: libyang
+Version: {{ version }}
+Release: {{ release }}%{?dist}
+Summary: YANG data modeling language library
+Url: https://github.com/CESNET/libyang
+Source: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz
+License: BSD
+
+BuildRequires: cmake
+BuildRequires: doxygen
+BuildRequires: gcc
+BuildRequires: cmake(cmocka) >= 1.0.1
+BuildRequires: make
+BuildRequires: pkgconfig(libpcre2-8) >= 10.21
+
+%package devel
+Summary: Development files for libyang
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: pcre2-devel
+
+%package devel-doc
+Summary: Documentation of libyang API
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%package tools
+Summary: YANG validator tools
+Requires: %{name}%{?_isa} = %{version}-%{release}
+# This was not properly split out before
+Conflicts: %{name} < 1.0.225-3
+
+%description devel
+Headers of libyang library.
+
+%description devel-doc
+Documentation of libyang API.
+
+%description tools
+YANG validator tools.
+
+%description
+Libyang is YANG data modeling language parser and toolkit
+written (and providing API) in C.
+
+%prep
+%autosetup -p1
+
+%build
+%cmake -DCMAKE_BUILD_TYPE=RELWITHDEBINFO
+%cmake_build
+
+%if "x%{?suse_version}" == "x"
+cd redhat-linux-build
+%endif
+make doc
+
+%check
+%if "x%{?suse_version}" == "x"
+cd redhat-linux-build
+%endif
+ctest --output-on-failure -V %{?_smp_mflags}
+
+%install
+%cmake_install
+
+mkdir -m0755 -p %{buildroot}/%{_docdir}/libyang
+cp -a doc/html %{buildroot}/%{_docdir}/libyang/html
+
+%files
+%license LICENSE
+%{_libdir}/libyang.so.2
+%{_libdir}/libyang.so.2.*
+%{_datadir}/yang/modules/libyang/*.yang
+%dir %{_datadir}/yang/
+%dir %{_datadir}/yang/modules/
+%dir %{_datadir}/yang/modules/libyang/
+
+%files tools
+%{_bindir}/yanglint
+%{_bindir}/yangre
+%{_datadir}/man/man1/yanglint.1.gz
+%{_datadir}/man/man1/yangre.1.gz
+
+%files devel
+%{_libdir}/libyang.so
+%{_libdir}/pkgconfig/libyang.pc
+%{_includedir}/libyang/*.h
+%dir %{_includedir}/libyang/
+
+%files devel-doc
+%{_docdir}/libyang
+
+%changelog
+* {{ now }} Jakub RužiÄka <jakub.ruzicka@nic.cz> - {{ version }}-{{ release }}
+- upstream package
diff --git a/distro/scripts/make-archive.sh b/distro/scripts/make-archive.sh
new file mode 100755
index 0000000..9b6295d
--- /dev/null
+++ b/distro/scripts/make-archive.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# create archive from current source using git
+
+VERSION=$(git describe --tags --always)
+# skip "v" from start of version number (if it exists) and replace - with .
+VERSION=${VERSION#v}
+VERSION=${VERSION//[-]/.}
+
+NAMEVER=libyang-$VERSION
+ARCHIVE=$NAMEVER.tar.gz
+
+git archive --format tgz --output $ARCHIVE --prefix $NAMEVER/ HEAD
+mkdir -p pkg/archives/dev/
+mv $ARCHIVE pkg/archives/dev/
+
+# apkg expects stdout to list archive files
+echo pkg/archives/dev/$ARCHIVE
diff --git a/distro/scripts/upstream-version.sh b/distro/scripts/upstream-version.sh
new file mode 100755
index 0000000..4489c8e
--- /dev/null
+++ b/distro/scripts/upstream-version.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+# get latest upstream libyang version from github
+
+RLS_URL=https://api.github.com/repos/CESNET/libyang/releases
+VERSION=$(curl -s $RLS_URL | grep tag_name | cut -d '"' -f 4 | sort --version-sort | tail -n 1)
+VERSION=${VERSION#v}
+echo $VERSION
diff --git a/distro/tests/control b/distro/tests/control
new file mode 100644
index 0000000..61b9060
--- /dev/null
+++ b/distro/tests/control
@@ -0,0 +1 @@
+Tests: test-pkg-config.sh test-yanglint.sh
diff --git a/distro/tests/test-pkg-config.sh b/distro/tests/test-pkg-config.sh
new file mode 100755
index 0000000..581e4d6
--- /dev/null
+++ b/distro/tests/test-pkg-config.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -ex
+
+version=`pkg-config --modversion libyang`
+echo "$version" | grep '2\.[0-9.]\+'
diff --git a/distro/tests/test-yanglint.sh b/distro/tests/test-yanglint.sh
new file mode 100755
index 0000000..70bf4b9
--- /dev/null
+++ b/distro/tests/test-yanglint.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -ex
+
+version=`yanglint --version`
+echo "$version" | grep '2\.[0-9.]\+'
diff --git a/doc/build.dox b/doc/build.dox
new file mode 100644
index 0000000..77fd165
--- /dev/null
+++ b/doc/build.dox
@@ -0,0 +1,139 @@
+/**
+ * @page build Building libyang
+ *
+ * [TOC]
+ *
+ * libyang utilizes CMake build system to detect necessary dependencies, checkout the build environment and prepare Makefiles
+ * for the compilation of the library and acompanied tools.
+ *
+ * @section buildRequirements Requirements
+ *
+ * @subsection buildRequirementsCompile Building Requirements
+ *
+ * - C compiler (gcc, clang, ...)
+ * - CMake >= 2.8.12
+ * - libpcre2 (including headers - devel package) >= 10.30
+ *
+ * @subsubsection buildRequirementsCompileOptional Optional Requirements
+ *
+ * - doxygen (for generating documentation)
+ * - cmocka >= 1.0.0 (for tests)
+ * - valgrind (for enhanced testing)
+ * - gcov (for code coverage)
+ * - lcov (for code coverage)
+ * - genhtml (for code coverage)
+ *
+ * @subsection buildRequirementsRun Runtime Requirements
+ *
+ * - libpcre2 (runtime package) >= 10.30
+ *
+ * @section buildCommands Building
+ *
+ * To simply build libyang library and the accompanied tools with the default settings, follow these steps (the `#` character
+ * indicates command(s) to run with `root` privileges):
+ *
+ * $ mkdir build; cd build
+ * $ cmake ..
+ * $ make
+ * # make install
+ *
+ * The default settings can be changed with various CMake variables set via command line or using e.g. `ccmake(1)` tool.
+ * The following sections introduces those variables and explain their meaning.
+ *
+ * @section buildTypes Build Types
+ *
+ * There are several build types according to the primary goal of using the built binaries.
+ *
+ * @subsection buildTypesRelese Release
+ *
+ * Build type to produce final binaries for production use without any debug option. The compiler flags are set as follows:
+ *
+ * -Wall -Wextra -Wno-missing-field-initializers -std=c99 -O3 -DNDEBUG
+ *
+ * Here is the list of the selected additional options available in the *Release* build type.
+ *
+ * Option | Description | Default
+ * ----------------------|------------------------------------------------------------------------------------|--------
+ * ENABLE_BUILD_TESTS | Build tests. | OFF
+ * ENABLE_VALGRIND_TESTS | Build tests with valgrind. | OFF
+ * ENABLE_COVERAGE | Build code coverage report from tests. | OFF
+ * ENABLE_FUZZ_TARGETS | Build target programs suitable for fuzzing with AFL. | OFF
+ * ENABLE_STATIC | Build static (.a) library | OFF
+ * INTERNAL_DOCS | Include developers notes and internal API description into generated Documentation | OFF
+ *
+ * Here is the list of available important targets for the `make(1)` tool:
+ *
+ * Target | Description
+ * -------|------------------------------------------------------------------------------------------------------------
+ * all | Default target, builds libyang library (.so), `yanglint(1)` and `yangre(1)`.
+ * clean | Removes files generated by the make process.
+ * cclean | Extends the `clean` target by removing all the cmake generated files.
+ * doc | Generate HTML documentation. Requires `doxygen(1)`.
+ *
+ * @subsection buildTypesDebug Debug
+ *
+ * `Debug` is the default build type, the produced binaries include additional debug information and the prepared tests are
+ * also built. The compiler flags are set as follows:
+ *
+ * -Wall -Wextra -Wno-missing-field-initializers -std=c99 -g3 -O0
+ *
+ * Here is the list of the selected additional options available in the *Debug* build type.
+ *
+ * Option | Description | Default
+ * ----------------------|------------------------------------------------------------------------------------|--------
+ * ENABLE_BUILD_TESTS | Build tests. | ON
+ * ENABLE_VALGRIND_TESTS | Build tests with valgrind. | ON
+ * ENABLE_COVERAGE | Build code coverage report from tests. | OFF
+ * ENABLE_FUZZ_TARGETS | Build target programs suitable for fuzzing with AFL. | OFF
+ * ENABLE_STATIC | Build static (.a) library | OFF
+ * INTERNAL_DOCS | Include developers notes and internal API description into generated Documentation | OFF
+ *
+ * Here is the list of available important targets for the `make(1)` tool:
+ *
+ * Target | Description
+ * -------------|------------------------------------------------------------------------------------------------------
+ * all | Default target, builds libyang library (.so), `yanglint(1)` and `yangre(1)`.
+ * clean | Removes files generated by the make process.
+ * cclean | Extends the `clean` target by removing all the cmake generated files.
+ * doc | Generate HTML documentation. Requires `doxygen(1)`.
+ * test | Run implementation tests. Requires `cmocka` (and `valgrind(1)` for part of the tests).
+ * format | Reformat source codes using `uncrustify(1)`.
+ * format-check | Dry-run of the `format` target.
+ *
+ * @subsection buildTypesABICheck ABICheck
+ *
+ * Special build type to perform check of the ABI/API compatibility and generate reports. In addition to the basic
+ * requirements, the build type requires some of the <a href="https://abi-laboratory.pro/">ABI Laboratory tools</a>:
+ * `abi-dumper(1)` and `abi-compliance-checker(1)`.
+ *
+ * The compiler flags are set as follows:
+ *
+ * -Wall -Wextra -Wno-missing-field-initializers -std=c99 -g -Og
+ *
+ * All the additional options are switched OFF and the settings should not be changed.
+ *
+ * Here is the list of available important targets for the `make(1)` tool:
+ *
+ * Target | Description
+ * -------------|------------------------------------------------------------------------------------------------------
+ * all | Default target, builds libyang library (.so), `yanglint(1)` and `yangre(1)`.
+ * clean | Removes files generated by the make process.
+ * cclean | Extends the `clean` target by removing all the cmake generated files.
+ * doc | Generate HTML documentation. Requires `doxygen(1)`.
+ * abi-check | Check the backward compatibility of the API/ABI changes. Requires `abi-compliance-checker(1)` and `abi-dump(1)`.
+ * abi-dump | Helper target for `abi-check` generating API/ABI reports. Requires `abi-dump(1)`.
+
+ * @subsection buildTypesDocOnly DocOnly
+ *
+ * Special build type to avoid any requirements except those for building documentation. There are no compiler flags set
+ * since the build type does not allow building any binary output. The settings of the additional options should not be
+ * changed.
+ *
+ * Here is the list of available important targets for the `make(1)` tool:
+ *
+ * Target | Description
+ * -------------|------------------------------------------------------------------------------------------------------
+ * all | Default target, does nothing.
+ * doc | Generate HTML documentation. Requires `doxygen(1)`.
+ *
+ */
diff --git a/doc/cesnet-style.css b/doc/cesnet-style.css
new file mode 100644
index 0000000..296c260
--- /dev/null
+++ b/doc/cesnet-style.css
@@ -0,0 +1,106 @@
+/* CESNET blue: #0068a2 */
+
+body {
+ background-color: #fff;
+}
+
+div.header {
+ background-image: none;
+ background-color: #fff;
+}
+
+div.contents {
+ background-color: #fff;
+ padding: 1.618em 3.236em;
+ max-width: 60em;
+ margin: auto;
+ margin-left: 0;
+ text-align: justify;
+}
+
+.sm-dox {
+ background-image: none;
+ background-color: #0068a2;
+ border-bottom: 1px solid white;
+}
+
+.sm-dox a {
+ background-image: none;
+ border-right: 1px solid white;
+ color: white;
+ text-shadow: none;
+}
+
+.sm-dox a:hover {
+ background-image: none;
+ background-color: rgba(0,0,0,0.3);
+}
+
+.sm-dox ul a:hover {
+ background-image: none;
+ background-color: #ddd;
+ text-shadow: none;
+ color: #555;
+}
+
+.navpath ul {
+ background-image: none;
+ background-color: #0068a2;
+}
+
+.navpath li.footer {
+ color: white;
+}
+img.footer {
+ height: 20px;
+}
+
+.navpath li.navelem a {
+ color: white;
+ text-shadow: none;
+}
+
+#side-nav {
+ background-color: #343131;
+}
+
+#nav-tree::-webkit-scrollbar {
+ width: 5px;
+}
+
+#nav-tree::-webkit-scrollbar-track {
+ background: #333;
+ border-radius: 50px;
+ }
+
+#nav-tree::-webkit-scrollbar-thumb {
+ background: #ccc;
+ border-radius: 50px;
+}
+
+#nav-tree {
+ background: none;
+}
+
+#nav-tree .item {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+#nav-tree .item:hover {
+ background-color: rgba(255,255,255,0.2);
+}
+
+#nav-tree a {
+ color: #fff;
+ font-size: 1.2em;
+}
+
+#nav-tree .selected {
+ background-image: none;
+ background-color: #0068a2;
+}
+
+#nav-tree-contents {
+ margin: 0;
+}
diff --git a/doc/compat_report.html b/doc/compat_report.html
new file mode 100644
index 0000000..5bcf4d9
--- /dev/null
+++ b/doc/compat_report.html
@@ -0,0 +1,7645 @@
+<!-- kind:binary;verdict:incompatible;affected:81.2;added:153;removed:114;type_problems_high:17;type_problems_medium:37;type_problems_low:90;interface_problems_high:18;interface_problems_medium:57;interface_problems_low:57;changed_constants:0;type_changes_other:7;tool_version:2.3 -->
+<!-- kind:source;verdict:incompatible;affected:85.9;added:154;removed:114;type_problems_high:112;type_problems_medium:2;type_problems_low:9;interface_problems_high:13;interface_problems_medium:47;interface_problems_low:52;changed_constants:0;type_changes_other:24;interface_changes_other:10;tool_version:2.3 -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width,initial-scale=1" />
+<meta name="keywords" content="libyang.so, compatibility, API, ABI, report" />
+<meta name="description" content="API/ABI compatibility report for the libyang.so object between 1.9.19 and 2.0.0 versions" />
+<title>libyang.so: 1.9.19 to 2.0.0 compatibility report</title>
+<style type="text/css">
+body {
+ font-family:Arial, sans-serif;
+ background-color:White;
+ color:Black;
+}
+hr {
+ color:Black;
+ background-color:Black;
+ height:1px;
+ border:0;
+}
+h1 {
+ margin-bottom:0px;
+ padding-bottom:0px;
+ font-size:1.625em;
+}
+h2 {
+ margin-bottom:0px;
+ padding-bottom:0px;
+ font-size:1.25em;
+ white-space:nowrap;
+}
+span.section {
+ font-weight:bold;
+ cursor:pointer;
+ color:#003E69;
+ white-space:nowrap;
+ margin-left:0.3125em;
+}
+span.new_sign {
+ font-weight:bold;
+ margin-left:1.65em;
+ color:#003E69;
+}
+span.new_sign_lbl {
+ margin-left:3em;
+ font-size:1em;
+ color:Black;
+}
+span:hover.section {
+ color:#336699;
+}
+span.sect_aff {
+ cursor:pointer;
+ padding-left:1.55em;
+ font-size:0.875em;
+ color:#cc3300;
+}
+span.sect_info {
+ cursor:pointer;
+ padding-left:1.55em;
+ font-size:0.875em;
+ color:Black;
+}
+span.ext {
+ font-weight:normal;
+}
+span.h_name {
+ color:#cc3300;
+ font-size:0.875em;
+ font-weight:bold;
+}
+div.h_list, div.lib_list {
+ font-size:0.94em;
+ padding-left:0.4em;
+}
+span.ns {
+ color:#408080;
+ font-size:0.94em;
+}
+span.lib_name {
+ color:Green;
+ font-size:0.875em;
+ font-weight:bold;
+}
+span.iname {
+ font-weight:bold;
+ color:#003E69;
+ margin-left:0.3125em;
+}
+span.iname_b {
+ font-weight:bold;
+}
+span.iname_a {
+ color:#333333;
+ font-weight:bold;
+ font-size:0.94em;
+}
+span.sym_p {
+ font-weight:normal;
+ white-space:normal;
+}
+span.sym_pd {
+ white-space:normal;
+}
+span.sym_p span, span.sym_pd span {
+ white-space:nowrap;
+}
+div.affect {
+ padding-left:1em;
+ padding-bottom:10px;
+ font-size:0.87em;
+ font-style:italic;
+ line-height:0.9em;
+}
+div.affected {
+ padding-left:1.9em;
+ padding-top:10px;
+}
+table.ptable {
+ border-collapse:collapse;
+ border:1px outset black;
+ margin-left:0.95em;
+ margin-top:3px;
+ margin-bottom:3px;
+ width:56.25em;
+}
+table.ptable td {
+ border:1px solid gray;
+ padding:3px;
+ font-size:0.875em;
+ text-align:left;
+ vertical-align:top;
+ max-width:28em;
+ word-wrap:break-word;
+}
+table.ptable th.pn {
+ width:2%;
+}
+table.ptable th.chg {
+ width:47%;
+}
+table.vtable {
+ border-collapse:collapse;
+ border:1px outset black;
+ margin-left:1.9em;
+ margin-top:0.7em;
+}
+table.vtable td {
+ border:1px solid gray;
+ padding:3px;
+ font-size:0.875em;
+ vertical-align:top;
+ max-width:450px;
+ word-wrap:break-word;
+}
+table.ptable th, table.vtable th {
+ background-color:#eeeeee;
+ font-weight:bold;
+ color:#333333;
+ font-family:Verdana, Arial;
+ font-size:0.875em;
+ border:1px solid gray;
+ text-align:center;
+ vertical-align:top;
+ white-space:nowrap;
+ padding:3px;
+}
+table.summary {
+ border-collapse:collapse;
+ border:1px outset black;
+}
+table.summary th {
+ background-color:#eeeeee;
+ font-weight:normal;
+ text-align:left;
+ font-size:0.94em;
+ white-space:nowrap;
+ border:1px inset gray;
+ padding:3px;
+}
+table.summary td {
+ text-align:right;
+ white-space:nowrap;
+ border:1px inset gray;
+ padding:3px 5px 3px 10px;
+}
+span.mngl {
+ padding-left:1em;
+ font-size:0.875em;
+ cursor:text;
+ color:#444444;
+ font-weight:bold;
+}
+span.pleft {
+ padding-left:2.5em;
+}
+span.sym_ver {
+ color:#333333;
+ white-space:nowrap;
+ font-family:"DejaVu Sans Mono", Monospace;
+}
+span.attr {
+ color:#333333;
+ font-weight:normal;
+}
+span.color_p {
+ font-style:italic;
+ color:Brown;
+}
+span.p {
+ font-style:italic;
+}
+span.fp {
+ font-style:italic;
+ background-color:#DCDCDC;
+}
+span.ttype {
+ font-weight:normal;
+}
+span.nowrap {
+ white-space:nowrap;
+}
+span.value {
+ font-weight:bold;
+}
+.passed {
+ background-color:#CCFFCC;
+ font-weight:normal;
+}
+.warning {
+ background-color:#F4F4AF;
+ font-weight:normal;
+}
+.failed {
+ background-color:#FFCCCC;
+ font-weight:normal;
+}
+.new {
+ background-color:#C6DEFF;
+ font-weight:normal;
+}
+.compatible {
+ background-color:#CCFFCC;
+ font-weight:normal;
+}
+.almost_compatible {
+ background-color:#FFDAA3;
+ font-weight:normal;
+}
+.incompatible {
+ background-color:#FFCCCC;
+ font-weight:normal;
+}
+.gray {
+ background-color:#DCDCDC;
+ font-weight:normal;
+}
+.top_ref {
+ font-size:0.69em;
+}
+.footer {
+ font-size:0.75em;
+}
+
+.tabset {
+ float:left;
+}
+a.tab {
+ border:1px solid Black;
+ float:left;
+ margin:0px 5px -1px 0px;
+ padding:3px 5px 3px 5px;
+ position:relative;
+ font-size:0.875em;
+ background-color:#DDD;
+ text-decoration:none;
+ color:Black;
+}
+a.disabled:hover
+{
+ color:Black;
+ background:#EEE;
+}
+a.active:hover
+{
+ color:Black;
+ background:White;
+}
+a.active {
+ border-bottom-color:White;
+ background-color:White;
+}
+div.tab {
+ border-top:1px solid Black;
+ padding:0px;
+ width:100%;
+ clear:both;
+}
+</style>
+<script type="text/javascript" language="JavaScript">
+<!--
+function showContent(header, id)
+{
+ e = document.getElementById(id);
+ if(e.style.display == 'none')
+ {
+ e.style.display = 'block';
+ e.style.visibility = 'visible';
+ header.innerHTML = header.innerHTML.replace(/\[[^0-9 ]\]/gi,"[&minus;]");
+ }
+ else
+ {
+ e.style.display = 'none';
+ e.style.visibility = 'hidden';
+ header.innerHTML = header.innerHTML.replace(/\[[^0-9 ]\]/gi,"[+]");
+ }
+}
+function initTabs()
+{
+ var url = window.location.href;
+ if(url.indexOf('_Source_')!=-1 || url.indexOf('#Source')!=-1)
+ {
+ var tab1 = document.getElementById('BinaryID');
+ var tab2 = document.getElementById('SourceID');
+ tab1.className='tab disabled';
+ tab2.className='tab active';
+ }
+ var sets = document.getElementsByTagName('div');
+ for (var i = 0; i < sets.length; i++)
+ {
+ if (sets[i].className.indexOf('tabset') != -1)
+ {
+ var tabs = [];
+ var links = sets[i].getElementsByTagName('a');
+ for (var j = 0; j < links.length; j++)
+ {
+ if (links[j].className.indexOf('tab') != -1)
+ {
+ tabs.push(links[j]);
+ links[j].tabs = tabs;
+ var tab = document.getElementById(links[j].href.substr(links[j].href.indexOf('#') + 1));
+ //reset all tabs on start
+ if (tab)
+ {
+ if (links[j].className.indexOf('active')!=-1) {
+ tab.style.display = 'block';
+ }
+ else {
+ tab.style.display = 'none';
+ }
+ }
+ links[j].onclick = function()
+ {
+ var tab = document.getElementById(this.href.substr(this.href.indexOf('#') + 1));
+ if (tab)
+ {
+ //reset all tabs before change
+ for (var k = 0; k < this.tabs.length; k++)
+ {
+ document.getElementById(this.tabs[k].href.substr(this.tabs[k].href.indexOf('#') + 1)).style.display = 'none';
+ this.tabs[k].className = this.tabs[k].className.replace('active', 'disabled');
+ }
+ this.className = 'tab active';
+ tab.style.display = 'block';
+ // window.location.hash = this.id.replace('ID', '');
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ if(url.indexOf('#')!=-1) {
+ location.href=location.href;
+ }
+}
+if (window.addEventListener) window.addEventListener('load', initTabs, false);
+else if (window.attachEvent) window.attachEvent('onload', initTabs);
+-->
+</script>
+</head>
+<body><a name='Source'></a><a name='Binary'></a><a name='Top'></a><h1>API compatibility report for the <span style='color:Blue;'>libyang.so</span> object between <span style='color:Red;'>1.9.19</span> and <span style='color:Red;'>2.0.0</span> versions on <span style='color:Blue;'>x86_64</span></h1>
+
+ <br/>
+ <div class='tabset'>
+ <a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>
+ <a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>
+ </div><div id='BinaryTab' class='tab'>
+<h2>Test Info</h2><hr/>
+<table class='summary'>
+<tr><th>Module Name</th><td>libyang.so</td></tr>
+<tr><th>Version #1</th><td>1.9.19</td></tr>
+<tr><th>Version #2</th><td>2.0.0</td></tr>
+<tr><th>Arch</th><td>x86_64</td></tr>
+<tr><th>GCC Version</th><td>7.5.0</td></tr>
+<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>
+</table>
+<h2>Test Results</h2><hr/>
+<table class='summary'><tr><th>Total Header Files</th><td><a href='#Headers' style='color:Blue;'>7</a></td></tr>
+<tr><th>Total Source Files</th><td><a href='#Sources' style='color:Blue;'>11</a></td></tr>
+<tr><th>Total Objects</th><td><a href='#Libs' style='color:Blue;'>1</a></td></tr>
+<tr><th>Total Symbols / Types</th><td>174 / 22</td></tr>
+<tr><th>Compatibility</th>
+<td class='incompatible'>18.8%</td>
+</tr>
+</table>
+<h2>Problem Summary</h2><hr/>
+<table class='summary'><tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr><tr><th>Added Symbols</th><td>-</td><td class='new'><a href='#Binary_Added' style='color:Blue;'>153</a></td></tr>
+<tr><th>Removed Symbols</th><td>High</td><td class='failed'><a href='#Binary_Removed' style='color:Blue;'>114</a></td></tr>
+<tr><th rowspan='3'>Problems with<br/>Data Types</th><td>High</td><td class='failed'><a href='#Type_Binary_Problems_High' style='color:Blue;'>17</a></td></tr>
+<tr><td>Medium</td><td class='failed'><a href='#Type_Binary_Problems_Medium' style='color:Blue;'>37</a></td></tr>
+<tr><td>Low</td><td class='warning'><a href='#Type_Binary_Problems_Low' style='color:Blue;'>90</a></td></tr>
+<tr><th rowspan='3'>Problems with<br/>Symbols</th><td>High</td><td class='failed'><a href='#Symbol_Binary_Problems_High' style='color:Blue;'>18</a></td></tr>
+<tr><td>Medium</td><td class='failed'><a href='#Symbol_Binary_Problems_Medium' style='color:Blue;'>57</a></td></tr>
+<tr><td>Low</td><td class='warning'><a href='#Symbol_Binary_Problems_Low' style='color:Blue;'>57</a></td></tr>
+<tr><th>Problems with<br/>Constants</th><td>Low</td><td>0</td></tr>
+<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td class='passed'><a href='#Other_Binary_Changes_In_Types' style='color:Blue;'>7</a></td></tr>
+</table>
+
+<a name='Binary_Added'></a><h2>Added Symbols <span class='new'>&nbsp;153&nbsp;</span></h2><hr/>
+<span class='h_name'>context.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">ly_ctx_get_module_implemented&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_implemented_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_latest&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_latest_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>, <span>char const* <span class='color_p'>revision</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_yanglib_data&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_node** <span class='color_p'>root_p</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_yanglib_id&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_reset_latests&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>uint16_t <span class='color_p'>option</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>uint16_t <span class='color_p'>option</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_searchdir&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_searchdir_last&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>uint32_t <span class='color_p'>count</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>in.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">ly_in_fd&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>int <span class='color_p'>fd</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_file&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>FILE* <span class='color_p'>f</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_filepath&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>char const* <span class='color_p'>filepath</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_free&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>ly_bool <span class='color_p'>destroy</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_memory&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>char const* <span class='color_p'>str</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_filepath&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>filepath</span></span>, <span>size_t <span class='color_p'>len</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_memory&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>str</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_parsed&#160;<span class='sym_p'><span>(&#160;struct ly_in const* <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_reset&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_type&#160;<span class='sym_p'><span>(&#160;struct ly_in const* <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>log.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">ly_err_last&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_errcode&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_log_dbg_groups&#160;<span class='sym_p'><span>(&#160;uint32_t <span class='color_p'>dbg_groups</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_log_level&#160;<span class='sym_p'><span>(&#160;enum LY_LOG_LEVEL <span class='color_p'>level</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>out.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">ly_out_clb&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>ly_write_clb <span class='color_p'>writeclb</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_clb_arg&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>void* <span class='color_p'>arg</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_fd&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>int <span class='color_p'>fd</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_file&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>FILE* <span class='color_p'>f</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_filepath&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>char const* <span class='color_p'>filepath</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_free&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>void(*<span class='color_p'>clb_arg_destructor</span>)(void*)</span>, <span>ly_bool <span class='color_p'>destroy</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_filepath&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>filepath</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_memory&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>size_t <span class='color_p'>size</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_printed&#160;<span class='sym_p'><span>(&#160;struct ly_out const* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_reset&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_type&#160;<span class='sym_p'><span>(&#160;struct ly_out const* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_print&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>char const* <span class='color_p'>format</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">ly_print_flush&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_write&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>char const* <span class='color_p'>buf</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>parser_data.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">lyd_parse_data&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_data_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_data_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_data_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_notif&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lyd_node** <span class='color_p'>ntf</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_reply&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>request</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lyd_node** <span class='color_p'>op</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_rpc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lyd_node** <span class='color_p'>op</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t <span class='color_p'>val_opts</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>val_opts</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_op&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>op_tree</span></span>, <span>struct lyd_node const* <span class='color_p'>tree</span></span>, <span>enum LYD_VALIDATE_OP <span class='color_p'>op</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>parser_schema.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">lys_parse&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>char const** <span class='color_p'>features</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>plugins_types.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">ly_err_free&#160;<span class='sym_p'><span>(&#160;void* <span class='color_p'>ptr</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_err_new&#160;<span class='sym_p'><span>(&#160;enum LY_LOG_LEVEL <span class='color_p'>level</span></span>, <span>enum LY_ERR <span class='color_p'>no</span></span>, <span>enum LY_VECODE <span class='color_p'>vecode</span></span>, <span>char* <span class='color_p'>msg</span></span>, <span>char* <span class='color_p'>path</span></span>, <span>char* <span class='color_p'>apptag</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_identity_isderived&#160;<span class='sym_p'><span>(&#160;struct lysc_ident* <span class='color_p'>base</span></span>, <span>struct lysc_ident* <span class='color_p'>der</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_parse_dec64&#160;<span class='sym_p'><span>(&#160;uint8_t <span class='color_p'>fraction_digits</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>int64_t* <span class='color_p'>ret</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_parse_int&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>datatype</span></span>, <span>int <span class='color_p'>base</span></span>, <span>int64_t <span class='color_p'>min</span></span>, <span>int64_t <span class='color_p'>max</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>int64_t* <span class='color_p'>ret</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_parse_uint&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>datatype</span></span>, <span>int <span class='color_p'>base</span></span>, <span>uint64_t <span class='color_p'>max</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>uint64_t* <span class='color_p'>ret</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_print_get_prefix&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>mod</span></span>, <span>enum LY_PREFIX_FORMAT <span class='color_p'>format</span></span>, <span>void* <span class='color_p'>prefix_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_store_resolve_prefix&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>prefix</span></span>, <span>size_t <span class='color_p'>prefix_len</span></span>, <span>enum LY_PREFIX_FORMAT <span class='color_p'>format</span></span>, <span>void* <span class='color_p'>prefix_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_validate_patterns&#160;<span class='sym_p'><span>(&#160;struct lysc_pattern** <span class='color_p'>patterns</span></span>, <span>char const* <span class='color_p'>str</span></span>, <span>size_t <span class='color_p'>str_len</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_validate_range&#160;<span class='sym_p'><span>(&#160;enum LY_DATA_TYPE <span class='color_p'>basetype</span></span>, <span>struct lysc_range* <span class='color_p'>range</span></span>, <span>int64_t <span class='color_p'>value</span></span>, <span>char const* <span class='color_p'>strval</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_prefixes_compile&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>str</span></span>, <span>size_t <span class='color_p'>str_len</span></span>, <span>struct lysp_module const* <span class='color_p'>prefix_mod</span></span>, <span>struct lysc_prefix** <span class='color_p'>prefixes</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_prefixes_dup&#160;<span class='sym_p'><span>(&#160;struct lysc_prefix const* <span class='color_p'>orig</span></span>, <span>struct lysc_prefix** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_prefixes_free&#160;<span class='sym_p'><span>(&#160;struct lysc_prefix* <span class='color_p'>prefixes</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>printer_data.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">lyd_print_all&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_print_tree&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>printer_schema.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">lys_print_module&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>size_t <span class='color_p'>UNUSED_line_length</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_print_node&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lysc_node const* <span class='color_p'>node</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>size_t <span class='color_p'>UNUSED_line_length</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_print_submodule&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>struct lysp_submodule const* <span class='color_p'>submodule</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>size_t <span class='color_p'>UNUSED_line_length</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>set.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">ly_set_erase&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_data.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">lyd_any_copy_value&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>trg</span></span>, <span>union lyd_any_value const* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_change_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_meta* <span class='color_p'>meta</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_change_term&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>term</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_child&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_child_no_keys&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_compare_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_meta const* <span class='color_p'>meta1</span></span>, <span>struct lyd_meta const* <span class='color_p'>meta2</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_compare_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node1</span></span>, <span>struct lyd_node const* <span class='color_p'>node2</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_compare_single&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node1</span></span>, <span>struct lyd_node const* <span class='color_p'>node2</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_apply_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>data</span></span>, <span>struct lyd_node const* <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_apply_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>data</span></span>, <span>struct lyd_node const* <span class='color_p'>diff</span></span>, <span>struct lys_module const* <span class='color_p'>mod</span></span>, <span>lyd_diff_cb <span class='color_p'>diff_cb</span></span>, <span>void* <span class='color_p'>cb_data</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_merge_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>diff</span></span>, <span>struct lyd_node const* <span class='color_p'>src_diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_merge_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>diff</span></span>, <span>struct lyd_node const* <span class='color_p'>src_diff</span></span>, <span>struct lys_module const* <span class='color_p'>mod</span></span>, <span>lyd_diff_cb <span class='color_p'>diff_cb</span></span>, <span>void* <span class='color_p'>cb_data</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_merge_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>diff_first</span></span>, <span>struct lyd_node* <span class='color_p'>diff_parent</span></span>, <span>struct lyd_node const* <span class='color_p'>src_sibling</span></span>, <span>lyd_diff_cb <span class='color_p'>diff_cb</span></span>, <span>void* <span class='color_p'>cb_data</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_reverse_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>src_diff</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>first</span></span>, <span>struct lyd_node const* <span class='color_p'>second</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>first</span></span>, <span>struct lyd_node const* <span class='color_p'>second</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_meta_single&#160;<span class='sym_p'><span>(&#160;struct lyd_meta const* <span class='color_p'>meta</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>, <span>struct lyd_meta** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>struct lyd_node_inner* <span class='color_p'>parent</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_single&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>struct lyd_node_inner* <span class='color_p'>parent</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_meta const* <span class='color_p'>first</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_sibling_first&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lyd_node const* <span class='color_p'>target</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_xpath&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>xpath</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_attr_siblings&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_attr* <span class='color_p'>attr</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_attr_single&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_attr* <span class='color_p'>attr</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_meta_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_meta* <span class='color_p'>meta</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_meta_single&#160;<span class='sym_p'><span>(&#160;struct lyd_meta* <span class='color_p'>meta</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_insert_child&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_is_default&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>target</span></span>, <span>struct lyd_node const* <span class='color_p'>source</span></span>, <span>uint16_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>target</span></span>, <span>struct lyd_node const* <span class='color_p'>source</span></span>, <span>uint16_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_any&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>void const* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_attr&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>char const* <span class='color_p'>module_name</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>, <span>struct lyd_attr** <span class='color_p'>attr</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_implicit_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t <span class='color_p'>implicit_options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_implicit_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>implicit_options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_implicit_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>tree</span></span>, <span>uint32_t <span class='color_p'>implicit_options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_inner&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_list&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_list2&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>keys</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>, <span>struct lyd_meta** <span class='color_p'>meta</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_opaq&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>char const* <span class='color_p'>module_name</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_path2&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void const* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>new_parent</span></span>, <span>struct lyd_node** <span class='color_p'>new_node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_term&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_owner_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parent&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_target&#160;<span class='sym_p'><span>(&#160;struct ly_path const* <span class='color_p'>path</span></span>, <span>struct lyd_node const* <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_unlink_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_value_compare&#160;<span class='sym_p'><span>(&#160;struct lyd_node_term const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_value_validate&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_node_term const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>struct lyd_node const* <span class='color_p'>tree</span></span>, <span>struct lysc_type const** <span class='color_p'>realtype</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_schema.h</span>, <span class='lib_name'>libyang.so.2.0.0</span><br/>
+<span class="iname">lyext_parent2str&#160;<span class='sym_p'><span>(&#160;enum LYEXT_PARENT <span class='color_p'>type</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_feature_value&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_child&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>size_t <span class='color_p'>name_len</span></span>, <span>uint16_t <span class='color_p'>nodetype</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_expr_atoms&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>struct lys_module const* <span class='color_p'>cur_mod</span></span>, <span>struct lyxp_expr const* <span class='color_p'>expr</span></span>, <span>struct lysc_prefix const* <span class='color_p'>prefixes</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_lypath_atoms&#160;<span class='sym_p'><span>(&#160;struct ly_path const* <span class='color_p'>path</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_path_atoms&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_xpath&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>xpath</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_xpath_atoms&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>xpath</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_nodetype2str&#160;<span class='sym_p'><span>(&#160;uint16_t <span class='color_p'>nodetype</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_value_validate&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_iffeature_value&#160;<span class='sym_p'><span>(&#160;struct lysc_iffeature const* <span class='color_p'>iff</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_module_dfs_full&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>mod</span></span>, <span>lysc_dfs_clb <span class='color_p'>dfs_clb</span></span>, <span>void* <span class='color_p'>data</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_actions&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_children&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>uint16_t <span class='color_p'>flags</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_children_full&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>uint16_t <span class='color_p'>flags</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_notifs&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_parent_full&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_path&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>enum LYSC_PATH_TYPE <span class='color_p'>pathtype</span></span>, <span>char* <span class='color_p'>buffer</span></span>, <span>size_t <span class='color_p'>buflen</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_set_private&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>void* <span class='color_p'>priv</span></span>, <span>void** <span class='color_p'>prev_priv_p</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_tree_dfs_full&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>root</span></span>, <span>lysc_dfs_clb <span class='color_p'>dfs_clb</span></span>, <span>void* <span class='color_p'>data</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_feature_next&#160;<span class='sym_p'><span>(&#160;struct lysp_feature const* <span class='color_p'>last</span></span>, <span>struct lysp_module const* <span class='color_p'>pmod</span></span>, <span>uint32_t* <span class='color_p'>idx</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_actions&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_children&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_groupings&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_notifs&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_typedefs&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxp_get_expr&#160;<span class='sym_p'><span>(&#160;struct lyxp_expr const* <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Binary_Removed'></a><a name='Binary_Withdrawn'></a><h2>Removed Symbols <span class='failed'>&nbsp;114&nbsp;</span></h2><hr/>
+<span class='h_name'>extensions.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="iname">lyext_vlog&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>enum LY_VECODE <span class='color_p'>vecode</span></span>, <span>char const* <span class='color_p'>plugin</span></span>, <span>char const* <span class='color_p'>function</span></span>, <span>enum LYEXT_VLOG_ELEM <span class='color_p'>elem_type</span></span>, <span>void const* <span class='color_p'>elem</span></span>, <span>char const* <span class='color_p'>format</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lys_iffeature_free&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lys_iffeature* <span class='color_p'>iffeature</span></span>, <span>uint8_t <span class='color_p'>iffeature_size</span></span>, <span>int <span class='color_p'>shallow</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>libyang.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="iname">ly_ctx_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_find_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_disabled_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t* <span class='color_p'>idx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_by_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>int <span class='color_p'>implemented</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_data_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>void** <span class='color_p'>user_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_older&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_node&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lys_node const* <span class='color_p'>start</span></span>, <span>char const* <span class='color_p'>nodeid</span></span>, <span>int <span class='color_p'>output</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_submodule&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char const* <span class='color_p'>submodule</span></span>, <span>char const* <span class='color_p'>sub_revision</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_submodule2&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>main_module</span></span>, <span>char const* <span class='color_p'>submodule</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_info&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_new_ylmem&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_new_ylpath&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_remove_module&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_allimplemented&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_disable_searchdir_cwd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_disable_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_module_data_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>ly_module_data_clb <span class='color_p'>clb</span></span>, <span>void* <span class='color_p'>user_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_prefer_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_trusted&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_allimplemented&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_disable_searchdir_cwd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_disable_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_prefer_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>index</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_trusted&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_errno_glob_address&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_path_data2schema&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data_path</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_path_xml2json&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>xml_path</span></span>, <span>struct lyxml_elem* <span class='color_p'>xml</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_verb&#160;<span class='sym_p'><span>(&#160;enum LY_LOG_LEVEL <span class='color_p'>level</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_verb_dbg&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>dbg_groups</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_data.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="iname">lyd_change_leaf&#160;<span class='sym_p'><span>(&#160;struct lyd_node_leaf_list* <span class='color_p'>leaf</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dec64_to_double&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>first</span></span>, <span>struct lyd_node* <span class='color_p'>second</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_to_ctx&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>, <span>struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_withsiblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_instance&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>data</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lyd_node const* <span class='color_p'>target</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_sibling_set&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lyd_node const* <span class='color_p'>target</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_attr&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lyd_attr* <span class='color_p'>attr</span></span>, <span>int <span class='color_p'>recursive</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_diff&#160;<span class='sym_p'><span>(&#160;struct lyd_difflist* <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_val_diff&#160;<span class='sym_p'><span>(&#160;struct lyd_difflist* <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_withsiblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_insert&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_insert_attr&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>mod</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_leaf_type&#160;<span class='sym_p'><span>(&#160;struct lyd_node_leaf_list const* <span class='color_p'>leaf</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>target</span></span>, <span>struct lyd_node const* <span class='color_p'>source</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge_to_ctx&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>trg</span></span>, <span>struct lyd_node const* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>, <span>struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_anydata&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_leaf&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_output&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_output_anydata&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_output_leaf&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_yangdata&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name_template</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_node_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_node_should_print&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_xml&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem** <span class='color_p'>root</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_schema_sort&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>int <span class='color_p'>recursive</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_unlink&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>, <span>void* <span class='color_p'>var_arg</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_modules&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>node</span></span>, <span>struct lys_module const** <span class='color_p'>modules</span></span>, <span>int <span class='color_p'>mod_count</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_value&#160;<span class='sym_p'><span>(&#160;struct lys_node* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_value_type&#160;<span class='sym_p'><span>(&#160;struct lys_node* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>struct lys_type** <span class='color_p'>type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_wd_default&#160;<span class='sym_p'><span>(&#160;struct lyd_node_leaf_list* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_schema.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="iname">ly_clean_plugins&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_get_loaded_plugins&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_load_plugins&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_register_exts&#160;<span class='sym_p'><span>(&#160;struct lyext_plugin_list* <span class='color_p'>plugin</span></span>, <span>char const* <span class='color_p'>log_name</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_register_types&#160;<span class='sym_p'><span>(&#160;struct lytype_plugin_list* <span class='color_p'>plugin</span></span>, <span>char const* <span class='color_p'>log_name</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_data_path&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_data_path_pattern&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>placeholder</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_ext_complex_get_substmt&#160;<span class='sym_p'><span>(&#160;enum LY_STMT <span class='color_p'>stmt</span></span>, <span>struct lys_ext_instance_complex* <span class='color_p'>ext</span></span>, <span>struct lyext_substmt** <span class='color_p'>info</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_ext_instance_presence&#160;<span class='sym_p'><span>(&#160;struct lys_ext* <span class='color_p'>def</span></span>, <span>struct lys_ext_instance** <span class='color_p'>ext</span></span>, <span>uint8_t <span class='color_p'>ext_size</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_ext_instance_substmt&#160;<span class='sym_p'><span>(&#160;struct lys_ext_instance const* <span class='color_p'>ext</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_disable&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_disable_force&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_enable&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_enable_force&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_list&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>uint8_t** <span class='color_p'>states</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_state&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_getnext_union_type&#160;<span class='sym_p'><span>(&#160;struct lys_type const* <span class='color_p'>last</span></span>, <span>struct lys_type const* <span class='color_p'>type</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_iffeature_value&#160;<span class='sym_p'><span>(&#160;struct lys_iffeature const* <span class='color_p'>iff</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_implemented_module&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>mod</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_is_disabled&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>recursive</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_is_key&#160;<span class='sym_p'><span>(&#160;struct lys_node_leaf const* <span class='color_p'>node</span></span>, <span>uint8_t* <span class='color_p'>index</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_main_module&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_node_module&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_node_xpath_atomize&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_parent&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_path&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_set_disabled&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_set_enabled&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_set_private&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>void* <span class='color_p'>priv</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_xpath_atomize&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>ctx_node</span></span>, <span>enum lyxp_node_type <span class='color_p'>ctx_node_type</span></span>, <span>char const* <span class='color_p'>expr</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>xml.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="iname">lyxml_dup&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>root</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_free&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>elem</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_free_withsiblings&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>elem</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_get_attr&#160;<span class='sym_p'><span>(&#160;struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>ns</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_get_ns&#160;<span class='sym_p'><span>(&#160;struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>char const* <span class='color_p'>prefix</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>filename</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>stream</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_unlink&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>elem</span></span>&#160;)</span></span><br/>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='High_Risk_Binary_Problems'></a><a name='Type_Binary_Problems_High'></a>
+<h2>Problems with Data Types, High Severity <span class='failed'>&nbsp;17&nbsp;</span></h2><hr/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_1')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lys_module <span class='failed'>&nbsp;17&nbsp;</span></span>
+<br/>
+<div id="c_1" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>augment</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>data</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>deviated</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Field <b>deviation</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>disabled</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Field <b>ext</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>Field <b>ext_size</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>Field <b>extensions</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>Field <b>features</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>10</th>
+<td>Field <b>ident</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>11</th>
+<td>Field <b>ident_size</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>12</th>
+<td>Field <b>imp</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>13</th>
+<td>Field <b>inc</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>14</th>
+<td>Field <b>rev</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>15</th>
+<td>Field <b>tpdf</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>16</th>
+<td>Field <b>type</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>17</th>
+<td>Field <b>version</b> has been removed from the middle position of this structural type.</td>
+<td>1) Previous accesses of applications to the removed field will be incorrect.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_2')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_2" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>int <i>implemented</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>uint32_t* <i>idx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Symbol_Binary_Problems_High'></a><a name='Interface_Binary_Problems_High'></a>
+<h2>Problems with Symbols, High Severity <span class='failed'>&nbsp;18&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_3')">
+<span class='ext'>[+]</span> ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_3" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct ly_ctx** <span class='color_p'>new_ctx</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>options</b> has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <span class='nowrap'><b>uint16_t</b> (<b>2</b> bytes)</span>.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_4')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_4" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>list</b> of type <b>ly_bool</b> has been added to the calling stack at the middle position.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_5')">
+<span class='ext'>[+]</span> ly_set_log_clb&#160;<span class='sym_p'><span>(&#160;void(*<span class='color_p'>clb</span>)(LY_LOG_LEVEL, char const*, char const*)</span>, <span>int <span class='color_p'>path</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_5" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_log_clb&#160;<span class='sym_p'><span>(&#160;ly_log_clb <span class='color_p'>clb</span></span>, <span>ly_bool <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>path</b> has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <span class='nowrap'><b>ly_bool</b> (<b>1</b> byte)</span>.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_6')">
+<span class='ext'>[+]</span> ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_6" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>void*(*<span class='color_p'>duplicator</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>list</b> of type <b>ly_bool</b> has been added to the calling stack at the middle position.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_7')">
+<span class='ext'>[+]</span> lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_7" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lysc_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>size_t <span class='color_p'>val_len</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>val_len</b> of type <b>size_t</b> has been added to the calling stack at the middle position.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_8')">
+<span class='ext'>[+]</span> lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>data_tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_8" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>The pointer level of <b>6th</b> parameter <b>options</b> has been increased from <b>0</b> to <b>2</b>.</td>
+<td>The library function may try to access unallocated memory by the dereferencing of old parameter value and therefore cause a crash of applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of <b>6th</b> parameter <b>options</b> has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lyd_node**</span> (<b>8</b> bytes)</span>.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_9')">
+<span class='ext'>[+]</span> lys_find_path&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>cur_module</span></span>, <span>struct lys_node const* <span class='color_p'>cur_node</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span> <span class='failed'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_9" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_find_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>ly_bool <span class='color_p'>output</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>ctx</b> of type <span class='value'>struct ly_ctx const*</span> has been added to the calling stack at the middle position.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Parameter <b>ctx_node</b> of type <span class='value'>struct lysc_node const*</span> has been added to the calling stack at the middle position.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+<tr>
+<th>3</th>
+<td><b>1st</b> middle parameter <b>cur_module</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+<tr>
+<th>4</th>
+<td><b>2nd</b> middle parameter <b>cur_node</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_10')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_10" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_11')">
+<span class='ext'>[+]</span> lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_11" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_12')">
+<span class='ext'>[+]</span> lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_12" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_13')">
+<span class='ext'>[+]</span> lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_13" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_14')">
+<span class='ext'>[+]</span> lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_14" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_15')">
+<span class='ext'>[+]</span> lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <span class='color_p'>searchpaths</span></span>, <span>int <span class='color_p'>cwd</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char** <span class='color_p'>localfile</span></span>, <span>LYS_INFORMAT* <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_15" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <span class='color_p'>searchpaths</span></span>, <span>ly_bool <span class='color_p'>cwd</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char** <span class='color_p'>localfile</span></span>, <span>LYS_INFORMAT* <span class='color_p'>format</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>cwd</b> has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <span class='nowrap'><b>ly_bool</b> (<b>1</b> byte)</span>.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_16')">
+<span class='ext'>[+]</span> lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_16" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module* <span class='color_p'>mod</span></span>, <span>char const** <span class='color_p'>features</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>mod</b> of type <span class='value'>struct lys_module*</span> has been added to the calling stack at the middle position.</td>
+<td>Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Medium_Risk_Binary_Problems'></a><a name='Type_Binary_Problems_Medium'></a>
+<h2>Problems with Data Types, Medium Severity <span class='failed'>&nbsp;37&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_17')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LY_ERR <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_17" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Value of member <b>LY_EINT</b> has been changed from <b>4</b> to <b>6</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Value of member <b>LY_EPLUGIN</b> has been changed from <b>6</b> to <b>128</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Value of member <b>LY_EVALID</b> has been changed from <b>5</b> to <b>7</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_18')">
+[+] affected symbols: 3 (1.7%)</span>
+<div id="c_18" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_err_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.no&#39; in 2nd parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+<span class='iname_a'>ly_err_first&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;retval.no&#39; in the return value (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+<span class='iname_a'>ly_err_print&#160;<span class='sym_p'><span>(&#160;struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.no&#39; in 1st parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_19')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lyd_node <span class='failed'>&nbsp;11&nbsp;</span></span>
+<br/>
+<div id="c_19" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The relative position of field <b>hash</b> has been changed from <b>4</b> to <b>0</b>.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The relative position of field <b>prev</b> has been changed from <b>2</b> to <b>4</b>.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>attr</b> has been removed from this type.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Field <b>child</b> has been removed from this type.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>ht</b> has been removed from this type.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Field <b>validity</b> has been removed from this type.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>The relative position of field <b>next</b> has been changed from <b>1</b> to <b>3</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>The relative position of field <b>parent</b> has been changed from <b>3</b> to <b>2</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>The relative position of field <b>schema</b> has been changed from <b>0</b> to <b>1</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>10</th>
+<td>Field <b>dflt</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>11</th>
+<td>Field <b>when_status</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_20')">
+[+] affected symbols: 9 (5.2%)</span>
+<div id="c_20" style="display:none;">
+<div class='affected'><span class='iname_a'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>siblings</span></span>, <span>struct lys_node const* <i>schema</i></span>, <span>char const* <i>key_or_value</i></span>, <span>struct lyd_node** <i>match</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;siblings&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_21')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_INFORMAT <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_21" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Value of member <b>LYS_IN_YIN</b> has been changed from <b>2</b> to <b>3</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_22')">
+[+] affected symbols: 4 (2.3%)</span>
+<div id="c_22" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>int <i>fd</i></span>, <span>enum LYS_INFORMAT <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_INFORMAT&#39;.</div>
+<span class='iname_a'>lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>data</i></span>, <span>enum LYS_INFORMAT <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_INFORMAT&#39;.</div>
+<span class='iname_a'>lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>path</i></span>, <span>enum LYS_INFORMAT <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_INFORMAT&#39;.</div>
+<span class='iname_a'>lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <i>searchpaths</i></span>, <span>int <i>cwd</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>char** <i>localfile</i></span>, <span>LYS_INFORMAT* <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>6th parameter &#39;format&#39; (pointer) has base type &#39;enum LYS_INFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_23')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_OUTFORMAT <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_23" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Value of member <b>LYS_OUT_TREE</b> has been changed from <b>3</b> to <b>4</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Value of member <b>LYS_OUT_YIN</b> has been changed from <b>2</b> to <b>3</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_24')">
+[+] affected symbols: 5 (2.9%)</span>
+<div id="c_24" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>4th parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_25')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lys_module <span class='failed'>&nbsp;20&nbsp;</span></span>
+<br/>
+<div id="c_25" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>augmented_by</b> has been added at the middle position of this structural type.</td>
+<td>1) Size of the inclusive type has been changed.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>compiled</b> has been added at the middle position of this structural type.</td>
+<td>1) Size of the inclusive type has been changed.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>deviated_by</b> has been added at the middle position of this structural type.</td>
+<td>1) Size of the inclusive type has been changed.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Field <b>identities</b> has been added at the middle position of this structural type.</td>
+<td>1) Size of the inclusive type has been changed.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>parsed</b> has been added at the middle position of this structural type.</td>
+<td>1) Size of the inclusive type has been changed.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Field <b>revision</b> has been added at the middle position of this structural type.</td>
+<td>1) Size of the inclusive type has been changed.<br/>2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>The relative position of field <b>latest_revision</b> has been changed from <b>9</b> to <b>10</b>.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>The relative position of field <b>ref</b> has been changed from <b>4</b> to <b>8</b>.</td>
+<td>1) Applications will access incorrect memory when attempting to access this field.<br/>2) Size of the inclusive type has been changed.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>The relative position of field <b>dsc</b> has been changed from <b>3</b> to <b>7</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>10</th>
+<td>The relative position of field <b>filepath</b> has been changed from <b>7</b> to <b>4</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>11</th>
+<td>The relative position of field <b>ns</b> has been changed from <b>10</b> to <b>2</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>12</th>
+<td>The relative position of field <b>prefix</b> has been changed from <b>2</b> to <b>3</b>.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>13</th>
+<td>Field <b>augment_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>14</th>
+<td>Field <b>deviation_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>15</th>
+<td>Field <b>extensions_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>16</th>
+<td>Field <b>features_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>17</th>
+<td>Field <b>imp_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>18</th>
+<td>Field <b>inc_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>19</th>
+<td>Field <b>rev_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+<tr>
+<th>20</th>
+<td>Field <b>tpdf_size</b> has been removed from this type.</td>
+<td>Applications will access incorrect memory when attempting to access this field.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_26')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_26" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>int <i>implemented</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>uint32_t* <i>idx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Symbol_Binary_Problems_Medium'></a><a name='Interface_Binary_Problems_Medium'></a>
+<h2>Problems with Symbols, Medium Severity <span class='failed'>&nbsp;57&nbsp;</span></h2><hr/>
+<span class='h_name'>dict.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_27')">
+<span class='ext'>[+]</span> lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_27" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>, <span>char const** <span class='color_p'>str_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>str_p</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>char const*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_28')">
+<span class='ext'>[+]</span> lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_28" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>, <span>char const** <span class='color_p'>str_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>str_p</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>char const*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>libyang.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_29')">
+<span class='ext'>[+]</span> ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>int <span class='color_p'>implemented</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_29" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> parameter <b>implemented</b> has been removed from the calling stack.</td>
+<td>This parameter will be ignored by the function.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_30')">
+<span class='ext'>[+]</span> ly_ctx_get_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_30" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <span class='nowrap'><b>uint16_t</b> (<b>2</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_31')">
+<span class='ext'>[+]</span> ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_31" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char const** <span class='color_p'>features</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>features</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_32')">
+<span class='ext'>[+]</span> ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_32" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct ly_ctx** <span class='color_p'>new_ctx</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>new_ctx</b> of type <span class='value'>struct ly_ctx**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct ly_ctx*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_33')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_33" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>index_p</b> of type <b>uint32_t*</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_34')">
+<span class='ext'>[+]</span> ly_set_clean&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_34" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_clean&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <b>void</b>.</td>
+<td>Applications will not obtain a return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_35')">
+<span class='ext'>[+]</span> ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_35" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>index_p</b> of type <b>uint32_t*</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <span class='nowrap'><b>ly_bool</b> (<b>1</b> byte)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_36')">
+<span class='ext'>[+]</span> ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>&#160;)</span> <span class='failed'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_36" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void*(*<span class='color_p'>duplicator</span>)(void*)</span>, <span>struct ly_set** <span class='color_p'>newset_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>duplicator</b> of type <b>void*(*)(void*)</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Parameter <b>newset_p</b> of type <span class='value'>struct ly_set**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct ly_set*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_37')">
+<span class='ext'>[+]</span> ly_set_free&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_37" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_free&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_38')">
+<span class='ext'>[+]</span> ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_38" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>void*(*<span class='color_p'>duplicator</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>duplicator</b> of type <b>void*(*)(void*)</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_39')">
+<span class='ext'>[+]</span> ly_set_new&#160;<span class='sym_p'>(&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_39" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_new&#160;<span class='sym_p'><span>(&#160;struct ly_set** <span class='color_p'>set_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>set_p</b> of type <span class='value'>struct ly_set**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct ly_set*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_40')">
+<span class='ext'>[+]</span> ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_40" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_41')">
+<span class='ext'>[+]</span> ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>unsigned int <span class='color_p'>index</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_41" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>uint32_t <span class='color_p'>index</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_42')">
+<span class='ext'>[+]</span> lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_42" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lysc_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>size_t <span class='color_p'>val_len</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Base type of <b>2nd</b> parameter <b>schema</b> has been changed from <span class='nowrap'><span class='value'>struct lys_node</span> (<b>112</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_node</span> (<b>88</b> bytes)</span>.</td>
+<td>This parameter may be incorrectly initialized by applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_43')">
+<span class='ext'>[+]</span> lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_43" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>, <span>struct lyd_node** <span class='color_p'>first</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>first</b> of type <span class='value'>struct lyd_node**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of <b>1st</b> parameter <b>sibling</b> has been decreased from <b>2</b> to <b>1</b>.</td>
+<td>The library function will treat the parameter as the lower-dimension array and will not read all elements. This may change the behavior of applications.<br/><br/><b>NOTE</b>: if this is <b>out</b>-parameter then this change may cause a crash of applications.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_44')">
+<span class='ext'>[+]</span> lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>data_tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_44" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct lyd_node*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_45')">
+<span class='ext'>[+]</span> lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_45" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>enum LYD_PATH_TYPE <span class='color_p'>pathtype</span></span>, <span>char* <span class='color_p'>buffer</span></span>, <span>size_t <span class='color_p'>buflen</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>pathtype</b> of type <span class='value'>enum LYD_PATH_TYPE</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Parameter <b>buffer</b> of type <b>char*</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Parameter <b>buflen</b> of type <b>size_t</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_46')">
+<span class='ext'>[+]</span> lys_find_path&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>cur_module</span></span>, <span>struct lys_node const* <span class='color_p'>cur_node</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_46" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_find_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>ly_bool <span class='color_p'>output</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>output</b> of type <b>ly_bool</b> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Base type of return value has been changed from <span class='nowrap'><span class='value'>struct ly_set</span> (<b>16</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_node</span> (<b>88</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_47')">
+<span class='ext'>[+]</span> lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>last</span></span>, <span>struct lys_node const* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_47" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>last</span></span>, <span>struct lysc_node const* <span class='color_p'>parent</span></span>, <span>struct lysc_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Base type of <b>1st</b> parameter <b>last</b> has been changed from <span class='nowrap'><span class='value'>struct lys_node</span> (<b>112</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_node</span> (<b>88</b> bytes)</span>.</td>
+<td>This parameter may be incorrectly initialized by applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Base type of <b>3rd</b> parameter <b>module</b> has been changed from <span class='nowrap'><span class='value'>struct lys_module</span> (<b>184</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_module</span> (<b>40</b> bytes)</span>.</td>
+<td>This parameter may be incorrectly initialized by applications.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Base type of <b>2nd</b> parameter <b>parent</b> has been changed from <span class='nowrap'><span class='value'>struct lys_node</span> (<b>112</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_node</span> (<b>88</b> bytes)</span>.</td>
+<td>This parameter may be incorrectly initialized by applications.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Base type of return value has been changed from <span class='nowrap'><span class='value'>struct lys_node</span> (<b>112</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_node</span> (<b>88</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_48')">
+<span class='ext'>[+]</span> lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_48" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>module</b> of type <span class='value'>struct lys_module const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct lys_module const*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_49')">
+<span class='ext'>[+]</span> lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_49" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>module</b> of type <span class='value'>struct lys_module const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct lys_module const*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_50')">
+<span class='ext'>[+]</span> lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_50" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>module</b> of type <span class='value'>struct lys_module const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been decreased from <b>1</b> to <b>0</b>.</td>
+<td>Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <span class='nowrap'><span class='value'>struct lys_module const*</span> (<b>8</b> bytes)</span> to <span class='nowrap'><span class='value'>enum LY_ERR</span> (<b>4</b> bytes)</span>.</td>
+<td>Applications will obtain a different return value and execution may change.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_51')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_51" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>6th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>This parameter will be ignored by the function.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_52')">
+<span class='ext'>[+]</span> lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_52" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>This parameter will be ignored by the function.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_53')">
+<span class='ext'>[+]</span> lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_53" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>This parameter will be ignored by the function.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_54')">
+<span class='ext'>[+]</span> lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_54" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>This parameter will be ignored by the function.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_55')">
+<span class='ext'>[+]</span> lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_55" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>This parameter will be ignored by the function.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_56')">
+<span class='ext'>[+]</span> lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_56" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module* <span class='color_p'>mod</span></span>, <span>char const** <span class='color_p'>features</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>features</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>This parameter will not be initialized by old clients.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Low_Risk_Binary_Problems'></a><a name='Type_Binary_Problems_Low'></a>
+<h2>Problems with Data Types, Low Severity <span class='warning'>&nbsp;90&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_57')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LY_VECODE <span class='warning'>&nbsp;77&nbsp;</span></span>
+<br/>
+<div id="c_57" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Name of member with value <b>1</b> has been changed from <b>LYVE_XML_MISS</b> to <b>LYVE_SYNTAX</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Name of member with value <b>10</b> has been changed from <b>LYVE_MISSSTMT</b> to <b>LYVE_OTHER</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Name of member with value <b>2</b> has been changed from <b>LYVE_XML_INVAL</b> to <b>LYVE_SYNTAX_YANG</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Name of member with value <b>3</b> has been changed from <b>LYVE_XML_INCHAR</b> to <b>LYVE_SYNTAX_YIN</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Name of member with value <b>4</b> has been changed from <b>LYVE_EOF</b> to <b>LYVE_REFERENCE</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Name of member with value <b>5</b> has been changed from <b>LYVE_INSTMT</b> to <b>LYVE_XPATH</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>Name of member with value <b>6</b> has been changed from <b>LYVE_INPAR</b> to <b>LYVE_SEMANTICS</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>Name of member with value <b>7</b> has been changed from <b>LYVE_INID</b> to <b>LYVE_SYNTAX_XML</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>Name of member with value <b>8</b> has been changed from <b>LYVE_INDATE</b> to <b>LYVE_SYNTAX_JSON</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>10</th>
+<td>Name of member with value <b>9</b> has been changed from <b>LYVE_INARG</b> to <b>LYVE_DATA</b>.</td>
+<td>Applications may execute a wrong branch of code in the library and therefore change the behavior.</td>
+</tr>
+<tr>
+<th>11</th>
+<td>The member <b>LYVE_BITS_INNAME</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>12</th>
+<td>The member <b>LYVE_BITS_INVAL</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>13</th>
+<td>The member <b>LYVE_CIRC_FEATURES</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>14</th>
+<td>The member <b>LYVE_CIRC_IMPORTS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>15</th>
+<td>The member <b>LYVE_CIRC_INCLUDES</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>16</th>
+<td>The member <b>LYVE_CIRC_LEAFREFS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>17</th>
+<td>The member <b>LYVE_DUPID</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>18</th>
+<td>The member <b>LYVE_DUPLEAFLIST</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>19</th>
+<td>The member <b>LYVE_DUPLIST</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>20</th>
+<td>The member <b>LYVE_ENUM_INNAME</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>21</th>
+<td>The member <b>LYVE_ENUM_INVAL</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>22</th>
+<td>The member <b>LYVE_ENUM_WS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>23</th>
+<td>The member <b>LYVE_INATTR</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>24</th>
+<td>The member <b>LYVE_INCHAR</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>25</th>
+<td>The member <b>LYVE_INELEM</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>26</th>
+<td>The member <b>LYVE_INMETA</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>27</th>
+<td>The member <b>LYVE_INMOD</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>28</th>
+<td>The member <b>LYVE_INORDER</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>29</th>
+<td>The member <b>LYVE_INPRED</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>30</th>
+<td>The member <b>LYVE_INREGEX</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>31</th>
+<td>The member <b>LYVE_INRESOLV</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>32</th>
+<td>The member <b>LYVE_INSTATUS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>33</th>
+<td>The member <b>LYVE_INVAL</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>34</th>
+<td>The member <b>LYVE_INVER</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>35</th>
+<td>The member <b>LYVE_INWHEN</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>36</th>
+<td>The member <b>LYVE_KEY_CONFIG</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>37</th>
+<td>The member <b>LYVE_KEY_DUP</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>38</th>
+<td>The member <b>LYVE_KEY_MISS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>39</th>
+<td>The member <b>LYVE_KEY_NLEAF</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>40</th>
+<td>The member <b>LYVE_KEY_TYPE</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>41</th>
+<td>The member <b>LYVE_MCASEDATA</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>42</th>
+<td>The member <b>LYVE_MISSARG</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>43</th>
+<td>The member <b>LYVE_MISSATTR</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>44</th>
+<td>The member <b>LYVE_MISSELEM</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>45</th>
+<td>The member <b>LYVE_NOCONSTR</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>46</th>
+<td>The member <b>LYVE_NOLEAFREF</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>47</th>
+<td>The member <b>LYVE_NOMANDCHOICE</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>48</th>
+<td>The member <b>LYVE_NOMAX</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>49</th>
+<td>The member <b>LYVE_NOMIN</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>50</th>
+<td>The member <b>LYVE_NOMUST</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>51</th>
+<td>The member <b>LYVE_NOREQINS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>52</th>
+<td>The member <b>LYVE_NORESOLV</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>53</th>
+<td>The member <b>LYVE_NOUNIQ</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>54</th>
+<td>The member <b>LYVE_NOWHEN</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>55</th>
+<td>The member <b>LYVE_OBSDATA</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>56</th>
+<td>The member <b>LYVE_PATH_EXISTS</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>57</th>
+<td>The member <b>LYVE_PATH_INCHAR</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>58</th>
+<td>The member <b>LYVE_PATH_INIDENTREF</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>59</th>
+<td>The member <b>LYVE_PATH_INKEY</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>60</th>
+<td>The member <b>LYVE_PATH_INMOD</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>61</th>
+<td>The member <b>LYVE_PATH_INNODE</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>62</th>
+<td>The member <b>LYVE_PATH_MISSKEY</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>63</th>
+<td>The member <b>LYVE_PATH_MISSMOD</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>64</th>
+<td>The member <b>LYVE_PATH_MISSPAR</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>65</th>
+<td>The member <b>LYVE_PATH_PREDTOOMANY</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>66</th>
+<td>The member <b>LYVE_SUBMODULE</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>67</th>
+<td>The member <b>LYVE_TOOMANY</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>68</th>
+<td>The member <b>LYVE_XPATH_DUMMY</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>69</th>
+<td>The member <b>LYVE_XPATH_EOF</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>70</th>
+<td>The member <b>LYVE_XPATH_INARGCOUNT</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>71</th>
+<td>The member <b>LYVE_XPATH_INARGTYPE</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>72</th>
+<td>The member <b>LYVE_XPATH_INCTX</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>73</th>
+<td>The member <b>LYVE_XPATH_INFUNC</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>74</th>
+<td>The member <b>LYVE_XPATH_INMOD</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>75</th>
+<td>The member <b>LYVE_XPATH_INOP</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>76</th>
+<td>The member <b>LYVE_XPATH_INTOK</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>77</th>
+<td>The member <b>LYVE_XPATH_NOEND</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_58')">
+[+] affected symbols: 4 (2.3%)</span>
+<div id="c_58" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_err_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.vecode&#39; in 2nd parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_VECODE&#39;.</div>
+<span class='iname_a'>ly_err_first&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;retval.vecode&#39; in the return value (pointer) is of type &#39;enum LY_VECODE&#39;.</div>
+<span class='iname_a'>ly_err_print&#160;<span class='sym_p'><span>(&#160;struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.vecode&#39; in 1st parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_VECODE&#39;.</div>
+<span class='iname_a'>ly_vecode&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value is of type &#39;enum LY_VECODE&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_59')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> ly_set <span class='warning'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_59" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Type of field <b>size</b> has been changed from <span class='value'>unsigned int</span> to <b>uint32_t</b>.</td>
+<td>Replacement of the field data type may indicate a change in the semantic meaning of the field.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>number</b> has been renamed to <b>count</b>.</td>
+<td>Renaming of a field in data type may indicate a change in the semantic meaning of the field.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>set</b> has been renamed to <b>unnamed0</b>.</td>
+<td>Renaming of a field in data type may indicate a change in the semantic meaning of the field.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_60')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_60" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>, <span>void* <i>node</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_clean&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='fp'>set</span></span>, <span>void* <i>node</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='fp'>set</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_free&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <i>trg</i></span>, <span>struct ly_set* <span class='fp'>src</span></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;src&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>, <span>void* <i>node</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>, <span>unsigned int <i>index</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_61')">
+<span class='ext'>[+]</span> <span class='ttype'>typedef</span> ly_module_imp_clb <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_61" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Base type has been changed from <span class='value'>char const*(*)(char const*, char const*, char const*, char const*, void*, LYS_INFORMAT*, void(**)(void*, void*))</span> to <span class='value'>LY_ERR(*)(char const*, char const*, char const*, char const*, void*, LYS_INFORMAT*, char const**, ly_module_imp_data_free_clb*)</span>.</td>
+<td>Replacement of the base data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_62')">
+[+] affected symbols: 2 (1.1%)</span>
+<div id="c_62" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module_imp_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>void** <i>user_data</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value is of type &#39;ly_module_imp_clb&#39;.</div>
+<span class='iname_a'>ly_ctx_set_module_imp_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const*(*<span class='fp'>clb</span>)(char const*, char const*, char const*, char const*, void*, LYS_INFORMAT*, void(**)(void*, void*))</span>, <span>void* <i>user_data</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;clb&#39; is of type &#39;ly_module_imp_clb&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_63')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lyd_node <span class='warning'>&nbsp;5&nbsp;</span></span>
+<br/>
+<div id="c_63" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>meta</b> has been added to this type.</td>
+<td>1) This field will not be initialized by old clients.<br/>2) Size of the inclusive type has been changed.<br/><br/><b>NOTE</b>: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Size of this type has been changed from <b>72</b> bytes to <b>48</b> bytes.</td>
+<td>The fields or parameters of such data type may be incorrectly initialized or accessed by old client applications.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Base type of field <b>parent</b> has been changed from <span class='nowrap'><span class='value'>struct lyd_node</span> (<b>72</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lyd_node_inner</span> (<b>64</b> bytes)</span>.</td>
+<td>Possible access of applications to incorrect memory through the pointer.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Base type of field <b>schema</b> has been changed from <span class='nowrap'><span class='value'>struct lys_node</span> (<b>112</b> bytes)</span> to <span class='nowrap'><span class='value'>struct lysc_node</span> (<b>88</b> bytes)</span>.</td>
+<td>Possible access of applications to incorrect memory through the pointer.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>flags</b> has been added to this type.</td>
+<td>This field will not be initialized by old clients.<br/><br/><b>NOTE</b>: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_64')">
+[+] affected symbols: 9 (5.2%)</span>
+<div id="c_64" style="display:none;">
+<div class='affected'><span class='iname_a'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>siblings</span></span>, <span>struct lys_node const* <i>schema</i></span>, <span>char const* <i>key_or_value</i></span>, <span>struct lyd_node** <i>match</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;siblings&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_65')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_OUTFORMAT <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_65" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The member <b>LYS_OUT_INFO</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The member <b>LYS_OUT_JSON</b> has been removed.</td>
+<td>This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_66')">
+[+] affected symbols: 5 (2.9%)</span>
+<div id="c_66" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>4th parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_67')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lys_module <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_67" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Size of this type has been changed from <b>184</b> bytes to <b>128</b> bytes.</td>
+<td>The fields or parameters of such data type may be incorrectly initialized or accessed by old client applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Size of field <b>latest_revision</b> has been changed from <b>1</b> bit to <b>1</b> byte.</td>
+<td>Previous accesses of applications and library functions to this field may be broken.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_68')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_68" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>int <i>implemented</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>uint32_t* <i>idx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Symbol_Binary_Problems_Low'></a><a name='Interface_Binary_Problems_Low'></a>
+<h2>Problems with Symbols, Low Severity <span class='warning'>&nbsp;57&nbsp;</span></h2><hr/>
+<span class='h_name'>dict.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_69')">
+<span class='ext'>[+]</span> lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_69" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>, <span>char const** <span class='color_p'>str_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>ctx</b> has been changed from <span class='value'>struct ly_ctx*</span> to <span class='value'>struct ly_ctx const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_70')">
+<span class='ext'>[+]</span> lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_70" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>, <span>char const** <span class='color_p'>str_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>ctx</b> has been changed from <span class='value'>struct ly_ctx*</span> to <span class='value'>struct ly_ctx const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_71')">
+<span class='ext'>[+]</span> lydict_remove&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_71" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_remove&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>ctx</b> has been changed from <span class='value'>struct ly_ctx*</span> to <span class='value'>struct ly_ctx const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>libyang.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_72')">
+<span class='ext'>[+]</span> ly_ctx_destroy&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_72" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_destroy&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lysc_node const*, void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Base type of <b>2nd</b> parameter <b>private_destructor</b> has been changed from <span class='value'>void(*)(struct lys_node const*, void*)</span> to <span class='value'>void(*)(struct lysc_node const*, void*)</span>.</td>
+<td>Replacement of parameter base type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_73')">
+<span class='ext'>[+]</span> ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>int <span class='color_p'>implemented</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_73" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct lys_module const*</span> to <span class='value'>struct lys_module*</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_74')">
+<span class='ext'>[+]</span> ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t* <span class='color_p'>idx</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_74" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t* <span class='color_p'>index</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>idx</b> has been renamed to <b>index</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_75')">
+<span class='ext'>[+]</span> ly_ctx_get_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_75" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_get_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>ctx</b> has been changed from <span class='value'>struct ly_ctx*</span> to <span class='value'>struct ly_ctx const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_76')">
+<span class='ext'>[+]</span> ly_ctx_internal_modules_count&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_76" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_internal_modules_count&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>ctx</b> has been changed from <span class='value'>struct ly_ctx*</span> to <span class='value'>struct ly_ctx const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='value'>unsigned int</span> to <b>uint32_t</b>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_77')">
+<span class='ext'>[+]</span> ly_ctx_set_searchdir&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>search_dir</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_77" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_78')">
+<span class='ext'>[+]</span> ly_get_log_clb&#160;<span class='sym_p'>(&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_78" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>void(*)(LY_LOG_LEVEL, char const*, char const*)</span> to <b>ly_log_clb</b>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_79')">
+<span class='ext'>[+]</span> ly_log_options&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>opts</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_79" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_log_options&#160;<span class='sym_p'><span>(&#160;uint32_t <span class='color_p'>opts</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>opts</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_80')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_80" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>node</b> has been renamed to <b>object</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_81')">
+<span class='ext'>[+]</span> ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_81" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>node</b> has been renamed to <b>object</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_82')">
+<span class='ext'>[+]</span> ly_set_log_clb&#160;<span class='sym_p'><span>(&#160;void(*<span class='color_p'>clb</span>)(LY_LOG_LEVEL, char const*, char const*)</span>, <span>int <span class='color_p'>path</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_82" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_log_clb&#160;<span class='sym_p'><span>(&#160;ly_log_clb <span class='color_p'>clb</span></span>, <span>ly_bool <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>clb</b> has been changed from <span class='value'>void(*)(LY_LOG_LEVEL, char const*, char const*)</span> to <b>ly_log_clb</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_83')">
+<span class='ext'>[+]</span> ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_83" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_84')">
+<span class='ext'>[+]</span> ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_84" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>node</b> has been renamed to <b>object</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_85')">
+<span class='ext'>[+]</span> ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>unsigned int <span class='color_p'>index</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_85" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>uint32_t <span class='color_p'>index</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>index</b> has been changed from <span class='value'>unsigned int</span> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_86')">
+<span class='ext'>[+]</span> lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_86" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_87')">
+<span class='ext'>[+]</span> lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_87" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>node</b> has been changed from <span class='value'>struct lyd_node*</span> to <span class='value'>struct lyd_node const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_88')">
+<span class='ext'>[+]</span> lyd_insert_after&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_88" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_89')">
+<span class='ext'>[+]</span> lyd_insert_before&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_89" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_90')">
+<span class='ext'>[+]</span> lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_90" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>, <span>struct lyd_node** <span class='color_p'>first</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>sibling</b> has been changed from <span class='value'>struct lyd_node**</span> to <span class='value'>struct lyd_node*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_91')">
+<span class='ext'>[+]</span> lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_91" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>instance</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>1st</b> parameter <b>node</b> has been renamed to <b>instance</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='value'>unsigned int</span> to <b>uint32_t</b>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_92')">
+<span class='ext'>[+]</span> lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>data_tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;5&nbsp;</span></span>
+<br/>
+<div id="c_92" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>value</b> has been changed from <b>void*</b> to <span class='value'>char const*</span>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of <b>5th</b> parameter <b>value_type</b> has been changed from <span class='value'>enum LYD_ANYDATA_VALUETYPE</span> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>3</th>
+<td><b>1st</b> parameter <b>data_tree</b> has been renamed to <b>parent</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>4</th>
+<td><b>5th</b> parameter <b>value_type</b> has been renamed to <b>options</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>5</th>
+<td><b>6th</b> parameter <b>options</b> has been renamed to <b>node</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_93')">
+<span class='ext'>[+]</span> lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_93" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>5th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of <b>1st</b> parameter <b>writeclb</b> has been changed from <span class='value'>ssize_t(*)(void*, void const*, size_t)</span> to <b>ly_write_clb</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>3</th>
+<td><b>2nd</b> parameter <b>arg</b> has been renamed to <b>user_data</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_94')">
+<span class='ext'>[+]</span> lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_94" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_95')">
+<span class='ext'>[+]</span> lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_95" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_96')">
+<span class='ext'>[+]</span> lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_96" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_97')">
+<span class='ext'>[+]</span> lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_97" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span>, <span class='lib_name'>libyang.so.1.9.19</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_98')">
+<span class='ext'>[+]</span> lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>last</span></span>, <span>struct lys_node const* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_98" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>last</span></span>, <span>struct lysc_node const* <span class='color_p'>parent</span></span>, <span>struct lysc_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_99')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_99" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>writeclb</b> has been changed from <span class='value'>ssize_t(*)(void*, void const*, size_t)</span> to <b>ly_write_clb</b>.</td>
+<td>Replacement of parameter data type may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>2</th>
+<td><b>2nd</b> parameter <b>arg</b> has been renamed to <b>user_data</b>.</td>
+<td>Renaming of a parameter may indicate a change in its semantic meaning.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_100')">
+<span class='ext'>[+]</span> lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_100" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_101')">
+<span class='ext'>[+]</span> lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_101" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_102')">
+<span class='ext'>[+]</span> lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_102" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_103')">
+<span class='ext'>[+]</span> lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_103" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_104')">
+<span class='ext'>[+]</span> lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <span class='color_p'>searchpaths</span></span>, <span>int <span class='color_p'>cwd</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char** <span class='color_p'>localfile</span></span>, <span>LYS_INFORMAT* <span class='color_p'>format</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_104" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_105')">
+<span class='ext'>[+]</span> lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_105" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Replacement of return type may indicate a change in its semantic meaning.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Other_Binary_Changes'></a><a name='Other_Binary_Changes_In_Types'></a>
+<h2>Other Changes in Data Types <span class='passed'>&nbsp;7&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_106')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LY_ERR <span class='passed'>&nbsp;6&nbsp;</span></span>
+<br/>
+<div id="c_106" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The member <b>LY_EDENIED</b> with value <b>8</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The member <b>LY_EEXIST</b> with value <b>4</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>The member <b>LY_EINCOMPLETE</b> with value <b>9</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>The member <b>LY_ENOT</b> with value <b>10</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>The member <b>LY_ENOTFOUND</b> with value <b>5</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>The member <b>LY_EOTHER</b> with value <b>11</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_107')">
+[+] affected symbols: 3 (1.7%)</span>
+<div id="c_107" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_err_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.no&#39; in 2nd parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+<span class='iname_a'>ly_err_first&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;retval.no&#39; in the return value (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+<span class='iname_a'>ly_err_print&#160;<span class='sym_p'><span>(&#160;struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.no&#39; in 1st parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_108')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_OUTFORMAT <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_108" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The member <b>LYS_OUT_YANG_COMPILED</b> with value <b>2</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_109')">
+[+] affected symbols: 5 (2.9%)</span>
+<div id="c_109" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>4th parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Headers'></a><h2>Header Files <span class='gray'>&nbsp;7&nbsp;</span></h2><hr/>
+<div class='h_list'>
+dict.h<br/>
+extensions.h<br/>
+libyang.h<br/>
+tree_data.h<br/>
+tree_schema.h<br/>
+user_types.h<br/>
+xml.h<br/>
+</div>
+<br/><a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Sources'></a><h2>Source Files <span class='gray'>&nbsp;11&nbsp;</span></h2><hr/>
+<div class='h_list'>
+common.c<br/>
+context.c<br/>
+hash_table.c<br/>
+log.c<br/>
+parser_lyb.c<br/>
+parser_xml.c<br/>
+plugins.c<br/>
+printer.c<br/>
+tree_data.c<br/>
+tree_schema.c<br/>
+xml.c<br/>
+</div>
+<br/><a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Libs'></a><h2>Objects <span class='gray'>&nbsp;1&nbsp;</span></h2><hr/>
+<div class='lib_list'>
+libyang.so.1.9.19<br/>
+</div>
+<br/><a class='top_ref' href='#Top'>to the top</a><br/>
+<br/><br/><br/></div><div id='SourceTab' class='tab'>
+<h2>Test Info</h2><hr/>
+<table class='summary'>
+<tr><th>Module Name</th><td>libyang.so</td></tr>
+<tr><th>Version #1</th><td>1.9.19</td></tr>
+<tr><th>Version #2</th><td>2.0.0</td></tr>
+<tr><th>Arch</th><td>x86_64</td></tr>
+<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>
+</table>
+<h2>Test Results</h2><hr/>
+<table class='summary'><tr><th>Total Header Files</th><td><a href='#Headers' style='color:Blue;'>7</a></td></tr>
+<tr><th>Total Source Files</th><td><a href='#Sources' style='color:Blue;'>11</a></td></tr>
+<tr><th>Total Objects</th><td><a href='#Libs' style='color:Blue;'>1</a></td></tr>
+<tr><th>Total Symbols / Types</th><td>174 / 22</td></tr>
+<tr><th>Compatibility</th>
+<td class='incompatible'>14.1%</td>
+</tr>
+</table>
+<h2>Problem Summary</h2><hr/>
+<table class='summary'><tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr><tr><th>Added Symbols</th><td>-</td><td class='new'><a href='#Source_Added' style='color:Blue;'>154</a></td></tr>
+<tr><th>Removed Symbols</th><td>High</td><td class='failed'><a href='#Source_Removed' style='color:Blue;'>114</a></td></tr>
+<tr><th rowspan='3'>Problems with<br/>Data Types</th><td>High</td><td class='failed'><a href='#Type_Source_Problems_High' style='color:Blue;'>112</a></td></tr>
+<tr><td>Medium</td><td class='failed'><a href='#Type_Source_Problems_Medium' style='color:Blue;'>2</a></td></tr>
+<tr><td>Low</td><td class='warning'><a href='#Type_Source_Problems_Low' style='color:Blue;'>9</a></td></tr>
+<tr><th rowspan='3'>Problems with<br/>Symbols</th><td>High</td><td class='failed'><a href='#Symbol_Source_Problems_High' style='color:Blue;'>13</a></td></tr>
+<tr><td>Medium</td><td class='failed'><a href='#Symbol_Source_Problems_Medium' style='color:Blue;'>47</a></td></tr>
+<tr><td>Low</td><td class='warning'><a href='#Symbol_Source_Problems_Low' style='color:Blue;'>52</a></td></tr>
+<tr><th>Problems with<br/>Constants</th><td>Low</td><td>0</td></tr>
+<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td class='passed'><a href='#Other_Source_Changes_In_Types' style='color:Blue;'>24</a></td></tr>
+<tr><th>Other Changes<br/>in Symbols</th><td>-</td><td class='passed'><a href='#Other_Source_Changes_In_Symbols' style='color:Blue;'>10</a></td></tr>
+</table>
+
+<a name='Source_Added'></a><h2>Added Symbols <span class='new'>&nbsp;154&nbsp;</span></h2><hr/>
+<span class='h_name'>context.h</span><br/>
+<span class="iname">ly_ctx_get_module_implemented&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_implemented_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_latest&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_latest_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>, <span>char const* <span class='color_p'>revision</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_yanglib_data&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_node** <span class='color_p'>root_p</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_yanglib_id&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_reset_latests&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>uint16_t <span class='color_p'>option</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>uint16_t <span class='color_p'>option</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_searchdir&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_searchdir_last&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>uint32_t <span class='color_p'>count</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>in.h</span><br/>
+<span class="iname">ly_in_fd&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>int <span class='color_p'>fd</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_file&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>FILE* <span class='color_p'>f</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_filepath&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>char const* <span class='color_p'>filepath</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_free&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>ly_bool <span class='color_p'>destroy</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_memory&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>, <span>char const* <span class='color_p'>str</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_filepath&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>filepath</span></span>, <span>size_t <span class='color_p'>len</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_new_memory&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>str</span></span>, <span>struct ly_in** <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_parsed&#160;<span class='sym_p'><span>(&#160;struct ly_in const* <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_reset&#160;<span class='sym_p'><span>(&#160;struct ly_in* <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_in_type&#160;<span class='sym_p'><span>(&#160;struct ly_in const* <span class='color_p'>in</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>log.h</span><br/>
+<span class="iname">ly_err_last&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_errcode&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_log_dbg_groups&#160;<span class='sym_p'><span>(&#160;uint32_t <span class='color_p'>dbg_groups</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_log_level&#160;<span class='sym_p'><span>(&#160;enum LY_LOG_LEVEL <span class='color_p'>level</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>out.h</span><br/>
+<span class="iname">ly_out_clb&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>ly_write_clb <span class='color_p'>writeclb</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_clb_arg&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>void* <span class='color_p'>arg</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_fd&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>int <span class='color_p'>fd</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_file&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>FILE* <span class='color_p'>f</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_filepath&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>char const* <span class='color_p'>filepath</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_free&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>void(*<span class='color_p'>clb_arg_destructor</span>)(void*)</span>, <span>ly_bool <span class='color_p'>destroy</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_filepath&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>filepath</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_new_memory&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>size_t <span class='color_p'>size</span></span>, <span>struct ly_out** <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_printed&#160;<span class='sym_p'><span>(&#160;struct ly_out const* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_reset&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_out_type&#160;<span class='sym_p'><span>(&#160;struct ly_out const* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_print&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>char const* <span class='color_p'>format</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">ly_print_flush&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_write&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>char const* <span class='color_p'>buf</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>parser_data.h</span><br/>
+<span class="iname">lyd_parse_data&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_data_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_data_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_data_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>parse_options</span></span>, <span>uint32_t <span class='color_p'>validate_options</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_notif&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lyd_node** <span class='color_p'>ntf</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_reply&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>request</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lyd_node** <span class='color_p'>op</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_rpc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lyd_node** <span class='color_p'>op</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t <span class='color_p'>val_opts</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>val_opts</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_op&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>op_tree</span></span>, <span>struct lyd_node const* <span class='color_p'>tree</span></span>, <span>enum LYD_VALIDATE_OP <span class='color_p'>op</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>parser_schema.h</span><br/>
+<span class="iname">lys_parse&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct ly_in* <span class='color_p'>in</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>char const** <span class='color_p'>features</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>plugins_types.h</span><br/>
+<span class="iname">ly_builtin_type_plugins <span class='attr'>[data]</span></span><br/>
+<span class="iname">ly_err_free&#160;<span class='sym_p'><span>(&#160;void* <span class='color_p'>ptr</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_err_new&#160;<span class='sym_p'><span>(&#160;enum LY_LOG_LEVEL <span class='color_p'>level</span></span>, <span>enum LY_ERR <span class='color_p'>no</span></span>, <span>enum LY_VECODE <span class='color_p'>vecode</span></span>, <span>char* <span class='color_p'>msg</span></span>, <span>char* <span class='color_p'>path</span></span>, <span>char* <span class='color_p'>apptag</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_identity_isderived&#160;<span class='sym_p'><span>(&#160;struct lysc_ident* <span class='color_p'>base</span></span>, <span>struct lysc_ident* <span class='color_p'>der</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_parse_dec64&#160;<span class='sym_p'><span>(&#160;uint8_t <span class='color_p'>fraction_digits</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>int64_t* <span class='color_p'>ret</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_parse_int&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>datatype</span></span>, <span>int <span class='color_p'>base</span></span>, <span>int64_t <span class='color_p'>min</span></span>, <span>int64_t <span class='color_p'>max</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>int64_t* <span class='color_p'>ret</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_parse_uint&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>datatype</span></span>, <span>int <span class='color_p'>base</span></span>, <span>uint64_t <span class='color_p'>max</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>uint64_t* <span class='color_p'>ret</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_print_get_prefix&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>mod</span></span>, <span>enum LY_PREFIX_FORMAT <span class='color_p'>format</span></span>, <span>void* <span class='color_p'>prefix_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_store_resolve_prefix&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>prefix</span></span>, <span>size_t <span class='color_p'>prefix_len</span></span>, <span>enum LY_PREFIX_FORMAT <span class='color_p'>format</span></span>, <span>void* <span class='color_p'>prefix_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_validate_patterns&#160;<span class='sym_p'><span>(&#160;struct lysc_pattern** <span class='color_p'>patterns</span></span>, <span>char const* <span class='color_p'>str</span></span>, <span>size_t <span class='color_p'>str_len</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_type_validate_range&#160;<span class='sym_p'><span>(&#160;enum LY_DATA_TYPE <span class='color_p'>basetype</span></span>, <span>struct lysc_range* <span class='color_p'>range</span></span>, <span>int64_t <span class='color_p'>value</span></span>, <span>char const* <span class='color_p'>strval</span></span>, <span>struct ly_err_item** <span class='color_p'>err</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_prefixes_compile&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>str</span></span>, <span>size_t <span class='color_p'>str_len</span></span>, <span>struct lysp_module const* <span class='color_p'>prefix_mod</span></span>, <span>struct lysc_prefix** <span class='color_p'>prefixes</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_prefixes_dup&#160;<span class='sym_p'><span>(&#160;struct lysc_prefix const* <span class='color_p'>orig</span></span>, <span>struct lysc_prefix** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_prefixes_free&#160;<span class='sym_p'><span>(&#160;struct lysc_prefix* <span class='color_p'>prefixes</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>printer_data.h</span><br/>
+<span class="iname">lyd_print_all&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_print_tree&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>printer_schema.h</span><br/>
+<span class="iname">lys_print_module&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>size_t <span class='color_p'>UNUSED_line_length</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_print_node&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lysc_node const* <span class='color_p'>node</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>size_t <span class='color_p'>UNUSED_line_length</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_print_submodule&#160;<span class='sym_p'><span>(&#160;struct ly_out* <span class='color_p'>out</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>struct lysp_submodule const* <span class='color_p'>submodule</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>size_t <span class='color_p'>UNUSED_line_length</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>set.h</span><br/>
+<span class="iname">ly_set_erase&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="iname">lyd_any_copy_value&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>trg</span></span>, <span>union lyd_any_value const* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_change_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_meta* <span class='color_p'>meta</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_change_term&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>term</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_child&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_child_no_keys&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_compare_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_meta const* <span class='color_p'>meta1</span></span>, <span>struct lyd_meta const* <span class='color_p'>meta2</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_compare_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node1</span></span>, <span>struct lyd_node const* <span class='color_p'>node2</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_compare_single&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node1</span></span>, <span>struct lyd_node const* <span class='color_p'>node2</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_apply_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>data</span></span>, <span>struct lyd_node const* <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_apply_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>data</span></span>, <span>struct lyd_node const* <span class='color_p'>diff</span></span>, <span>struct lys_module const* <span class='color_p'>mod</span></span>, <span>lyd_diff_cb <span class='color_p'>diff_cb</span></span>, <span>void* <span class='color_p'>cb_data</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_merge_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>diff</span></span>, <span>struct lyd_node const* <span class='color_p'>src_diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_merge_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>diff</span></span>, <span>struct lyd_node const* <span class='color_p'>src_diff</span></span>, <span>struct lys_module const* <span class='color_p'>mod</span></span>, <span>lyd_diff_cb <span class='color_p'>diff_cb</span></span>, <span>void* <span class='color_p'>cb_data</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_merge_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>diff_first</span></span>, <span>struct lyd_node* <span class='color_p'>diff_parent</span></span>, <span>struct lyd_node const* <span class='color_p'>src_sibling</span></span>, <span>lyd_diff_cb <span class='color_p'>diff_cb</span></span>, <span>void* <span class='color_p'>cb_data</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_reverse_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>src_diff</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>first</span></span>, <span>struct lyd_node const* <span class='color_p'>second</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>first</span></span>, <span>struct lyd_node const* <span class='color_p'>second</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_meta_single&#160;<span class='sym_p'><span>(&#160;struct lyd_meta const* <span class='color_p'>meta</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>, <span>struct lyd_meta** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>struct lyd_node_inner* <span class='color_p'>parent</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_single&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>struct lyd_node_inner* <span class='color_p'>parent</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>dup</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_meta const* <span class='color_p'>first</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_sibling_first&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lyd_node const* <span class='color_p'>target</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_xpath&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>xpath</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_attr_siblings&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_attr* <span class='color_p'>attr</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_attr_single&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_attr* <span class='color_p'>attr</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_meta_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_meta* <span class='color_p'>meta</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_meta_single&#160;<span class='sym_p'><span>(&#160;struct lyd_meta* <span class='color_p'>meta</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_insert_child&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_is_default&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge_siblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>target</span></span>, <span>struct lyd_node const* <span class='color_p'>source</span></span>, <span>uint16_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>target</span></span>, <span>struct lyd_node const* <span class='color_p'>source</span></span>, <span>uint16_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_any&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>void const* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_attr&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>char const* <span class='color_p'>module_name</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>, <span>struct lyd_attr** <span class='color_p'>attr</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_implicit_all&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t <span class='color_p'>implicit_options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_implicit_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>tree</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>implicit_options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_implicit_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>tree</span></span>, <span>uint32_t <span class='color_p'>implicit_options</span></span>, <span>struct lyd_node** <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_inner&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_list&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_list2&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>keys</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_meta&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>, <span>struct lyd_meta** <span class='color_p'>meta</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_opaq&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>char const* <span class='color_p'>module_name</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_path2&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void const* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>new_parent</span></span>, <span>struct lyd_node** <span class='color_p'>new_node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_term&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_owner_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parent&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_target&#160;<span class='sym_p'><span>(&#160;struct ly_path const* <span class='color_p'>path</span></span>, <span>struct lyd_node const* <span class='color_p'>tree</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_unlink_tree&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_value_compare&#160;<span class='sym_p'><span>(&#160;struct lyd_node_term const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_value_validate&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lyd_node_term const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>, <span>struct lyd_node const* <span class='color_p'>tree</span></span>, <span>struct lysc_type const** <span class='color_p'>realtype</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="iname">lyext_parent2str&#160;<span class='sym_p'><span>(&#160;enum LYEXT_PARENT <span class='color_p'>type</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_feature_value&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_child&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>size_t <span class='color_p'>name_len</span></span>, <span>uint16_t <span class='color_p'>nodetype</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_expr_atoms&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>struct lys_module const* <span class='color_p'>cur_mod</span></span>, <span>struct lyxp_expr const* <span class='color_p'>expr</span></span>, <span>struct lysc_prefix const* <span class='color_p'>prefixes</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_lypath_atoms&#160;<span class='sym_p'><span>(&#160;struct ly_path const* <span class='color_p'>path</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_path_atoms&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>ly_bool <span class='color_p'>output</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_xpath&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>xpath</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_find_xpath_atoms&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>xpath</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_nodetype2str&#160;<span class='sym_p'><span>(&#160;uint16_t <span class='color_p'>nodetype</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_value_validate&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>value_len</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_iffeature_value&#160;<span class='sym_p'><span>(&#160;struct lysc_iffeature const* <span class='color_p'>iff</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_module_dfs_full&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>mod</span></span>, <span>lysc_dfs_clb <span class='color_p'>dfs_clb</span></span>, <span>void* <span class='color_p'>data</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_actions&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_children&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>uint16_t <span class='color_p'>flags</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_children_full&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>uint16_t <span class='color_p'>flags</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_notifs&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_node_parent_full&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_path&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>enum LYSC_PATH_TYPE <span class='color_p'>pathtype</span></span>, <span>char* <span class='color_p'>buffer</span></span>, <span>size_t <span class='color_p'>buflen</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_set_private&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>node</span></span>, <span>void* <span class='color_p'>priv</span></span>, <span>void** <span class='color_p'>prev_priv_p</span></span>&#160;)</span></span><br/>
+<span class="iname">lysc_tree_dfs_full&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>root</span></span>, <span>lysc_dfs_clb <span class='color_p'>dfs_clb</span></span>, <span>void* <span class='color_p'>data</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_feature_next&#160;<span class='sym_p'><span>(&#160;struct lysp_feature const* <span class='color_p'>last</span></span>, <span>struct lysp_module const* <span class='color_p'>pmod</span></span>, <span>uint32_t* <span class='color_p'>idx</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_actions&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_children&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_groupings&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_notifs&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lysp_node_typedefs&#160;<span class='sym_p'><span>(&#160;struct lysp_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxp_get_expr&#160;<span class='sym_p'><span>(&#160;struct lyxp_expr const* <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Source_Removed'></a><a name='Source_Withdrawn'></a><h2>Removed Symbols <span class='failed'>&nbsp;114&nbsp;</span></h2><hr/>
+<span class='h_name'>extensions.h</span><br/>
+<span class="iname">lyext_vlog&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>enum LY_VECODE <span class='color_p'>vecode</span></span>, <span>char const* <span class='color_p'>plugin</span></span>, <span>char const* <span class='color_p'>function</span></span>, <span>enum LYEXT_VLOG_ELEM <span class='color_p'>elem_type</span></span>, <span>void const* <span class='color_p'>elem</span></span>, <span>char const* <span class='color_p'>format</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lys_iffeature_free&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lys_iffeature* <span class='color_p'>iffeature</span></span>, <span>uint8_t <span class='color_p'>iffeature_size</span></span>, <span>int <span class='color_p'>shallow</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="iname">ly_ctx_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_find_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_disabled_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t* <span class='color_p'>idx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_by_ns&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>ns</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>int <span class='color_p'>implemented</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_data_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>void** <span class='color_p'>user_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_module_older&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_node&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lys_node const* <span class='color_p'>start</span></span>, <span>char const* <span class='color_p'>nodeid</span></span>, <span>int <span class='color_p'>output</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_submodule&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char const* <span class='color_p'>submodule</span></span>, <span>char const* <span class='color_p'>sub_revision</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_get_submodule2&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>main_module</span></span>, <span>char const* <span class='color_p'>submodule</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_info&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_new_ylmem&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_new_ylpath&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_remove_module&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_allimplemented&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_disable_searchdir_cwd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_disable_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_module_data_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>ly_module_data_clb <span class='color_p'>clb</span></span>, <span>void* <span class='color_p'>user_data</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_prefer_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_set_trusted&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_allimplemented&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_disable_searchdir_cwd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_disable_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_prefer_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_searchdirs&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>index</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_ctx_unset_trusted&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_errno_glob_address&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_path_data2schema&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data_path</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_path_xml2json&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>xml_path</span></span>, <span>struct lyxml_elem* <span class='color_p'>xml</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_verb&#160;<span class='sym_p'><span>(&#160;enum LY_LOG_LEVEL <span class='color_p'>level</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_verb_dbg&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>dbg_groups</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="iname">lyd_change_leaf&#160;<span class='sym_p'><span>(&#160;struct lyd_node_leaf_list* <span class='color_p'>leaf</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dec64_to_double&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_diff&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>first</span></span>, <span>struct lyd_node* <span class='color_p'>second</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_to_ctx&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>, <span>struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_dup_withsiblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_instance&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>data</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lyd_node const* <span class='color_p'>target</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_find_sibling_set&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lyd_node const* <span class='color_p'>target</span></span>, <span>struct ly_set** <span class='color_p'>set</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_attr&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lyd_attr* <span class='color_p'>attr</span></span>, <span>int <span class='color_p'>recursive</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_diff&#160;<span class='sym_p'><span>(&#160;struct lyd_difflist* <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_val_diff&#160;<span class='sym_p'><span>(&#160;struct lyd_difflist* <span class='color_p'>diff</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_free_withsiblings&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_insert&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_insert_attr&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>mod</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_leaf_type&#160;<span class='sym_p'><span>(&#160;struct lyd_node_leaf_list const* <span class='color_p'>leaf</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>target</span></span>, <span>struct lyd_node const* <span class='color_p'>source</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_merge_to_ctx&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>trg</span></span>, <span>struct lyd_node const* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>, <span>struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_anydata&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_leaf&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_output&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_output_anydata&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_output_leaf&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>val_str</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_new_yangdata&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>name_template</span></span>, <span>char const* <span class='color_p'>name</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_node_module&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_node_should_print&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_parse_xml&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem** <span class='color_p'>root</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_schema_sort&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>int <span class='color_p'>recursive</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_unlink&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>, <span>void* <span class='color_p'>var_arg</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_modules&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>node</span></span>, <span>struct lys_module const** <span class='color_p'>modules</span></span>, <span>int <span class='color_p'>mod_count</span></span>, <span>int <span class='color_p'>options</span></span>, <span>...</span>&#160;)</span></span><br/>
+<span class="iname">lyd_validate_value&#160;<span class='sym_p'><span>(&#160;struct lys_node* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_value_type&#160;<span class='sym_p'><span>(&#160;struct lys_node* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>struct lys_type** <span class='color_p'>type</span></span>&#160;)</span></span><br/>
+<span class="iname">lyd_wd_default&#160;<span class='sym_p'><span>(&#160;struct lyd_node_leaf_list* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="iname">ly_clean_plugins&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_get_loaded_plugins&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_load_plugins&#160;<span class='sym_p'>(&#160;)</span></span><br/>
+<span class="iname">ly_register_exts&#160;<span class='sym_p'><span>(&#160;struct lyext_plugin_list* <span class='color_p'>plugin</span></span>, <span>char const* <span class='color_p'>log_name</span></span>&#160;)</span></span><br/>
+<span class="iname">ly_register_types&#160;<span class='sym_p'><span>(&#160;struct lytype_plugin_list* <span class='color_p'>plugin</span></span>, <span>char const* <span class='color_p'>log_name</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_data_path&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_data_path_pattern&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>char const* <span class='color_p'>placeholder</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_ext_complex_get_substmt&#160;<span class='sym_p'><span>(&#160;enum LY_STMT <span class='color_p'>stmt</span></span>, <span>struct lys_ext_instance_complex* <span class='color_p'>ext</span></span>, <span>struct lyext_substmt** <span class='color_p'>info</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_ext_instance_presence&#160;<span class='sym_p'><span>(&#160;struct lys_ext* <span class='color_p'>def</span></span>, <span>struct lys_ext_instance** <span class='color_p'>ext</span></span>, <span>uint8_t <span class='color_p'>ext_size</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_ext_instance_substmt&#160;<span class='sym_p'><span>(&#160;struct lys_ext_instance const* <span class='color_p'>ext</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_disable&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_disable_force&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_enable&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_enable_force&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_list&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>uint8_t** <span class='color_p'>states</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_features_state&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>, <span>char const* <span class='color_p'>feature</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_getnext_union_type&#160;<span class='sym_p'><span>(&#160;struct lys_type const* <span class='color_p'>last</span></span>, <span>struct lys_type const* <span class='color_p'>type</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_iffeature_value&#160;<span class='sym_p'><span>(&#160;struct lys_iffeature const* <span class='color_p'>iff</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_implemented_module&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>mod</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_is_disabled&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>recursive</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_is_key&#160;<span class='sym_p'><span>(&#160;struct lys_node_leaf const* <span class='color_p'>node</span></span>, <span>uint8_t* <span class='color_p'>index</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_main_module&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_node_module&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_node_xpath_atomize&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_parent&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_path&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_set_disabled&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_set_enabled&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_set_private&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>node</span></span>, <span>void* <span class='color_p'>priv</span></span>&#160;)</span></span><br/>
+<span class="iname">lys_xpath_atomize&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>ctx_node</span></span>, <span>enum lyxp_node_type <span class='color_p'>ctx_node_type</span></span>, <span>char const* <span class='color_p'>expr</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<br/>
+<span class='h_name'>xml.h</span><br/>
+<span class="iname">lyxml_dup&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>root</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_free&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>elem</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_free_withsiblings&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>elem</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_get_attr&#160;<span class='sym_p'><span>(&#160;struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>ns</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_get_ns&#160;<span class='sym_p'><span>(&#160;struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>char const* <span class='color_p'>prefix</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>filename</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>stream</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lyxml_elem const* <span class='color_p'>elem</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<span class="iname">lyxml_unlink&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>struct lyxml_elem* <span class='color_p'>elem</span></span>&#160;)</span></span><br/>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='High_Risk_Source_Problems'></a><a name='Type_Source_Problems_High'></a>
+<h2>Problems with Data Types, High Severity <span class='failed'>&nbsp;112&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_110')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LY_VECODE <span class='failed'>&nbsp;77&nbsp;</span></span>
+<br/>
+<div id="c_110" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Name of member with value <b>1</b> has been changed from <b>LYVE_XML_MISS</b> to <b>LYVE_SYNTAX</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_XML_MISS</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Name of member with value <b>10</b> has been changed from <b>LYVE_MISSSTMT</b> to <b>LYVE_OTHER</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_MISSSTMT</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Name of member with value <b>2</b> has been changed from <b>LYVE_XML_INVAL</b> to <b>LYVE_SYNTAX_YANG</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_XML_INVAL</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Name of member with value <b>3</b> has been changed from <b>LYVE_XML_INCHAR</b> to <b>LYVE_SYNTAX_YIN</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_XML_INCHAR</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Name of member with value <b>4</b> has been changed from <b>LYVE_EOF</b> to <b>LYVE_REFERENCE</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_EOF</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Name of member with value <b>5</b> has been changed from <b>LYVE_INSTMT</b> to <b>LYVE_XPATH</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_INSTMT</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>Name of member with value <b>6</b> has been changed from <b>LYVE_INPAR</b> to <b>LYVE_SEMANTICS</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_INPAR</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>Name of member with value <b>7</b> has been changed from <b>LYVE_INID</b> to <b>LYVE_SYNTAX_XML</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_INID</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>Name of member with value <b>8</b> has been changed from <b>LYVE_INDATE</b> to <b>LYVE_SYNTAX_JSON</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_INDATE</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>10</th>
+<td>Name of member with value <b>9</b> has been changed from <b>LYVE_INARG</b> to <b>LYVE_DATA</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: '<b>LYVE_INARG</b>' was not declared in this scope.</td>
+</tr>
+<tr>
+<th>11</th>
+<td>The member <b>LYVE_BITS_INNAME</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>12</th>
+<td>The member <b>LYVE_BITS_INVAL</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>13</th>
+<td>The member <b>LYVE_CIRC_FEATURES</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>14</th>
+<td>The member <b>LYVE_CIRC_IMPORTS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>15</th>
+<td>The member <b>LYVE_CIRC_INCLUDES</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>16</th>
+<td>The member <b>LYVE_CIRC_LEAFREFS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>17</th>
+<td>The member <b>LYVE_DUPID</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>18</th>
+<td>The member <b>LYVE_DUPLEAFLIST</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>19</th>
+<td>The member <b>LYVE_DUPLIST</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>20</th>
+<td>The member <b>LYVE_ENUM_INNAME</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>21</th>
+<td>The member <b>LYVE_ENUM_INVAL</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>22</th>
+<td>The member <b>LYVE_ENUM_WS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>23</th>
+<td>The member <b>LYVE_INATTR</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>24</th>
+<td>The member <b>LYVE_INCHAR</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>25</th>
+<td>The member <b>LYVE_INELEM</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>26</th>
+<td>The member <b>LYVE_INMETA</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>27</th>
+<td>The member <b>LYVE_INMOD</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>28</th>
+<td>The member <b>LYVE_INORDER</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>29</th>
+<td>The member <b>LYVE_INPRED</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>30</th>
+<td>The member <b>LYVE_INREGEX</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>31</th>
+<td>The member <b>LYVE_INRESOLV</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>32</th>
+<td>The member <b>LYVE_INSTATUS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>33</th>
+<td>The member <b>LYVE_INVAL</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>34</th>
+<td>The member <b>LYVE_INVER</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>35</th>
+<td>The member <b>LYVE_INWHEN</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>36</th>
+<td>The member <b>LYVE_KEY_CONFIG</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>37</th>
+<td>The member <b>LYVE_KEY_DUP</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>38</th>
+<td>The member <b>LYVE_KEY_MISS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>39</th>
+<td>The member <b>LYVE_KEY_NLEAF</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>40</th>
+<td>The member <b>LYVE_KEY_TYPE</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>41</th>
+<td>The member <b>LYVE_MCASEDATA</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>42</th>
+<td>The member <b>LYVE_MISSARG</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>43</th>
+<td>The member <b>LYVE_MISSATTR</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>44</th>
+<td>The member <b>LYVE_MISSELEM</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>45</th>
+<td>The member <b>LYVE_NOCONSTR</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>46</th>
+<td>The member <b>LYVE_NOLEAFREF</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>47</th>
+<td>The member <b>LYVE_NOMANDCHOICE</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>48</th>
+<td>The member <b>LYVE_NOMAX</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>49</th>
+<td>The member <b>LYVE_NOMIN</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>50</th>
+<td>The member <b>LYVE_NOMUST</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>51</th>
+<td>The member <b>LYVE_NOREQINS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>52</th>
+<td>The member <b>LYVE_NORESOLV</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>53</th>
+<td>The member <b>LYVE_NOUNIQ</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>54</th>
+<td>The member <b>LYVE_NOWHEN</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>55</th>
+<td>The member <b>LYVE_OBSDATA</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>56</th>
+<td>The member <b>LYVE_PATH_EXISTS</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>57</th>
+<td>The member <b>LYVE_PATH_INCHAR</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>58</th>
+<td>The member <b>LYVE_PATH_INIDENTREF</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>59</th>
+<td>The member <b>LYVE_PATH_INKEY</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>60</th>
+<td>The member <b>LYVE_PATH_INMOD</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>61</th>
+<td>The member <b>LYVE_PATH_INNODE</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>62</th>
+<td>The member <b>LYVE_PATH_MISSKEY</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>63</th>
+<td>The member <b>LYVE_PATH_MISSMOD</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>64</th>
+<td>The member <b>LYVE_PATH_MISSPAR</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>65</th>
+<td>The member <b>LYVE_PATH_PREDTOOMANY</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>66</th>
+<td>The member <b>LYVE_SUBMODULE</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>67</th>
+<td>The member <b>LYVE_TOOMANY</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>68</th>
+<td>The member <b>LYVE_XPATH_DUMMY</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>69</th>
+<td>The member <b>LYVE_XPATH_EOF</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>70</th>
+<td>The member <b>LYVE_XPATH_INARGCOUNT</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>71</th>
+<td>The member <b>LYVE_XPATH_INARGTYPE</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>72</th>
+<td>The member <b>LYVE_XPATH_INCTX</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>73</th>
+<td>The member <b>LYVE_XPATH_INFUNC</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>74</th>
+<td>The member <b>LYVE_XPATH_INMOD</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>75</th>
+<td>The member <b>LYVE_XPATH_INOP</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>76</th>
+<td>The member <b>LYVE_XPATH_INTOK</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>77</th>
+<td>The member <b>LYVE_XPATH_NOEND</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_111')">
+[+] affected symbols: 4 (2.3%)</span>
+<div id="c_111" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_err_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.vecode&#39; in 2nd parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_VECODE&#39;.</div>
+<span class='iname_a'>ly_err_first&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;retval.vecode&#39; in the return value (pointer) is of type &#39;enum LY_VECODE&#39;.</div>
+<span class='iname_a'>ly_err_print&#160;<span class='sym_p'><span>(&#160;struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.vecode&#39; in 1st parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_VECODE&#39;.</div>
+<span class='iname_a'>ly_vecode&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value is of type &#39;enum LY_VECODE&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_112')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> ly_set <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_112" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>number</b> has been renamed to <b>count</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: <span class='value'>struct ly_set</span> has no member named <b>number</b>.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>set</b> has been renamed to <b>unnamed0</b>.</td>
+<td>Recompilation of a client program may be broken with the error message: <span class='value'>struct ly_set</span> has no member named <b>set</b>.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_113')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_113" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>, <span>void* <i>node</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_clean&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='fp'>set</span></span>, <span>void* <i>node</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='fp'>set</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_free&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <i>trg</i></span>, <span>struct ly_set* <span class='fp'>src</span></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;src&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>, <span>void* <i>node</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+<span class='iname_a'>ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='fp'>set</span></span>, <span>unsigned int <i>index</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;set&#39; (pointer) has base type &#39;struct ly_set&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_114')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lyd_node <span class='failed'>&nbsp;6&nbsp;</span></span>
+<br/>
+<div id="c_114" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>attr</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lyd_node</span>' has no member named '<b>attr</b>'.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>child</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lyd_node</span>' has no member named '<b>child</b>'.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>dflt</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lyd_node</span>' has no member named '<b>dflt</b>'.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Field <b>ht</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lyd_node</span>' has no member named '<b>ht</b>'.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>validity</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lyd_node</span>' has no member named '<b>validity</b>'.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Field <b>when_status</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lyd_node</span>' has no member named '<b>when_status</b>'.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_115')">
+[+] affected symbols: 9 (5.2%)</span>
+<div id="c_115" style="display:none;">
+<div class='affected'><span class='iname_a'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>siblings</span></span>, <span>struct lys_node const* <i>schema</i></span>, <span>char const* <i>key_or_value</i></span>, <span>struct lyd_node** <i>match</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;siblings&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_116')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_OUTFORMAT <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_116" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The member <b>LYS_OUT_INFO</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The member <b>LYS_OUT_JSON</b> has been removed.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_117')">
+[+] affected symbols: 5 (2.9%)</span>
+<div id="c_117" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>4th parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_118')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lys_module <span class='failed'>&nbsp;25&nbsp;</span></span>
+<br/>
+<div id="c_118" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>augment</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>augment</b>'.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>augment_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>augment_size</b>'.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>data</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>data</b>'.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Field <b>deviated</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>deviated</b>'.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>deviation</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>deviation</b>'.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Field <b>deviation_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>deviation_size</b>'.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>Field <b>disabled</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>disabled</b>'.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>Field <b>ext</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>ext</b>'.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>Field <b>ext_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>ext_size</b>'.</td>
+</tr>
+<tr>
+<th>10</th>
+<td>Field <b>extensions</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>extensions</b>'.</td>
+</tr>
+<tr>
+<th>11</th>
+<td>Field <b>extensions_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>extensions_size</b>'.</td>
+</tr>
+<tr>
+<th>12</th>
+<td>Field <b>features</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>features</b>'.</td>
+</tr>
+<tr>
+<th>13</th>
+<td>Field <b>features_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>features_size</b>'.</td>
+</tr>
+<tr>
+<th>14</th>
+<td>Field <b>ident</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>ident</b>'.</td>
+</tr>
+<tr>
+<th>15</th>
+<td>Field <b>ident_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>ident_size</b>'.</td>
+</tr>
+<tr>
+<th>16</th>
+<td>Field <b>imp</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>imp</b>'.</td>
+</tr>
+<tr>
+<th>17</th>
+<td>Field <b>imp_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>imp_size</b>'.</td>
+</tr>
+<tr>
+<th>18</th>
+<td>Field <b>inc</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>inc</b>'.</td>
+</tr>
+<tr>
+<th>19</th>
+<td>Field <b>inc_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>inc_size</b>'.</td>
+</tr>
+<tr>
+<th>20</th>
+<td>Field <b>rev</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>rev</b>'.</td>
+</tr>
+<tr>
+<th>21</th>
+<td>Field <b>rev_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>rev_size</b>'.</td>
+</tr>
+<tr>
+<th>22</th>
+<td>Field <b>tpdf</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>tpdf</b>'.</td>
+</tr>
+<tr>
+<th>23</th>
+<td>Field <b>tpdf_size</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>tpdf_size</b>'.</td>
+</tr>
+<tr>
+<th>24</th>
+<td>Field <b>type</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>type</b>'.</td>
+</tr>
+<tr>
+<th>25</th>
+<td>Field <b>version</b> has been removed from this type.</td>
+<td>Recompilation of a client program may be broken with the error message: '<span class='value'>struct lys_module</span>' has no member named '<b>version</b>'.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_119')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_119" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>int <i>implemented</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>uint32_t* <i>idx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Symbol_Source_Problems_High'></a><a name='Interface_Source_Problems_High'></a>
+<h2>Problems with Symbols, High Severity <span class='failed'>&nbsp;13&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_120')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_120" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>list</b> of type <b>ly_bool</b> has been added to the calling stack at the middle position.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_121')">
+<span class='ext'>[+]</span> ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_121" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>void*(*<span class='color_p'>duplicator</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>list</b> of type <b>ly_bool</b> has been added to the calling stack at the middle position.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_122')">
+<span class='ext'>[+]</span> lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_122" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lysc_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>size_t <span class='color_p'>val_len</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>val_len</b> of type <b>size_t</b> has been added to the calling stack at the middle position.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_123')">
+<span class='ext'>[+]</span> lys_find_path&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>cur_module</span></span>, <span>struct lys_node const* <span class='color_p'>cur_node</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span> <span class='failed'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_123" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_find_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>ly_bool <span class='color_p'>output</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>ctx</b> of type <span class='value'>struct ly_ctx const*</span> has been added to the calling stack at the middle position.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Parameter <b>ctx_node</b> of type <span class='value'>struct lysc_node const*</span> has been added to the calling stack at the middle position.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>3</th>
+<td><b>1st</b> middle parameter <b>cur_module</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>4</th>
+<td><b>2nd</b> middle parameter <b>cur_node</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_124')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_124" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_125')">
+<span class='ext'>[+]</span> lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_125" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_126')">
+<span class='ext'>[+]</span> lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_126" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_127')">
+<span class='ext'>[+]</span> lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_127" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_128')">
+<span class='ext'>[+]</span> lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_128" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> middle parameter <b>target_node</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_129')">
+<span class='ext'>[+]</span> lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_129" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module* <span class='color_p'>mod</span></span>, <span>char const** <span class='color_p'>features</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>mod</b> of type <span class='value'>struct lys_module*</span> has been added to the calling stack at the middle position.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Medium_Risk_Source_Problems'></a><a name='Type_Source_Problems_Medium'></a>
+<h2>Problems with Data Types, Medium Severity <span class='failed'>&nbsp;2&nbsp;</span></h2><hr/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_130')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lyd_node <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_130" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Base type of field <b>parent</b> has been changed from <span class='value'>struct lyd_node</span> to <span class='value'>struct lyd_node_inner</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Base type of field <b>schema</b> has been changed from <span class='value'>struct lys_node</span> to <span class='value'>struct lysc_node</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_131')">
+[+] affected symbols: 9 (5.2%)</span>
+<div id="c_131" style="display:none;">
+<div class='affected'><span class='iname_a'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>siblings</span></span>, <span>struct lys_node const* <i>schema</i></span>, <span>char const* <i>key_or_value</i></span>, <span>struct lyd_node** <i>match</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;siblings&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Symbol_Source_Problems_Medium'></a><a name='Interface_Source_Problems_Medium'></a>
+<h2>Problems with Symbols, Medium Severity <span class='failed'>&nbsp;47&nbsp;</span></h2><hr/>
+<span class='h_name'>dict.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_132')">
+<span class='ext'>[+]</span> lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_132" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>, <span>char const** <span class='color_p'>str_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>str_p</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_133')">
+<span class='ext'>[+]</span> lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_133" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>, <span>char const** <span class='color_p'>str_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>str_p</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_134')">
+<span class='ext'>[+]</span> ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>int <span class='color_p'>implemented</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_134" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>4th</b> parameter <b>implemented</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_135')">
+<span class='ext'>[+]</span> ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_135" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char const** <span class='color_p'>features</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>features</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_136')">
+<span class='ext'>[+]</span> ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_136" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct ly_ctx** <span class='color_p'>new_ctx</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>new_ctx</b> of type <span class='value'>struct ly_ctx**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_137')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_137" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>index_p</b> of type <b>uint32_t*</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_138')">
+<span class='ext'>[+]</span> ly_set_clean&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_138" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_clean&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='nowrap'><b>int</b> (<b>4</b> bytes)</span> to <b>void</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_139')">
+<span class='ext'>[+]</span> ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_139" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>index_p</b> of type <b>uint32_t*</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_140')">
+<span class='ext'>[+]</span> ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_140" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void*(*<span class='color_p'>duplicator</span>)(void*)</span>, <span>struct ly_set** <span class='color_p'>newset_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>duplicator</b> of type <b>void*(*)(void*)</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Parameter <b>newset_p</b> of type <span class='value'>struct ly_set**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_141')">
+<span class='ext'>[+]</span> ly_set_free&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_141" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_free&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_142')">
+<span class='ext'>[+]</span> ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_142" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>void*(*<span class='color_p'>duplicator</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>duplicator</b> of type <b>void*(*)(void*)</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_143')">
+<span class='ext'>[+]</span> ly_set_new&#160;<span class='sym_p'>(&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_143" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_new&#160;<span class='sym_p'><span>(&#160;struct ly_set** <span class='color_p'>set_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>set_p</b> of type <span class='value'>struct ly_set**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_144')">
+<span class='ext'>[+]</span> ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_144" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_145')">
+<span class='ext'>[+]</span> ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>unsigned int <span class='color_p'>index</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_145" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>uint32_t <span class='color_p'>index</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>destructor</b> of type <b>void(*)(void*)</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_146')">
+<span class='ext'>[+]</span> lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_146" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lysc_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>size_t <span class='color_p'>val_len</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Base type of parameter <b>schema</b> has been changed from <span class='value'>struct lys_node</span> to <span class='value'>struct lysc_node</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_147')">
+<span class='ext'>[+]</span> lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_147" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>, <span>struct lyd_node** <span class='color_p'>first</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>first</b> of type <span class='value'>struct lyd_node**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of <b>1st</b> parameter <b>sibling</b> has been increased from <b>2</b> to <b>1</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_148')">
+<span class='ext'>[+]</span> lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>data_tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_148" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>The pointer level of <b>6th</b> parameter <b>options</b> has been increased from <b>0</b> to <b>2</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_149')">
+<span class='ext'>[+]</span> lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span> <span class='failed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_149" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>, <span>enum LYD_PATH_TYPE <span class='color_p'>pathtype</span></span>, <span>char* <span class='color_p'>buffer</span></span>, <span>size_t <span class='color_p'>buflen</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>pathtype</b> of type <span class='value'>enum LYD_PATH_TYPE</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Parameter <b>buffer</b> of type <b>char*</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Parameter <b>buflen</b> of type <b>size_t</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_150')">
+<span class='ext'>[+]</span> lys_find_path&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>cur_module</span></span>, <span>struct lys_node const* <span class='color_p'>cur_node</span></span>, <span>char const* <span class='color_p'>path</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_150" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_find_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>struct lysc_node const* <span class='color_p'>ctx_node</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>ly_bool <span class='color_p'>output</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>output</b> of type <b>ly_bool</b> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Base type of return value has been changed from <span class='value'>struct ly_set</span> to <span class='value'>struct lysc_node</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_151')">
+<span class='ext'>[+]</span> lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>last</span></span>, <span>struct lys_node const* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_151" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>last</span></span>, <span>struct lysc_node const* <span class='color_p'>parent</span></span>, <span>struct lysc_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Base type of parameter <b>last</b> has been changed from <span class='value'>struct lys_node</span> to <span class='value'>struct lysc_node</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Base type of parameter <b>module</b> has been changed from <span class='value'>struct lys_module</span> to <span class='value'>struct lysc_module</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Base type of parameter <b>parent</b> has been changed from <span class='value'>struct lys_node</span> to <span class='value'>struct lysc_node</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Base type of return value has been changed from <span class='value'>struct lys_node</span> to <span class='value'>struct lysc_node</span> of different format.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_152')">
+<span class='ext'>[+]</span> lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_152" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>module</b> of type <span class='value'>struct lys_module const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_153')">
+<span class='ext'>[+]</span> lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_153" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>module</b> of type <span class='value'>struct lys_module const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_154')">
+<span class='ext'>[+]</span> lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='failed'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_154" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>, <span>struct lys_module const** <span class='color_p'>module</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>module</b> of type <span class='value'>struct lys_module const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The pointer level of return value has been increased from <b>1</b> to <b>0</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_155')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_155" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>6th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_156')">
+<span class='ext'>[+]</span> lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_156" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_157')">
+<span class='ext'>[+]</span> lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_157" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_158')">
+<span class='ext'>[+]</span> lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_158" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_159')">
+<span class='ext'>[+]</span> lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_159" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>5th</b> parameter <b>line_length</b> has been removed from the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_160')">
+<span class='ext'>[+]</span> lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span> <span class='failed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_160" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module* <span class='color_p'>mod</span></span>, <span>char const** <span class='color_p'>features</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Parameter <b>features</b> of type <span class='value'>char const**</span> has been added to the calling stack.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Low_Risk_Source_Problems'></a><a name='Type_Source_Problems_Low'></a>
+<h2>Problems with Data Types, Low Severity <span class='warning'>&nbsp;9&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_161')">
+<span class='ext'>[+]</span> <span class='ttype'>typedef</span> ly_module_imp_clb <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_161" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Base type has been changed from <span class='value'>char const*(*)(char const*, char const*, char const*, char const*, void*, LYS_INFORMAT*, void(**)(void*, void*))</span> to <span class='value'>LY_ERR(*)(char const*, char const*, char const*, char const*, void*, LYS_INFORMAT*, char const**, ly_module_imp_data_free_clb*)</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_162')">
+[+] affected symbols: 2 (1.1%)</span>
+<div id="c_162" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module_imp_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>void** <i>user_data</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value is of type &#39;ly_module_imp_clb&#39;.</div>
+<span class='iname_a'>ly_ctx_set_module_imp_clb&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const*(*<span class='fp'>clb</span>)(char const*, char const*, char const*, char const*, void*, LYS_INFORMAT*, void(**)(void*, void*))</span>, <span>void* <i>user_data</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;clb&#39; is of type &#39;ly_module_imp_clb&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_163')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lyd_node <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_163" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>flags</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>meta</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_164')">
+[+] affected symbols: 9 (5.2%)</span>
+<div id="c_164" style="display:none;">
+<div class='affected'><span class='iname_a'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>siblings</span></span>, <span>struct lys_node const* <i>schema</i></span>, <span>char const* <i>key_or_value</i></span>, <span>struct lyd_node** <i>match</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;siblings&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_165')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lys_module <span class='warning'>&nbsp;6&nbsp;</span></span>
+<br/>
+<div id="c_165" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Field <b>augmented_by</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Field <b>compiled</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Field <b>deviated_by</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Field <b>identities</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>Field <b>parsed</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>Field <b>revision</b> has been added to this type.</td>
+<td>This field will not be initialized or used by old client applications.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_166')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_166" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>int <i>implemented</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>uint32_t* <i>idx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Symbol_Source_Problems_Low'></a><a name='Interface_Source_Problems_Low'></a>
+<h2>Problems with Symbols, Low Severity <span class='warning'>&nbsp;52&nbsp;</span></h2><hr/>
+<span class='h_name'>dict.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_167')">
+<span class='ext'>[+]</span> lydict_insert&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>size_t <span class='color_p'>len</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_167" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>char const*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_168')">
+<span class='ext'>[+]</span> lydict_insert_zc&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char* <span class='color_p'>value</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_168" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>char const*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_169')">
+<span class='ext'>[+]</span> ly_ctx_destroy&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lys_node const*, void*)</span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_169" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_destroy&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>void(*<span class='color_p'>private_destructor</span>)(struct lysc_node const*, void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Base type of <b>2nd</b> parameter <b>private_destructor</b> has been changed from <span class='value'>void(*)(struct lys_node const*, void*)</span> to <span class='value'>void(*)(struct lysc_node const*, void*)</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_170')">
+<span class='ext'>[+]</span> ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>int <span class='color_p'>implemented</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_170" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct lys_module const*</span> to <span class='value'>struct lys_module*</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_171')">
+<span class='ext'>[+]</span> ly_ctx_get_options&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_171" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <b>uint16_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_172')">
+<span class='ext'>[+]</span> ly_ctx_internal_modules_count&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_172" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>unsigned int</span> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_173')">
+<span class='ext'>[+]</span> ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_173" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_new&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>search_dir</span></span>, <span>uint16_t <span class='color_p'>options</span></span>, <span>struct ly_ctx** <span class='color_p'>new_ctx</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint16_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <span class='value'>struct ly_ctx*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_174')">
+<span class='ext'>[+]</span> ly_ctx_set_searchdir&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>search_dir</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_174" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_175')">
+<span class='ext'>[+]</span> ly_get_log_clb&#160;<span class='sym_p'>(&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_175" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>void(*)(LY_LOG_LEVEL, char const*, char const*)</span> to <b>ly_log_clb</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_176')">
+<span class='ext'>[+]</span> ly_log_options&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>opts</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_176" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_log_options&#160;<span class='sym_p'><span>(&#160;uint32_t <span class='color_p'>opts</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>opts</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_177')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_177" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_178')">
+<span class='ext'>[+]</span> ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_178" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <b>ly_bool</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_179')">
+<span class='ext'>[+]</span> ly_set_dup&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_179" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct ly_set*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_180')">
+<span class='ext'>[+]</span> ly_set_log_clb&#160;<span class='sym_p'><span>(&#160;void(*<span class='color_p'>clb</span>)(LY_LOG_LEVEL, char const*, char const*)</span>, <span>int <span class='color_p'>path</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_180" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_log_clb&#160;<span class='sym_p'><span>(&#160;ly_log_clb <span class='color_p'>clb</span></span>, <span>ly_bool <span class='color_p'>path</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>path</b> has been changed from <b>int</b> to <b>ly_bool</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_181')">
+<span class='ext'>[+]</span> ly_set_merge&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>trg</span></span>, <span>struct ly_set* <span class='color_p'>src</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_181" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_182')">
+<span class='ext'>[+]</span> ly_set_new&#160;<span class='sym_p'>(&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_182" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct ly_set*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_183')">
+<span class='ext'>[+]</span> ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_183" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_184')">
+<span class='ext'>[+]</span> ly_set_rm_index&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>unsigned int <span class='color_p'>index</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_184" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_185')">
+<span class='ext'>[+]</span> lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>siblings</span></span>, <span>struct lys_node const* <span class='color_p'>schema</span></span>, <span>char const* <span class='color_p'>key_or_value</span></span>, <span>struct lyd_node** <span class='color_p'>match</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_185" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_186')">
+<span class='ext'>[+]</span> lyd_insert_after&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_186" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_187')">
+<span class='ext'>[+]</span> lyd_insert_before&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_187" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_188')">
+<span class='ext'>[+]</span> lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node** <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_188" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_insert_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>sibling</span></span>, <span>struct lyd_node* <span class='color_p'>node</span></span>, <span>struct lyd_node** <span class='color_p'>first</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>1st</b> parameter <b>sibling</b> has been changed from <span class='value'>struct lyd_node**</span> to <span class='value'>struct lyd_node*</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_189')">
+<span class='ext'>[+]</span> lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_189" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>unsigned int</span> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_190')">
+<span class='ext'>[+]</span> lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>data_tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;4&nbsp;</span></span>
+<br/>
+<div id="c_190" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>6th</b> parameter <b>options</b> has been changed from <b>int</b> to <span class='value'>struct lyd_node**</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of <b>4th</b> parameter <b>value</b> has been changed from <b>void*</b> to <span class='value'>char const*</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Type of <b>5th</b> parameter <b>value_type</b> has been changed from <span class='value'>enum LYD_ANYDATA_VALUETYPE</span> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>Type of return value has been changed from <span class='value'>struct lyd_node*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_191')">
+<span class='ext'>[+]</span> lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_191" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>5th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_192')">
+<span class='ext'>[+]</span> lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_192" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_193')">
+<span class='ext'>[+]</span> lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_193" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_194')">
+<span class='ext'>[+]</span> lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_194" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_195')">
+<span class='ext'>[+]</span> lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_195" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_196')">
+<span class='ext'>[+]</span> lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lys_node const* <span class='color_p'>last</span></span>, <span>struct lys_node const* <span class='color_p'>parent</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_196" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_getnext&#160;<span class='sym_p'><span>(&#160;struct lysc_node const* <span class='color_p'>last</span></span>, <span>struct lysc_node const* <span class='color_p'>parent</span></span>, <span>struct lysc_module const* <span class='color_p'>module</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>4th</b> parameter <b>options</b> has been changed from <b>int</b> to <b>uint32_t</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_197')">
+<span class='ext'>[+]</span> lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>int <span class='color_p'>fd</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_197" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct lys_module const*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_198')">
+<span class='ext'>[+]</span> lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>data</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_198" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct lys_module const*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_199')">
+<span class='ext'>[+]</span> lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>enum LYS_INFORMAT <span class='color_p'>format</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_199" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <span class='value'>struct lys_module const*</span> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_200')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_200" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_201')">
+<span class='ext'>[+]</span> lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <span class='color_p'>fd</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_201" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_202')">
+<span class='ext'>[+]</span> lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <span class='color_p'>f</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_202" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_203')">
+<span class='ext'>[+]</span> lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <span class='color_p'>strp</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_203" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_204')">
+<span class='ext'>[+]</span> lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <span class='color_p'>path</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_204" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_205')">
+<span class='ext'>[+]</span> lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <span class='color_p'>searchpaths</span></span>, <span>int <span class='color_p'>cwd</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char** <span class='color_p'>localfile</span></span>, <span>LYS_INFORMAT* <span class='color_p'>format</span></span>&#160;)</span> <span class='warning'>&nbsp;2&nbsp;</span></span>
+<br/>
+<div id="c_205" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <span class='color_p'>searchpaths</span></span>, <span>ly_bool <span class='color_p'>cwd</span></span>, <span>char const* <span class='color_p'>name</span></span>, <span>char const* <span class='color_p'>revision</span></span>, <span>char** <span class='color_p'>localfile</span></span>, <span>LYS_INFORMAT* <span class='color_p'>format</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of <b>2nd</b> parameter <b>cwd</b> has been changed from <b>int</b> to <b>ly_bool</b>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_206')">
+<span class='ext'>[+]</span> lys_set_implemented&#160;<span class='sym_p'><span>(&#160;struct lys_module const* <span class='color_p'>module</span></span>&#160;)</span> <span class='warning'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_206" style="display:none;">
+
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td>Type of return value has been changed from <b>int</b> to <span class='value'>enum LY_ERR</span>.</td>
+<td>Recompilation of a client program may be broken.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Other_Source_Changes'></a><a name='Other_Source_Changes_In_Types'></a>
+<h2>Other Changes in Data Types <span class='passed'>&nbsp;24&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_207')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LY_ERR <span class='passed'>&nbsp;9&nbsp;</span></span>
+<br/>
+<div id="c_207" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The member <b>LY_EDENIED</b> with value <b>8</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The member <b>LY_EEXIST</b> with value <b>4</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>The member <b>LY_EINCOMPLETE</b> with value <b>9</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>The member <b>LY_ENOT</b> with value <b>10</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>The member <b>LY_ENOTFOUND</b> with value <b>5</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>The member <b>LY_EOTHER</b> with value <b>11</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>7</th>
+<td>Value of member <b>LY_EINT</b> has been changed from <b>4</b> to <b>6</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>8</th>
+<td>Value of member <b>LY_EPLUGIN</b> has been changed from <b>6</b> to <b>128</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>9</th>
+<td>Value of member <b>LY_EVALID</b> has been changed from <b>5</b> to <b>7</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_208')">
+[+] affected symbols: 3 (1.7%)</span>
+<div id="c_208" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_err_clean&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.no&#39; in 2nd parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+<span class='iname_a'>ly_err_first&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;retval.no&#39; in the return value (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+<span class='iname_a'>ly_err_print&#160;<span class='sym_p'><span>(&#160;struct ly_err_item* <span class='fp'>eitem</span></span>&#160;)</span></span><br/>
+<div class='affect'>Field &#39;eitem.no&#39; in 1st parameter &#39;eitem&#39; (pointer) is of type &#39;enum LY_ERR&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_209')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lyd_node <span class='passed'>&nbsp;5&nbsp;</span></span>
+<br/>
+<div id="c_209" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The relative position of field <b>hash</b> has been changed from <b>4</b> to <b>0</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The relative position of field <b>next</b> has been changed from <b>1</b> to <b>3</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>The relative position of field <b>parent</b> has been changed from <b>3</b> to <b>2</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>The relative position of field <b>prev</b> has been changed from <b>2</b> to <b>4</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>The relative position of field <b>schema</b> has been changed from <b>0</b> to <b>1</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_210')">
+[+] affected symbols: 9 (5.2%)</span>
+<div id="c_210" style="display:none;">
+<div class='affected'><span class='iname_a'>lyd_find_sibling_val&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>siblings</span></span>, <span>struct lys_node const* <i>schema</i></span>, <span>char const* <i>key_or_value</i></span>, <span>struct lyd_node** <i>match</i></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;siblings&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_first_sibling&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='fp'>node</span></span>&#160;)</span></span><br/>
+<div class='affect'>1st parameter &#39;node&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+<span class='iname_a'>lyd_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lyd_node const* <span class='fp'>root</span></span>, <span>enum LYD_FORMAT <i>format</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;root&#39; (pointer) has base type &#39;struct lyd_node&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_211')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_INFORMAT <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_211" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>Value of member <b>LYS_IN_YIN</b> has been changed from <b>2</b> to <b>3</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_212')">
+[+] affected symbols: 4 (2.3%)</span>
+<div id="c_212" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_parse_fd&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>int <i>fd</i></span>, <span>enum LYS_INFORMAT <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_INFORMAT&#39;.</div>
+<span class='iname_a'>lys_parse_mem&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>data</i></span>, <span>enum LYS_INFORMAT <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_INFORMAT&#39;.</div>
+<span class='iname_a'>lys_parse_path&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>path</i></span>, <span>enum LYS_INFORMAT <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_INFORMAT&#39;.</div>
+<span class='iname_a'>lys_search_localfile&#160;<span class='sym_p'><span>(&#160;char const*const* <i>searchpaths</i></span>, <span>int <i>cwd</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>char** <i>localfile</i></span>, <span>LYS_INFORMAT* <span class='fp'>format</span></span>&#160;)</span></span><br/>
+<div class='affect'>6th parameter &#39;format&#39; (pointer) has base type &#39;enum LYS_INFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_213')">
+<span class='ext'>[+]</span> <span class='ttype'>enum</span> LYS_OUTFORMAT <span class='passed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_213" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The member <b>LYS_OUT_YANG_COMPILED</b> with value <b>2</b> has been added.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>Value of member <b>LYS_OUT_TREE</b> has been changed from <b>3</b> to <b>4</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>Value of member <b>LYS_OUT_YIN</b> has been changed from <b>2</b> to <b>3</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_214')">
+[+] affected symbols: 5 (2.9%)</span>
+<div id="c_214" style="display:none;">
+<div class='affected'><span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>4th parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <i>module</i></span>, <span>enum LYS_OUTFORMAT <span class='fp'>format</span></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;format&#39; is of type &#39;enum LYS_OUTFORMAT&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<span class="section" onclick="javascript:showContent(this, 'c_215')">
+<span class='ext'>[+]</span> <span class='ttype'>struct</span> lys_module <span class='passed'>&nbsp;6&nbsp;</span></span>
+<br/>
+<div id="c_215" style="display:none;">
+<table class='ptable'><tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th></tr><tr>
+<th>1</th>
+<td>The relative position of field <b>dsc</b> has been changed from <b>3</b> to <b>7</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>2</th>
+<td>The relative position of field <b>filepath</b> has been changed from <b>7</b> to <b>4</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>3</th>
+<td>The relative position of field <b>latest_revision</b> has been changed from <b>9</b> to <b>10</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>4</th>
+<td>The relative position of field <b>ns</b> has been changed from <b>10</b> to <b>2</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>5</th>
+<td>The relative position of field <b>prefix</b> has been changed from <b>2</b> to <b>3</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>6</th>
+<td>The relative position of field <b>ref</b> has been changed from <b>4</b> to <b>8</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<span class="sect_aff" onclick="javascript:showContent(this, 'c_216')">
+[+] affected symbols: 8 (4.6%)</span>
+<div id="c_216" style="display:none;">
+<div class='affected'><span class='iname_a'>ly_ctx_get_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>, <span>int <i>implemented</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <i>ctx</i></span>, <span>uint32_t* <i>idx</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>ly_ctx_load_module&#160;<span class='sym_p'><span>(&#160;struct ly_ctx* <i>ctx</i></span>, <span>char const* <i>name</i></span>, <span>char const* <i>revision</i></span>&#160;)</span></span><br/>
+<div class='affect'>Return value (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<i>writeclb</i>)(void*, void const*, size_t)</span>, <span>void* <i>arg</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>3rd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_fd&#160;<span class='sym_p'><span>(&#160;int <i>fd</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_file&#160;<span class='sym_p'><span>(&#160;FILE* <i>f</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_mem&#160;<span class='sym_p'><span>(&#160;char** <i>strp</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+<span class='iname_a'>lys_print_path&#160;<span class='sym_p'><span>(&#160;char const* <i>path</i></span>, <span>struct lys_module const* <span class='fp'>module</span></span>, <span>enum LYS_OUTFORMAT <i>format</i></span>, <span>char const* <i>target_node</i></span>, <span>int <i>line_length</i></span>, <span>int <i>options</i></span>&#160;)</span></span><br/>
+<div class='affect'>2nd parameter &#39;module&#39; (pointer) has base type &#39;struct lys_module&#39;.</div>
+</div>
+</div>
+<br/><br/></div>
+
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Other_Source_Changes_In_Symbols'></a><a name='Other_Source_Changes_In_Interfaces'></a>
+<h2>Other Changes in Symbols <span class='passed'>&nbsp;10&nbsp;</span></h2><hr/>
+<span class='h_name'>libyang.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_217')">
+<span class='ext'>[+]</span> ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t* <span class='color_p'>idx</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_217" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_ctx_get_module_iter&#160;<span class='sym_p'><span>(&#160;struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>uint32_t* <span class='color_p'>index</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>idx</b> has been renamed to <b>index</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_218')">
+<span class='ext'>[+]</span> ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_218" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_add&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>ly_bool <span class='color_p'>list</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>node</b> has been renamed to <b>object</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_219')">
+<span class='ext'>[+]</span> ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_219" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_contains&#160;<span class='sym_p'><span>(&#160;struct ly_set const* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>uint32_t* <span class='color_p'>index_p</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>node</b> has been renamed to <b>object</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_220')">
+<span class='ext'>[+]</span> ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>node</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_220" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>ly_set_rm&#160;<span class='sym_p'><span>(&#160;struct ly_set* <span class='color_p'>set</span></span>, <span>void* <span class='color_p'>object</span></span>, <span>void(*<span class='color_p'>destructor</span>)(void*)</span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>node</b> has been renamed to <b>object</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_data.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_221')">
+<span class='ext'>[+]</span> lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>node</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_221" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_list_pos&#160;<span class='sym_p'><span>(&#160;struct lyd_node const* <span class='color_p'>instance</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>1st</b> parameter <b>node</b> has been renamed to <b>instance</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_222')">
+<span class='ext'>[+]</span> lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>data_tree</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>void* <span class='color_p'>value</span></span>, <span>enum LYD_ANYDATA_VALUETYPE <span class='color_p'>value_type</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='passed'>&nbsp;3&nbsp;</span></span>
+<br/>
+<div id="c_222" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_new_path&#160;<span class='sym_p'><span>(&#160;struct lyd_node* <span class='color_p'>parent</span></span>, <span>struct ly_ctx const* <span class='color_p'>ctx</span></span>, <span>char const* <span class='color_p'>path</span></span>, <span>char const* <span class='color_p'>value</span></span>, <span>uint32_t <span class='color_p'>options</span></span>, <span>struct lyd_node** <span class='color_p'>node</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>1st</b> parameter <b>data_tree</b> has been renamed to <b>parent</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>2</th>
+<td><b>5th</b> parameter <b>value_type</b> has been renamed to <b>options</b>.</td>
+<td>No effect.</td>
+</tr>
+<tr>
+<th>3</th>
+<td><b>6th</b> parameter <b>options</b> has been renamed to <b>node</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<span class="section" onclick="javascript:showContent(this, 'c_223')">
+<span class='ext'>[+]</span> lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_223" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lyd_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lyd_node const* <span class='color_p'>root</span></span>, <span>enum LYD_FORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>arg</b> has been renamed to <b>user_data</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<span class='h_name'>tree_schema.h</span><br/>
+<span class="section" onclick="javascript:showContent(this, 'c_224')">
+<span class='ext'>[+]</span> lys_print_clb&#160;<span class='sym_p'><span>(&#160;ssize_t(*<span class='color_p'>writeclb</span>)(void*, void const*, size_t)</span>, <span>void* <span class='color_p'>arg</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>char const* <span class='color_p'>target_node</span></span>, <span>int <span class='color_p'>line_length</span></span>, <span>int <span class='color_p'>options</span></span>&#160;)</span> <span class='passed'>&nbsp;1&nbsp;</span></span>
+<br/>
+<div id="c_224" style="display:none;">
+
+
+<span class='new_sign_lbl'>&#8675;</span>
+<br/>
+<span class='new_sign'>lys_print_clb&#160;<span class='sym_p'><span>(&#160;ly_write_clb <span class='color_p'>writeclb</span></span>, <span>void* <span class='color_p'>user_data</span></span>, <span>struct lys_module const* <span class='color_p'>module</span></span>, <span>enum LYS_OUTFORMAT <span class='color_p'>format</span></span>, <span>uint32_t <span class='color_p'>options</span></span>&#160;)</span></span><br/>
+<table class='ptable'>
+<tr>
+<th class='pn'></th>
+<th class='chg'>Change</th>
+<th>Effect</th>
+</tr>
+<tr>
+<th>1</th>
+<td><b>2nd</b> parameter <b>arg</b> has been renamed to <b>user_data</b>.</td>
+<td>No effect.</td>
+</tr>
+</table>
+<br/>
+</div>
+<br/>
+<a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Headers'></a><h2>Header Files <span class='gray'>&nbsp;7&nbsp;</span></h2><hr/>
+<div class='h_list'>
+dict.h<br/>
+extensions.h<br/>
+libyang.h<br/>
+tree_data.h<br/>
+tree_schema.h<br/>
+user_types.h<br/>
+xml.h<br/>
+</div>
+<br/><a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Sources'></a><h2>Source Files <span class='gray'>&nbsp;11&nbsp;</span></h2><hr/>
+<div class='h_list'>
+common.c<br/>
+context.c<br/>
+hash_table.c<br/>
+log.c<br/>
+parser_lyb.c<br/>
+parser_xml.c<br/>
+plugins.c<br/>
+printer.c<br/>
+tree_data.c<br/>
+tree_schema.c<br/>
+xml.c<br/>
+</div>
+<br/><a class='top_ref' href='#Top'>to the top</a><br/>
+<a name='Libs'></a><h2>Objects <span class='gray'>&nbsp;1&nbsp;</span></h2><hr/>
+<div class='lib_list'>
+libyang.so.1.9.19<br/>
+</div>
+<br/><a class='top_ref' href='#Top'>to the top</a><br/>
+<br/><br/><br/></div><hr/>
+<div class='footer' align='right'><i>Generated by <a href='https://github.com/lvc/abi-compliance-checker'>ABI Compliance Checker</a> 2.3 &#160;</i>
+</div>
+<br/>
+
+</body></html>
diff --git a/doc/logo.png b/doc/logo.png
new file mode 100644
index 0000000..13878b7
--- /dev/null
+++ b/doc/logo.png
Binary files differ
diff --git a/doc/transition.dox b/doc/transition.dox
new file mode 100644
index 0000000..fb0c638
--- /dev/null
+++ b/doc/transition.dox
@@ -0,0 +1,408 @@
+/**
+ * @page transition Transition Manual (1.x -> 2.0)
+ *
+ * [TOC]
+ *
+ * Rewriting libyang codebase and creating libyang version 2.0 was motivated mainly by improving long term maintainability.
+ * Especially some of the features and design decisions become killers for further development and maintaining the libyang
+ * codebase. On the other hand, most of the principles introduced in libyang 1.x to handle YANG schemas and manipulate
+ * instantiated data have proved successful. Therefore, we have decided to keep the basic mechanisms from version 1.x and
+ * remove the problematic features and modify the improper design decisions. Despite the vision to keep with the mechanisms
+ * also the API, the new version became a great opportunity to clean up the API and improve its usability mainly by unifying
+ * how the libyang functions are used. So, in the end, this manual is not just a description of the removed features listing
+ * removed, modified or added functions. The API should be even better prepared for adding new features and functions.
+ * Shortly, almost everything has changed at least a little, so you cannot move from version 1.x to 2.0 just by replacing
+ * code snippets. However, we believe that the change is good and libyang 2.0 is simply better.
+ *
+ * In this Manual, we want to introduce the differences between libyang 1.x and 2.0. It is intended for the transition
+ * from 1.x to 2.0, so if you are new to libyang, simply go to the @ref howto section, this Manual is not for you.
+ *
+ * The complete generated list of changes (without any additional notes) can be found in
+ * <a href="../compat_report.html">the compatibility report</a>.
+ *
+ * @section transitionGeneral General Changes
+ *
+ *
+ * @subsection transitionGeneralErros Errors Handling
+ *
+ * The most visible change is probably the changed approach to handling errors. While libyang 1.x was using variable
+ * *ly_errno* to provide error information in case the called function failed, there is no such variable in libyang 2.0.
+ * On the other hand, most API functions now return ::LY_ERR values directly stating the result. In addition, in case
+ * the error is somehow connected with the [context](@ref howtoContext), more detailed error information can be obtained
+ * from the context handler itself. Mainly this change is responsible for the backward incompatibility of almost all
+ * functions between versions 1.x and 2.0.
+ *
+ * Here is the overview of the changed / removed / added functions connected with errors and logging.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :-------------------------|:-------------------------------------|:-------------------------------------------------------
+ * - | ::ly_err_last() | New API for handling errors.
+ * - | ::ly_errcode() | ^
+ * ly_verb_dbg() | ::ly_log_dbg_groups() | Rename to align with the names of the accepted values.
+ * ly_verb() | ::ly_log_level() | ^
+ *
+ * More information about error handling in libyang 2.0 can be found at @ref howtoErrors page.
+ *
+ * @subsection transitionGeneralInOut Input / Output Processing
+ *
+ * libyang 2.0 introduces input (::ly_in) and output (::ly_out) handlers covering the specific input sources for parsers and
+ * output targets for printers. They are supposed mainly to simplify parser's and printer's API to avoid the need for
+ * separate functions for each source/target. The handlers can be used repeatedly to split the inputs or join the outputs.
+ *
+ * More information can be found at @ref howtoInput and @ref howtoOutput pages.
+ *
+ *
+ * @subsection transitionGeneralOutputFormatting Output Formatting
+ *
+ * In libyang 1.x, there was an inconsistency in printing schemas and data. While the schemas were always printed formatted,
+ * the data were printed by default without additional indentation. It is clearly visible in the YIN format of the schema,
+ * which is XML, and the XML encoding of the data. While there was a possibility to format data output with the LYP_FORMAT
+ * flag, it wasn't possible to change schema output formatting.
+ *
+ * libyang 2.0 unifies the behavior of all printers. By default, all the output formats are formatted now. Both, the data as
+ * well as the schema printers, accept the option to remove additional formatting (different for the specific format, usually
+ * indentations and blank lines): ::LYD_PRINT_SHRINK for the data printer and ::LYS_PRINT_SHRINK for the schema printer.
+ *
+ *
+ * @subsection transitionGeneralXPath Addressing
+ *
+ * If you compare the @ref howtoXPath page from libyang 1.x and 2.0 documentation, you will be probably confused since they
+ * seem very different. In fact, we have tried to simplify the API by removing the original Schema path format from the
+ * public API. Since this format is used in YANG format, libyang still supports it internally, but it is not possible to use
+ * it anywhere in the API. The new formats XPath and Path, mentioned at the @ref howtoXPath page, are both the Data paths
+ * used in libyang 1.x. The Path format is a subset of XPath format limited to refer to a single node.
+ *
+ * This change was reflected in functions serving to get a node based on the specified path. Unfortunately, when comparing
+ * old and new API, the transition can be confusing since the names are sometimes overloaded (be careful mainly of
+ * ::lys_find_path()). The following table depicts the changes, but probably a better approach is to handle the functions as
+ * a completely new API.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :-------------------------|:-------------------------------------|:-------------------------------------------------------
+ * ly_ctx_find_path() | - | To simplify the different [types of paths](@ref howtoXPath), the Schema path format is not supported for now. If there will be use cases for it, it can be re-added later, but for now try using ::lys_find_xpath().
+ * %lys_find_path() | - | To simplify the different [types of paths](@ref howtoXPath), the Schema path format is not supported for now. If there will be use cases for it, it can be re-added later, but for now try using ::lys_find_xpath().
+ * ly_ctx_get_node() | ::lys_find_path() | Renamed to unify API functions, note that there was lys_find_path in libyang 1.x with different functionality in comparison to the function of the same name from libyang 2.0.
+ * - | ::lys_find_path_atoms() | Extension of ::lys_find_path().
+ * - | ::lys_find_lypath_atoms() | ^
+ * - | ::lys_find_xpath() | New function reflecting updated @ref howtoXPath\.
+ * lys_xpath_atomize() | ::lys_find_xpath_atoms() | Rename to unify with the new API, extends ::lys_find_xpath().
+ * - | ::lys_find_expr_atoms() | Extension of ::lys_find_xpath().
+ * %lyd_path() | ::lyd_path() | Same purpose, just extended functionality.
+ * lyd_find_path() | ::lyd_find_xpath() | Rename to unify with the new API.
+ * - | ::lyd_find_path() | Simplified version of ::lyd_find_path().
+ * - | ::lyxp_get_expr() | Added functionality due to the changed representation of XPath expressions.
+ * ly_path_data2schema() | - | Removed since the schema path is not available in API.
+ * ly_path_xml2json() | - | Removed since done internally, note that even the generic XML parser is not available now.
+ * lys_node_xpath_atomize() | - | Removed as useless/redundant, use ::lys_find_xpath_atoms().
+ *
+ * @section transitionContext Context
+ *
+ * Context, as a concept of a storage interconnecting YANG modules into a YANG schema and YANG schema with the instantiated
+ * data, was preserved. However, it is now more supposed to be prepared just once before connecting it with any instantiated
+ * data. The possibility of removing YANG modules from the context was completely dropped. Furthermore, we would like to
+ * introduce some kind of context lock to completely abandon any change of the YANG modules after starting work with the
+ * instantiated data.
+ *
+ * Another change to note is the removed destructor of private data (::lysc_node.priv) in ::ly_ctx_destroy(). The mechanism
+ * was not reliable with the context recompilation and the splitted parsed and compiled schema trees or the complexity of
+ * YANG extensions. It is better to let the caller maintain the allocated data directly via some memory pool or using
+ * ::lysc_tree_dfs_full() since he is the best to know where the additional data were added.
+ *
+ * The meaining of the ::lysc_node.priv pointer can be now also changed by ::LY_CTX_SET_PRIV_PARSED context option, which
+ * makes libyang to connect the original parsed schema node structure (::lysp_node) into the compiled nodes via their 'priv'
+ * pointer.
+ *
+ * Other significant changes connected with the context are depicted in the following table.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :-------------------------|:-------------------------------------|:-------------------------------------------------------
+ * ly_ctx_clean() | - | Removed functionality of manipulating with the context and the modules already placed in the context.
+ * ly_ctx_remove_module() | - | ^
+ * ly_ctx_set_module_data_clb() and the associated ly_module_data_clb type. | - | ^
+ * ly_ctx_get_disabled_module() and the associated ly_ctx_get_disabled_module_iter() | - | ^
+ * ly_ctx_info() | ::ly_ctx_get_yanglib_data() | Clarification of what to expect as the output of the function and possibility to specify custom content ID.
+ * ly_ctx_get_module_set_id() | ::ly_ctx_get_change_count() | The functionality is the same but the exact meaning of the value was clarified.
+ * - | ::ly_ctx_unset_searchdir_last() | Extend the functionality of the ::ly_ctx_unset_searchdir() to make its use easier.
+ * ly_ctx_get_module_older() | - | Removed functionality.
+ * - | ::ly_ctx_get_module_implemented() | Supplement for ::ly_ctx_get_module()
+ * - | ::ly_ctx_get_module_latest() | ^
+ * - | ::ly_ctx_get_module_implemented_ns() | Supplement for ::ly_ctx_get_module_ns()
+ * - | ::ly_ctx_get_module_latest_ns() | ^
+ * - | ::ly_ctx_get_submodule_latest() | Supplement for ::ly_ctx_get_submodule()
+ * - | ::ly_ctx_get_submodule2_latest() | Supplement for ::ly_ctx_get_submodule2()
+ * ly_ctx_get_module_by_ns() | ::ly_ctx_get_module_ns () | Redesign the API - replace some of the parameters with standalone supplement functions.
+ * - | ::ly_ctx_reset_latests() | The new functionality of maintaining the latest module revision flag.
+ * ly_ctx_unset_searchdirs() | ::ly_ctx_unset_searchdir() | Simplify API and instead of index numbers, work with the values themselves.
+ * ly_ctx_set*() | ::ly_ctx_set_options() | API simplification.
+ * ly_ctx_unset*() | ::ly_ctx_unset_options() | ^
+ * ly_ctx_destroy() | ::ly_ctx_destroy() | The destructor callback parameter was removed, see the notes above.
+ *
+ *
+ * @section transitionSchemas YANG Modules (Schema)
+ *
+ * The most significant change between libyang 1.x and 2.0 can be found in schema structures. The schema tree now has two
+ * different forms - parsed and compiled trees. While the parsed tree reflects the source of the YANG module, the compiled
+ * tree represents the final tree used to validate the instantiated data. Both formats can be found inside the covering
+ * ::lys_module structure. More about the new structures can be found at @ref howtoSchema page.
+ *
+ * This is an essential change allowing speed up and simplification of data validation, but requiring carefully determine
+ * which format is more suitable for the specific use case. As mentioned, the compiled trees are better for data validation
+ * and getting information about the intentioned structure of the schema with all the applied groupings, type modifications,
+ * augments, deviations, and any defined if-features. On the other hand, the parsed trees are useful for the schema format conversions since they
+ * provide the original structure of the modules. There is a number of new functions intended to work only with the
+ * parsed or the compiled tree. These functions are prefixed with `lysp_` and `lysp_` prefixes.
+ *
+ * Schema parser, as well as printer functions, are now extended to accept new
+ * [input / output handlers](@ref transitionGeneralInOut). The previous API working directly with inputs and outputs is
+ * preserved (or slightly changed), but the functions can be limited in the functionality of the new API. More information
+ * can be found at @ref howtoSchemaParsers and @ref howtoSchemaPrinters pages.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * - | ::lys_parse() | New generic schema parser API using [generic input handler](@ref howtoInput).
+ * - | ::lys_print_module() | New generic schema printer API using [generic output handler](@ref howtoOutput).
+ * - | ::lys_print_node() | ^
+ * - | ::lys_print_submodule() | ^
+ *
+ *
+ * The following table introduces other significant changes in the API functions connected with the schema.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lys_set_private() | - | Not needed, all ::lysc_node compatible structures have this pointer so it can be accessed directly.
+ * lys_is_disabled() | - | Make no sense since the nodes disabled by if-feature are not present in the compiled tree.
+ * lys_features_list() | - | Not needed, the list of features is available in the parsed tree of the module and submodule.
+ * lys_features_enable(), lys_features_enable_force() | ::lys_set_implemented() | Set of enabled features can be changed but it causes the whole context (all the modules) to recompile.
+ * lys_features_disable(), lys_features_disable_force() | - | ^
+ * lys_features_state() | - | Redesign of the features handling in the schema tree, the feature's status is newly directly visible as ::LYS_FENABLED flag (in parsed feature structure).
+ * - | ::lys_feature_value() | Simplified API to get feature's state only based on a feature name string.
+ * - | ::lysp_feature_next() | After redesigning features handling, this function helps to iterate over all features connected with the module.
+ * lys_iffeature_value() | ::lysc_iffeature_value() | Renamed, but note that after features handling redesign, the compiled if-feature structure to evaluate is only in ::lysp_feature.iffeatures_c.
+ * lys_iffeature_value() | - | Not needed since the if-feature statements are directly applied onto the compiled tree.
+ * lys_is_disabled() | - | ^
+ * lys_parent() | - | The compiled tree is more straightforward without the need to take care of nodes added via augments.
+ * lys_main_module() | - | The compiled tree does not include submodules, so there is always only the main module.
+ * lys_node_module() | - | ^
+ * lys_set_enabled() | - | It is not possible to change context this way (remove or disable modules).
+ * lys_set_disabled() | - | ^
+ * - | ::lys_find_child() | Helpers wrapper around ::lys_getnext().
+ * - | ::lys_getnext_ext() | Alternative to ::lys_getnext() allowing processing schema trees inside extension instances.
+ * - | ::lys_nodetype2str() | New functionality.
+ * lys_is_key() | ::lysc_is_key() | Renamed to connect with the compiled schema tree.
+ * - | ::lysc_is_userordered() | Added functionality to simplify the examination of generic compiled schema nodes.
+ * - | ::lysc_is_np_cont() | ^
+ * - | ::lysc_node_child() | ^
+ * - | ::lysc_node_actions() | ^
+ * - | ::lysc_node_notifs() | ^
+ * - | ::lysp_node_child() | Added functionality to simplify the examination of generic parsed schema nodes.
+ * - | ::lysp_node_actions() | ^
+ * - | ::lysp_node_notifs() | ^
+ * - | ::lysp_node_groupings() | ^
+ * - | ::lysp_node_typedefs() | ^
+ * - | ::lysc_tree_dfs_full() | Alternative DFS passing implementation to ::LYSC_TREE_DFS_BEGIN macro.
+ * - | ::lysc_module_dfs_full() | Supplement functionality to ::lysc_tree_dfs_full().
+ * lys_path(), lys_data_path() | ::lysc_path() | Redesigned functionality.
+ * lys_data_path_pattern() | - | Removed as useless
+ * lys_implemented_module() | ::ly_ctx_get_module_implemented() | Removed, the same result can be simply achieved using ::ly_ctx_get_module_implemented().
+ *
+ * There is a set of functions available to implement data type plugins for storing and manipulating data values in a more
+ * natural way to the specific data type. For example, IPv4 address type is defined as a string with a pattern, but it is
+ * more effective and usual to store and handle IPv4 as a 32-bit number.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:---------------------------------|:--------------------------------------------------------
+ * lys_getnext_union_type() | - | Removed after the type representation redesign.
+ * - | ::lyplg_type_identity_isderived()| Helper functions for base types.
+ * - | ::lyplg_type_parse_dec64() | ^
+ * - | ::lyplg_type_parse_int() | ^
+ * - | ::lyplg_type_parse_uint() | ^
+ * - | ::lyplg_type_validate_patterns() | ^
+ * - | ::lyplg_type_validate_range() | ^
+ * - | ::lyplg_type_get_prefix() | Helper functions for processing prefixes in data values.
+ * - | ::lyplg_type_prefix_data_new() | ^
+ * - | ::lyplg_type_prefix_data_dup() | ^
+ * - | ::lyplg_type_prefix_data_free() | ^
+ *
+ *
+ * YANG extensions are supported via [extension plugins API](@ref pluginsExtensions) allowing to implement specific extension
+ * and load its support into libyang as a shared module. libyang implements several extensions on its own (see
+ * @ref howtoPluginsExtensions), but even these internal implementations use the same API. The API and [mechanism of loading
+ * external plugins](@ref howtoPlugins) changed a lot in contrast to libyang 1.x. The plugins are now loaded automatically
+ * with creating the first libyang context. The only public function to handle external plugins is ::lyplg_add().
+ *
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lys_ext_complex_get_substmt()| lysc_ext_substmt() | Changed design of the extensions and the way how it's substatements are accessed.
+ * lys_ext_instance_presence() | lysc_ext_substmt() | ^
+ * lys_ext_instance_substmt() | lysc_ext_substmt() | ^
+ * ly_clean_plugins() | - | Manipulating external plugins (from plugins directories) is now automatically connected with creating (first) and destroying (last) libyang contexts.
+ * ly_get_loaded_plugins() | - | ^
+ * ly_load_plugins() | - | ^
+ * ly_register_exts() | ::lyplg_add() | Redesigned to a common function for any plugin type.
+ * ly_register_types() | ::lyplg_add() | ^
+ *
+ *
+ * @section transitionData Data Instances
+ *
+ * Conceptually, the data tree did not change as much as the schema tree. There is still a generic ::lyd_node structure that
+ * maps according to the schema node's nodetype to some of the other lyd_node_* structures. All the data nodes were extended
+ * by hashes to improve performance when searching and processing data trees. The hashes are used for example in the
+ * lyd_find_* functions.
+ *
+ * All the data nodes are also implicitly ordered following the order of the schema nodes. This is the
+ * reason to change the insertion function. Only the user-ordered lists and leaf-lists can be now placed relative to other
+ * instances of the same list/leaf-list. Otherwise, the data instance is always inserted/created at the correct place beside
+ * the right sibling nodes.
+ *
+ * For any functions that are available on inputs of different scale (node, subtree, all siblings, whole data tree),
+ * there are several variants of the specific function with suffix specifying what exactly the processed input will be
+ * (_single, _tree, _siblings, _all). This way the behavior is always clear in the code.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * %lyd_insert_after() | ::lyd_insert_after() | Changed meaning by limiting applicability only to user-ordered list and leaf-list instances.
+ * %lyd_insert_before() | ::lyd_insert_before() | ^
+ * lyd_schema_sort() | - | Not necessary since the nodes are sorted implicitly.
+ *
+ *
+ * Parsing data instances in XML format is newly done directly, without any interstep. Therefore, complete XML API
+ * (lyxml_*() functions) from libyang 1.x was removed.
+ *
+ * Parser's API was simplified to avoid variadic parameters. The functions are newly split to parsed data, notifications,
+ * RPCs and replies to the RPCs. Similarly to the schema parsers, also the new data parser API works with
+ * [input handlers](@ref transitionGeneralInOut). The data printer's API was also extended to use new
+ * [output handlers](@ref transitionGeneralInOut). However, part of the previous API working directly with inputs
+ * and outputs was preserved. More information about the data parser and printer can be found at
+ * @ref howtoDataParsers and @ref howtoDataPrinters pages.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * - | ::lyd_parse_data() | Redesigned data tree parser.
+ * lyd_parse_xml() | - | XML tree format was removed.
+ * lyd_parse_fd() | ::lyd_parse_data_fd() | Renamed and limited to data trees only.
+ * lyd_parse_mem() | ::lyd_parse_data_mem() | ^
+ * lyd_parse_path() | ::lyd_parse_data_path() | ^
+ * - | ::lyd_parse_ext_data() | Separated function for parsing data trees matching a schema tree of the given extension instance.
+ * - | ::lyd_parse_op() | Separated function for parsing RPCs, actions, replies, and notifications.
+ * - | ::lyd_parse_ext_op() | Separated function for parsing RPCs, actions, replies, and notifications of the given extension instance.
+ * - | ::lyd_print_all() | New API accepting ::ly_out.
+ * - | ::lyd_print_tree() | ^
+ *
+ *
+ * Data validation is still done as part of the parser's process. The standalone functionality is available to check the
+ * result of data manipulation with the values of the terminal nodes or with the structure of the data tree. In contrast to
+ * libyang 1.x, adding the default nodes was made available as a standalone function to simplify the data manipulation
+ * process before the final validation.
+ *
+ * Many validation flags were removed because they became obsolete (LYD_OPT_DESTRUCT, LYD_OPT_WHENAUTODEL,
+ * LYD_OPT_NOEXTDEPS, LYD_OPT_DATA_NO_YANGLIB, LYD_OPT_VAL_DIFF) or we consider them redundant (LYD_OPT_OBSOLETE,
+ * LYD_OPT_NOSIBLINGS, LYD_OPT_DATA_ADD_YANGLIB). As for LYD_OPT_TRUSTED, this option was mostly replaced with
+ * ::LYD_PARSE_ONLY because both skip validation but with the significant difference that LYD_OPT_TRUSTED also sets
+ * the data node flags to be considered validated. The current option ::LYD_PARSE_ONLY does not do this because
+ * there is now much better support for working with non-validated data trees. Also, in case an invalid tree was marked
+ * as validated, it could lead to some undesired behavior, which is now avoided.
+ *
+ * Worth mentioning is a difference in the LYB format, which now also stores its validation flags. So, in case
+ * a validated data tree is stored, it is also loaded as validated.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lyd_validate() | ::lyd_validate_all(), ::lyd_validate_op() | Redesigned functionality.
+ * lyd_validate_modules() | ::lyd_validate_module() | ^
+ * lyd_validate_value(), lyd_value_type() | ::lyd_value_validate() | Redesigned functionality.
+ * - | ::lyd_new_implicit_all() | New in API to explicitly add default nodes, previously done only as part of the validation process.
+ * - | ::lyd_new_implicit_module() | Supplement functionality to ::lyd_new_implicit_all().
+ * - | ::lyd_new_implicit_tree() | ^
+ *
+ *
+ * The `diff` functionality was completely redesigned. Instead of the array of operations to transform one tree into another,
+ * the difference between two data trees has newly a form of the annotated data tree describing the differences. A number of
+ * functions to use the diff tree was added into API. To simply compare just nodes, there are the `compare` functions.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lyd_diff() | ::lyd_diff_tree() | Redesigned functionality.
+ * - | ::lyd_diff_siblings() | Supplement functionality to ::lyd_diff_tree().
+ * - | ::lyd_diff_apply_all() | ^
+ * - | ::lyd_diff_apply_module() | ^
+ * - | ::lyd_diff_merge_all() | ^
+ * - | ::lyd_diff_merge_module() | ^
+ * - | ::lyd_diff_merge_tree() | ^
+ * - | ::lyd_diff_reverse_all() | ^
+ * lyd_free_diff() | - | Removed.
+ * lyd_free_val_diff() | - | Removed.
+ * - | ::lyd_compare_single() | Added functionality.
+ * - | ::lyd_compare_meta() | Supplement functionality to ::lyd_compare_single()
+ * - | ::lyd_compare_siblings() | ^
+ *
+ *
+ * For now, the functionality of moving data between two different contexts is not implemented. If you have a real use case
+ * for this functionality, let us know.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lyd_dup() | ::lyd_dup_single() | Redesigned functionality.
+ * lyd_dup_withsiblings() | ::lyd_dup_siblings() | ^
+ * - | ::lyd_dup_meta_single() | Supplement functionality to ::lyd_dup_single().
+ * lyd_dup_to_ctx() | - | Transferring data from one context to another is not supported.
+ * lyd_merge() | ::lyd_merge_tree() | Renamed.
+ * - | ::lyd_merge_siblings() | Supplement functionality to ::lyd_merge_tree()
+ * lyd_merge_to_ctx() | - | Transferring data from one context to another is not supported.
+ *
+ *
+ * There is a significant change in the value structure in terminal nodes. Thanks to the changes in the schema tree,
+ * it is now much more straightforward to get the type of the value and work with the alternative representation of the value
+ * fitting better to its type. There is also a new API for the type plugins (see @ref howtoPluginsTypes). Even the base YANG
+ * data types are now implemented using this API and can be taken as examples for implementing derived data types.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lyd_new(), lyd_new_output() | ::lyd_new_inner(), ::lyd_new_list(), ::lyd_new_list2() | Redesigned functionality to better fit new lyd_node structures, creating RPC's output data is now done via a flag parameter of the functions.
+ * lyd_new_leaf(), lyd_new_output_leaf() | ::lyd_new_term() | ^
+ * lyd_new_anydata(), lyd_new_output_anydata() | ::lyd_new_any() | ^
+ * lyd_new_yangdata() | ::lyd_new_ext_inner(), ::lyd_new_ext_list(), ::lyd_new_ext_term(), ::lyd_new_ext_any() | Additional functionality to lyd_new_* functions to cover top-level nodes in extension instances.
+ * lyd_insert_attr() | ::lyd_new_meta() | Unify naming used in other functions with the similar functionality.
+ * - | ::lyd_new_attr() | Added functionality to store the data (temporarily) not connected with schema definitions.
+ * - | ::lyd_new_attr2() | ^
+ * - | ::lyd_new_opaq() | ^
+ * - | ::lyd_new_opaq2() | ^
+ * - | ::lyd_new_path2() | Supplement functionality to ::lyd_new_path().
+ * LYD_PATH_OPT_NOPARENTRET | ::lyd_new_path2() | ::lyd_new_path2() returns both the first parent and the wanted node.
+ * LYD_PATH_OPT_EDIT | ::LYD_NEW_PATH_OPAQ | This functionality was replaced by opaq nodes.
+ * - | ::lyd_new_ext_path() | Supplement functionality to ::lyd_new_path() covering data defined in extension instances.
+ * lyd_insert() | ::lyd_insert_child() | Renamed to better distinguish from ::lyd_insert_sibling().
+ * lyd_change_leaf() | ::lyd_change_term() | Align naming with changed names of data structures.
+ * - | ::lyd_change_meta() | Transferred functionality of ::lyd_change_term() to metadata.
+ * - | ::lyd_any_copy_value() | Added functionality.
+ * lyd_free() | ::lyd_free_tree() | Renamed.
+ * lyd_free_withsiblings() | ::lyd_free_all() | ^
+ * - | ::lyd_free_siblings() | Supplement functionality to ::lyd_free_siblings().
+ * lyd_free_attr() | ::lyd_free_attr_single() | Renamed.
+ * - | ::lyd_free_attr_siblings() | Supplement functionality to ::lyd_free_attr_single().
+ * - | ::lyd_free_meta_single() | Added functionality.
+ * - | ::lyd_free_meta_siblings() | ^
+ * lyd_unlink() | ::lyd_unlink_tree() | Renamed.
+ *
+ *
+ * Here is the table of other changes in data API.
+ *
+ * libyang 1.x | libyang 2.0 | Notes
+ * :----------------------------|:--------------------------------|:---------------------------------------------------------
+ * lyd_find_instance() | - | Removed.
+ * lyd_find_sibling() | - | Removed, functionality can be replaced by ::lyd_find_sibling_val().
+ * lyd_find_sibling_set() | - | ^
+ * - | ::lyd_find_sibling_first() | Alternative function to ::lyd_find_sibling_val().
+ * - | ::lyd_find_meta() | New functionality.
+ * lyd_wd_default() | ::lyd_is_default() | Renamed to unlink from with-default capability.
+ * - | ::lyd_parent() | New wrapper to get generic ::lyd_node pointer of the parent.
+ * - | ::lyd_child(), ::lyd_child_no_keys() | New wrapper to cover all children possibilities hidden behind a generic ::lyd_node structure.
+ * - | ::lyd_owner_module() | Added functionality.
+ * - | ::lyd_value_compare() | Added Functionality.
+ * - | ::lyd_target() | Added functionality for the instance-identifier representation.
+ * lyd_node_module() | - | Not necessary since the connection with the compiled modules is much more straightforward.
+ * lyd_leaf_type() | - | Not necessary since the real type information is much more clear from the new ::lyd_value.
+ * lyd_dec64_to_double() | - | Removed as useless.
+ * lyd_node_should_print() | - | ^
+
+ */
diff --git a/libyang.pc.in b/libyang.pc.in
new file mode 100644
index 0000000..72d1689
--- /dev/null
+++ b/libyang.pc.in
@@ -0,0 +1,11 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+
+Name: @PROJECT_NAME@
+Description: @LIBYANG_DESCRIPTION@
+Version: @LIBYANG_VERSION@
+Requires.private: libpcre2-8
+Libs: -L${libdir} -lyang
+Libs.private: -lpcre2-8
+Cflags: -I${includedir}
diff --git a/models/ietf-datastores@2018-02-14.h b/models/ietf-datastores@2018-02-14.h
new file mode 100644
index 0000000..b61d599
--- /dev/null
+++ b/models/ietf-datastores@2018-02-14.h
@@ -0,0 +1,226 @@
+unsigned char ietf_datastores_2018_02_14_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e, 0x31, 0x3b, 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, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x73,
+ 0x74, 0x6f, 0x72, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x70, 0x72,
+ 0x65, 0x66, 0x69, 0x78, 0x20, 0x64, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20,
+ 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x45, 0x54, 0x46, 0x20, 0x4e,
+ 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c,
+ 0x69, 0x6e, 0x67, 0x20, 0x28, 0x4e, 0x45, 0x54, 0x4d, 0x4f, 0x44, 0x29,
+ 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x47, 0x72, 0x6f,
+ 0x75, 0x70, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x57, 0x47, 0x20,
+ 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x74, 0x72, 0x61, 0x63,
+ 0x6b, 0x65, 0x72, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67,
+ 0x2f, 0x77, 0x67, 0x2f, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64, 0x2f, 0x3e,
+ 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x57, 0x47, 0x20, 0x4c, 0x69,
+ 0x73, 0x74, 0x3a, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f,
+ 0x3a, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64, 0x40, 0x69, 0x65, 0x74, 0x66,
+ 0x2e, 0x6f, 0x72, 0x67, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4d, 0x61,
+ 0x72, 0x74, 0x69, 0x6e, 0x20, 0x42, 0x6a, 0x6f, 0x72, 0x6b, 0x6c, 0x75,
+ 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74,
+ 0x6f, 0x3a, 0x6d, 0x62, 0x6a, 0x40, 0x74, 0x61, 0x69, 0x6c, 0x2d, 0x66,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4a, 0x75,
+ 0x65, 0x72, 0x67, 0x65, 0x6e, 0x20, 0x53, 0x63, 0x68, 0x6f, 0x65, 0x6e,
+ 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+ 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6a, 0x2e, 0x73, 0x63, 0x68,
+ 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x40, 0x6a,
+ 0x61, 0x63, 0x6f, 0x62, 0x73, 0x2d, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x74, 0x79, 0x2e, 0x64, 0x65, 0x3e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20,
+ 0x20, 0x50, 0x68, 0x69, 0x6c, 0x20, 0x53, 0x68, 0x61, 0x66, 0x65, 0x72,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a,
+ 0x70, 0x68, 0x69, 0x6c, 0x40, 0x6a, 0x75, 0x6e, 0x69, 0x70, 0x65, 0x72,
+ 0x2e, 0x6e, 0x65, 0x74, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4b, 0x65,
+ 0x6e, 0x74, 0x20, 0x57, 0x61, 0x74, 0x73, 0x65, 0x6e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6b, 0x77, 0x61,
+ 0x74, 0x73, 0x65, 0x6e, 0x40, 0x6a, 0x75, 0x6e, 0x69, 0x70, 0x65, 0x72,
+ 0x2e, 0x6e, 0x65, 0x74, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x52, 0x6f,
+ 0x62, 0x20, 0x57, 0x69, 0x6c, 0x74, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x3c, 0x72, 0x77, 0x69, 0x6c, 0x74, 0x6f, 0x6e, 0x40, 0x63, 0x69, 0x73,
+ 0x63, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 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, 0x59,
+ 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x6f, 0x66, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69,
+ 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
+ 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x38,
+ 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, 0x72, 0x73,
+ 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x20, 0x20, 0x41, 0x6c, 0x6c,
+ 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x52, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x69,
+ 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d,
+ 0x73, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20,
+ 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x2c, 0x20, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74,
+ 0x65, 0x64, 0x20, 0x70, 0x75, 0x72, 0x73, 0x75, 0x61, 0x6e, 0x74, 0x20,
+ 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6a,
+ 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
+ 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x42,
+ 0x53, 0x44, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x73,
+ 0x65, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x74,
+ 0x68, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x34, 0x2e, 0x63, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x27, 0x73,
+ 0x20, 0x4c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52,
+ 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x49,
+ 0x45, 0x54, 0x46, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x2e,
+ 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63,
+ 0x65, 0x6e, 0x73, 0x65, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x29, 0x2e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x76,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f,
+ 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x38, 0x33, 0x34, 0x32, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x66, 0x63, 0x2d, 0x65, 0x64, 0x69,
+ 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x69, 0x6e, 0x66, 0x6f,
+ 0x2f, 0x72, 0x66, 0x63, 0x38, 0x33, 0x34, 0x32, 0x29, 0x3b, 0x20, 0x73,
+ 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x46, 0x43, 0x20, 0x69,
+ 0x74, 0x73, 0x65, 0x6c, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x67, 0x61,
+ 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x30, 0x31, 0x38, 0x2d, 0x30, 0x32, 0x2d, 0x31, 0x34, 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, 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, 0x38, 0x33,
+ 0x34, 0x32, 0x3a, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20,
+ 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x44,
+ 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x41, 0x72, 0x63,
+ 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x28, 0x4e,
+ 0x4d, 0x44, 0x41, 0x29, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x49, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x74, 0x79, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 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, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x62,
+ 0x61, 0x73, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f,
+ 0x72, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65,
+ 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x73, 0x74, 0x6f, 0x72, 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, 0x41, 0x62, 0x73, 0x74, 0x72,
+ 0x61, 0x63, 0x74, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f,
+ 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
+ 0x79, 0x20, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 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, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x74, 0x79, 0x20, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61,
+ 0x74, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73,
+ 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e,
+ 0x61, 0x6c, 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, 0x63, 0x61, 0x6e, 0x64, 0x69,
+ 0x64, 0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73,
+ 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a,
+ 0x0a, 0x20, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20,
+ 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65,
+ 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 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,
+ 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x63, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61,
+ 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x74, 0x79, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x63,
+ 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 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, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64,
+ 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x64, 0x79, 0x6e, 0x61,
+ 0x6d, 0x69, 0x63, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
+ 0x73, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 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, 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x62, 0x61,
+ 0x73, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x20,
+ 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x70, 0x65, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73,
+ 0x74, 0x6f, 0x72, 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, 0x54, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f,
+ 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x54, 0x79, 0x70, 0x65, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x64, 0x65, 0x66, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f,
+ 0x72, 0x65, 0x2d, 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x74, 0x79, 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73,
+ 0x74, 0x6f, 0x72, 0x65, 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, 0x41,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d,
+ 0x0a, 0x7d, 0x0a, 0x00
+};
diff --git a/models/ietf-datastores@2018-02-14.yang b/models/ietf-datastores@2018-02-14.yang
new file mode 100644
index 0000000..9e875ab
--- /dev/null
+++ b/models/ietf-datastores@2018-02-14.yang
@@ -0,0 +1,117 @@
+module ietf-datastores {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-datastores";
+ prefix ds;
+
+ organization
+ "IETF Network Modeling (NETMOD) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netmod/>
+
+ WG List: <mailto:netmod@ietf.org>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+
+ Author: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Author: Phil Shafer
+ <mailto:phil@juniper.net>
+
+ Author: Kent Watsen
+ <mailto:kwatsen@juniper.net>
+
+ Author: Rob Wilton
+ <rwilton@cisco.com>";
+
+ description
+ "This YANG module defines a set of identities for identifying
+ datastores.
+
+ Copyright (c) 2018 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Simplified BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8342
+ (https://www.rfc-editor.org/info/rfc8342); see the RFC itself
+ for full legal notices.";
+
+ revision 2018-02-14 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8342: Network Management Datastore Architecture (NMDA)";
+ }
+
+ /*
+ * Identities
+ */
+
+ identity datastore {
+ description
+ "Abstract base identity for datastore identities.";
+ }
+
+ identity conventional {
+ base datastore;
+ description
+ "Abstract base identity for conventional configuration
+ datastores.";
+ }
+
+ identity running {
+ base conventional;
+ description
+ "The running configuration datastore.";
+ }
+
+ identity candidate {
+ base conventional;
+ description
+ "The candidate configuration datastore.";
+ }
+
+ identity startup {
+ base conventional;
+ description
+ "The startup configuration datastore.";
+ }
+
+ identity intended {
+ base conventional;
+ description
+ "The intended configuration datastore.";
+ }
+
+ identity dynamic {
+ base datastore;
+ description
+ "Abstract base identity for dynamic configuration datastores.";
+ }
+
+ identity operational {
+ base datastore;
+ description
+ "The operational state datastore.";
+ }
+
+ /*
+ * Type definitions
+ */
+
+ typedef datastore-ref {
+ type identityref {
+ base datastore;
+ }
+ description
+ "A datastore identity reference.";
+ }
+}
diff --git a/models/ietf-inet-types@2013-07-15.h b/models/ietf-inet-types@2013-07-15.h
new file mode 100644
index 0000000..514c532
--- /dev/null
+++ b/models/ietf-inet-types@2013-07-15.h
@@ -0,0 +1,1189 @@
+unsigned char ietf_inet_types_2013_07_15_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x69, 0x6e, 0x65, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b,
+ 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, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x69, 0x6e, 0x65, 0x74,
+ 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x70, 0x72, 0x65, 0x66,
+ 0x69, 0x78, 0x20, 0x22, 0x69, 0x6e, 0x65, 0x74, 0x22, 0x3b, 0x6f, 0x72,
+ 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x22,
+ 0x49, 0x45, 0x54, 0x46, 0x20, 0x4e, 0x45, 0x54, 0x4d, 0x4f, 0x44, 0x20,
+ 0x28, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20, 0x44, 0x61, 0x74,
+ 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x4c,
+ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x29, 0x20, 0x57, 0x6f, 0x72,
+ 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x3b,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x22, 0x57, 0x47, 0x20,
+ 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x69, 0x65, 0x74,
+ 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x67, 0x2f, 0x6e, 0x65, 0x74,
+ 0x6d, 0x6f, 0x64, 0x2f, 0x3e, 0x0a, 0x57, 0x47, 0x20, 0x4c, 0x69, 0x73,
+ 0x74, 0x3a, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a,
+ 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64, 0x40, 0x69, 0x65, 0x74, 0x66, 0x2e,
+ 0x6f, 0x72, 0x67, 0x3e, 0x0a, 0x0a, 0x57, 0x47, 0x20, 0x43, 0x68, 0x61,
+ 0x69, 0x72, 0x3a, 0x20, 0x44, 0x61, 0x76, 0x69, 0x64, 0x20, 0x4b, 0x65,
+ 0x73, 0x73, 0x65, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f,
+ 0x3a, 0x64, 0x61, 0x76, 0x69, 0x64, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65,
+ 0x6e, 0x73, 0x40, 0x6e, 0x73, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a,
+ 0x0a, 0x57, 0x47, 0x20, 0x43, 0x68, 0x61, 0x69, 0x72, 0x3a, 0x20, 0x4a,
+ 0x75, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x20, 0x53, 0x63, 0x68, 0x6f, 0x65,
+ 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69,
+ 0x6c, 0x74, 0x6f, 0x3a, 0x6a, 0x2e, 0x73, 0x63, 0x68, 0x6f, 0x65, 0x6e,
+ 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x40, 0x6a, 0x61, 0x63, 0x6f,
+ 0x62, 0x73, 0x2d, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x69, 0x74,
+ 0x79, 0x2e, 0x64, 0x65, 0x3e, 0x0a, 0x0a, 0x45, 0x64, 0x69, 0x74, 0x6f,
+ 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4a, 0x75, 0x65, 0x72, 0x67, 0x65, 0x6e,
+ 0x20, 0x53, 0x63, 0x68, 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64,
+ 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6a, 0x2e,
+ 0x73, 0x63, 0x68, 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65,
+ 0x72, 0x40, 0x6a, 0x61, 0x63, 0x6f, 0x62, 0x73, 0x2d, 0x75, 0x6e, 0x69,
+ 0x76, 0x65, 0x72, 0x73, 0x69, 0x74, 0x79, 0x2e, 0x64, 0x65, 0x3e, 0x22,
+ 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x79,
+ 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x64, 0x65, 0x72, 0x69,
+ 0x76, 0x65, 0x64, 0x0a, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64,
+ 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72,
+ 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67,
+ 0x73, 0x2e, 0x0a, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68,
+ 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x49,
+ 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64,
+ 0x20, 0x61, 0x73, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e,
+ 0x20, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73,
+ 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0a, 0x0a,
+ 0x52, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x69,
+ 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d,
+ 0x73, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x77,
+ 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x20,
+ 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x70, 0x75,
+ 0x72, 0x73, 0x75, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x2c, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x0a, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
+ 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64,
+ 0x20, 0x42, 0x53, 0x44, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
+ 0x0a, 0x73, 0x65, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x74, 0x68, 0x20, 0x69,
+ 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e,
+ 0x63, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, 0x54,
+ 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x27, 0x73, 0x20, 0x4c, 0x65,
+ 0x67, 0x61, 0x6c, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x73, 0x0a, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20,
+ 0x74, 0x6f, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x44, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x2e, 0x69, 0x65,
+ 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e,
+ 0x73, 0x65, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x29, 0x2e, 0x0a, 0x0a, 0x54,
+ 0x68, 0x69, 0x73, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70,
+ 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x36,
+ 0x39, 0x39, 0x31, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x0a, 0x74, 0x68, 0x65,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x67,
+ 0x61, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x22,
+ 0x3b, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30,
+ 0x31, 0x33, 0x2d, 0x30, 0x37, 0x2d, 0x31, 0x35, 0x20, 0x7b, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54,
+ 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x61, 0x64, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f,
+ 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x65, 0x77, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3a, 0x0a,
+ 0x2d, 0x20, 0x69, 0x70, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x2d, 0x6e, 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65, 0x0a, 0x2d, 0x20, 0x69,
+ 0x70, 0x76, 0x34, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2d,
+ 0x6e, 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65, 0x0a, 0x2d, 0x20, 0x69, 0x70,
+ 0x76, 0x36, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2d, 0x6e,
+ 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x36,
+ 0x39, 0x39, 0x31, 0x3a, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x54, 0x79,
+ 0x70, 0x65, 0x73, 0x22, 0x3b, 0x7d, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x32,
+ 0x34, 0x20, 0x7b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x22, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x22, 0x3b, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46,
+ 0x43, 0x20, 0x36, 0x30, 0x32, 0x31, 0x3a, 0x20, 0x43, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61,
+ 0x20, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70,
+ 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x2d, 0x76, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x6e,
+ 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x65,
+ 0x6e, 0x75, 0x6d, 0x20, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20,
+ 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x22, 0x30, 0x22, 0x3b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22,
+ 0x41, 0x6e, 0x20, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x6f,
+ 0x72, 0x20, 0x75, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65,
+ 0x64, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x22,
+ 0x3b, 0x7d, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x69, 0x70, 0x76, 0x34, 0x20,
+ 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x22, 0x31, 0x22, 0x3b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x22,
+ 0x54, 0x68, 0x65, 0x20, 0x49, 0x50, 0x76, 0x34, 0x20, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x61, 0x73, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46, 0x43, 0x20,
+ 0x37, 0x39, 0x31, 0x2e, 0x22, 0x3b, 0x7d, 0x65, 0x6e, 0x75, 0x6d, 0x20,
+ 0x69, 0x70, 0x76, 0x36, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
+ 0x22, 0x32, 0x22, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x49, 0x50, 0x76,
+ 0x36, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x61,
+ 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x32, 0x34, 0x36, 0x30, 0x2e, 0x22, 0x3b,
+ 0x7d, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x50, 0x20, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x0a, 0x0a, 0x49, 0x6e,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73,
+ 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73,
+ 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65,
+ 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x65, 0x74, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c,
+ 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32,
+ 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x20, 0x37, 0x39, 0x31, 0x3a, 0x20,
+ 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x50, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x32, 0x34,
+ 0x36, 0x30, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+ 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2c, 0x20, 0x56,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x36, 0x20, 0x28, 0x49, 0x50,
+ 0x76, 0x36, 0x29, 0x20, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x34, 0x30,
+ 0x30, 0x31, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20,
+ 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+ 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x41, 0x64, 0x64,
+ 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70,
+ 0x65, 0x64, 0x65, 0x66, 0x20, 0x64, 0x73, 0x63, 0x70, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x20, 0x7b, 0x72,
+ 0x61, 0x6e, 0x67, 0x65, 0x20, 0x22, 0x30, 0x2e, 0x2e, 0x36, 0x33, 0x22,
+ 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20, 0x64, 0x73, 0x63, 0x70, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72,
+ 0x65, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x43, 0x6f, 0x64, 0x65, 0x20, 0x50,
+ 0x6f, 0x69, 0x6e, 0x74, 0x0a, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61,
+ 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61,
+ 0x63, 0x6b, 0x65, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x74,
+ 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61,
+ 0x6d, 0x2e, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63,
+ 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65,
+ 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x44, 0x73,
+ 0x63, 0x70, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63,
+ 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22,
+ 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22,
+ 0x52, 0x46, 0x43, 0x20, 0x33, 0x32, 0x38, 0x39, 0x3a, 0x20, 0x4d, 0x61,
+ 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x73,
+ 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x44, 0x69,
+ 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x41, 0x72, 0x63,
+ 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x52, 0x46,
+ 0x43, 0x20, 0x32, 0x34, 0x37, 0x34, 0x3a, 0x20, 0x44, 0x65, 0x66, 0x69,
+ 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69,
+ 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x73, 0x20, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x44, 0x53, 0x20, 0x46,
+ 0x69, 0x65, 0x6c, 0x64, 0x29, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x49, 0x50, 0x76, 0x34, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x49, 0x50,
+ 0x76, 0x36, 0x20, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x0a, 0x52,
+ 0x46, 0x43, 0x20, 0x32, 0x37, 0x38, 0x30, 0x3a, 0x20, 0x49, 0x41, 0x4e,
+ 0x41, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20,
+ 0x46, 0x6f, 0x72, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x49,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x20, 0x48,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70,
+ 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x76, 0x36, 0x2d, 0x66, 0x6c,
+ 0x6f, 0x77, 0x2d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x7b, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x7b, 0x72,
+ 0x61, 0x6e, 0x67, 0x65, 0x20, 0x22, 0x30, 0x2e, 0x2e, 0x31, 0x30, 0x34,
+ 0x38, 0x35, 0x37, 0x35, 0x22, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20,
+ 0x69, 0x70, 0x76, 0x36, 0x2d, 0x66, 0x6c, 0x6f, 0x77, 0x2d, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70,
+ 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x66, 0x6c, 0x6f, 0x77, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x6c, 0x6f, 0x77, 0x0a,
+ 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x20,
+ 0x49, 0x50, 0x76, 0x36, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20,
+ 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
+ 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20,
+ 0x74, 0x6f, 0x0a, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e,
+ 0x61, 0x74, 0x65, 0x20, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x20,
+ 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d,
+ 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75,
+ 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x49, 0x50, 0x76, 0x36, 0x46, 0x6c, 0x6f, 0x77, 0x4c,
+ 0x61, 0x62, 0x65, 0x6c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c,
+ 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32,
+ 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x33, 0x35, 0x39, 0x35, 0x3a, 0x20,
+ 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43, 0x6f, 0x6e, 0x76,
+ 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x49, 0x50, 0x76, 0x36, 0x20, 0x46, 0x6c, 0x6f, 0x77, 0x20, 0x4c, 0x61,
+ 0x62, 0x65, 0x6c, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x32, 0x34, 0x36, 0x30,
+ 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x50,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2c, 0x20, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x36, 0x20, 0x28, 0x49, 0x50, 0x76, 0x36,
+ 0x29, 0x20, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65,
+ 0x66, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+ 0x72, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74,
+ 0x31, 0x36, 0x20, 0x7b, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x22, 0x30,
+ 0x2e, 0x2e, 0x36, 0x35, 0x35, 0x33, 0x35, 0x22, 0x3b, 0x7d, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54,
+ 0x68, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x6e, 0x75, 0x6d, 0x62,
+ 0x65, 0x72, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x31, 0x36, 0x2d,
+ 0x62, 0x69, 0x74, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d,
+ 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x0a, 0x49, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73,
+ 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x20, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x73, 0x75, 0x63, 0x68,
+ 0x20, 0x61, 0x73, 0x20, 0x55, 0x44, 0x50, 0x2c, 0x20, 0x54, 0x43, 0x50,
+ 0x2c, 0x20, 0x44, 0x43, 0x43, 0x50, 0x2c, 0x20, 0x6f, 0x72, 0x0a, 0x53,
+ 0x43, 0x54, 0x50, 0x2e, 0x20, 0x20, 0x50, 0x6f, 0x72, 0x74, 0x20, 0x6e,
+ 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61,
+ 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x49,
+ 0x41, 0x4e, 0x41, 0x2e, 0x20, 0x20, 0x41, 0x20, 0x63, 0x75, 0x72, 0x72,
+ 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x0a,
+ 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c,
+ 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x3c, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x61,
+ 0x6e, 0x61, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x3e, 0x2e, 0x0a, 0x0a, 0x4e,
+ 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20,
+ 0x69, 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x49, 0x41, 0x4e, 0x41, 0x2e, 0x20, 0x20, 0x49, 0x6e,
+ 0x0a, 0x73, 0x69, 0x74, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x64, 0x6f, 0x65,
+ 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x73,
+ 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x63, 0x61, 0x6e,
+ 0x0a, 0x62, 0x65, 0x20, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x73, 0x75, 0x62, 0x74, 0x79, 0x70, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x6e,
+ 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x0a,
+ 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73,
+ 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73,
+ 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a,
+ 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x65, 0x74, 0x50,
+ 0x6f, 0x72, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x20,
+ 0x37, 0x36, 0x38, 0x3a, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x44, 0x61,
+ 0x74, 0x61, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x6f, 0x6c, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x20, 0x37, 0x39, 0x33,
+ 0x3a, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x50,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x0a, 0x52, 0x46, 0x43, 0x20,
+ 0x34, 0x39, 0x36, 0x30, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
+ 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x54, 0x72, 0x61,
+ 0x6e, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x34,
+ 0x33, 0x34, 0x30, 0x3a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x67, 0x72, 0x61,
+ 0x6d, 0x20, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x50, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x28, 0x44, 0x43, 0x43, 0x50, 0x29,
+ 0x0a, 0x52, 0x46, 0x43, 0x20, 0x34, 0x30, 0x30, 0x31, 0x3a, 0x20, 0x54,
+ 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65,
+ 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65,
+ 0x73, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x61, 0x73, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22,
+ 0x54, 0x68, 0x65, 0x20, 0x61, 0x73, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+ 0x72, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65,
+ 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6e, 0x6f,
+ 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
+ 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x0a, 0x77, 0x68, 0x69, 0x63,
+ 0x68, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x20, 0x61,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73,
+ 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x28, 0x41, 0x53, 0x29,
+ 0x2e, 0x20, 0x20, 0x41, 0x6e, 0x20, 0x41, 0x53, 0x20, 0x69, 0x73, 0x20,
+ 0x61, 0x20, 0x73, 0x65, 0x74, 0x0a, 0x6f, 0x66, 0x20, 0x72, 0x6f, 0x75,
+ 0x74, 0x65, 0x72, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x61,
+ 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x65, 0x63, 0x68,
+ 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x69,
+ 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x75, 0x73,
+ 0x69, 0x6e, 0x67, 0x0a, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72,
+ 0x69, 0x6f, 0x72, 0x20, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x20,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x72,
+ 0x69, 0x63, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65,
+ 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x53, 0x2c, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e,
+ 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x72, 0x20, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x6f, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x20,
+ 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6f,
+ 0x74, 0x68, 0x65, 0x72, 0x20, 0x41, 0x53, 0x65, 0x73, 0x2e, 0x20, 0x20,
+ 0x49, 0x41, 0x4e, 0x41, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69,
+ 0x6e, 0x73, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x41, 0x53, 0x20, 0x6e, 0x75,
+ 0x6d, 0x62, 0x65, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x68, 0x61, 0x73, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x67,
+ 0x61, 0x74, 0x65, 0x64, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x70,
+ 0x61, 0x72, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a,
+ 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x67,
+ 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x41, 0x75,
+ 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x73, 0x79, 0x73,
+ 0x74, 0x65, 0x6d, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20,
+ 0x77, 0x65, 0x72, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
+ 0x6c, 0x6c, 0x79, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20,
+ 0x74, 0x6f, 0x20, 0x31, 0x36, 0x0a, 0x62, 0x69, 0x74, 0x73, 0x2e, 0x20,
+ 0x20, 0x42, 0x47, 0x50, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+ 0x6f, 0x6e, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x65, 0x6e, 0x6c,
+ 0x61, 0x72, 0x67, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x75,
+ 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x73, 0x79, 0x73,
+ 0x74, 0x65, 0x6d, 0x0a, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x33, 0x32, 0x20, 0x62,
+ 0x69, 0x74, 0x73, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x66, 0x6f, 0x72,
+ 0x65, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x75, 0x69,
+ 0x6e, 0x74, 0x33, 0x32, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61,
+ 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x72,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f,
+ 0x72, 0x74, 0x0a, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20,
+ 0x61, 0x75, 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x73,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+ 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65,
+ 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71,
+ 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f,
+ 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75,
+ 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49,
+ 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x31, 0x39, 0x33, 0x30,
+ 0x3a, 0x20, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x53,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x28, 0x41, 0x53, 0x29, 0x0a, 0x52,
+ 0x46, 0x43, 0x20, 0x34, 0x32, 0x37, 0x31, 0x3a, 0x20, 0x41, 0x20, 0x42,
+ 0x6f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x34,
+ 0x20, 0x28, 0x42, 0x47, 0x50, 0x2d, 0x34, 0x29, 0x0a, 0x52, 0x46, 0x43,
+ 0x20, 0x34, 0x30, 0x30, 0x31, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75,
+ 0x61, 0x6c, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20,
+ 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x0a, 0x52, 0x46,
+ 0x43, 0x20, 0x36, 0x37, 0x39, 0x33, 0x3a, 0x20, 0x42, 0x47, 0x50, 0x20,
+ 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x46, 0x6f, 0x75, 0x72, 0x2d, 0x4f, 0x63, 0x74, 0x65, 0x74, 0x20, 0x41,
+ 0x75, 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x53, 0x79,
+ 0x73, 0x74, 0x65, 0x6d, 0x20, 0x28, 0x41, 0x53, 0x29, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x75, 0x6d,
+ 0x62, 0x65, 0x72, 0x20, 0x53, 0x70, 0x61, 0x63, 0x65, 0x22, 0x3b, 0x7d,
+ 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x2d, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70, 0x76, 0x34, 0x2d, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70, 0x76, 0x36, 0x2d, 0x61, 0x64,
+ 0x64, 0x72, 0x65, 0x73, 0x73, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20,
+ 0x69, 0x70, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x20, 0x61, 0x64, 0x64,
+ 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20,
+ 0x49, 0x50, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e,
+ 0x65, 0x75, 0x74, 0x72, 0x61, 0x6c, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65,
+ 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x72,
+ 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x49, 0x50, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x73, 0x63,
+ 0x6f, 0x70, 0x65, 0x64, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x65, 0x73, 0x0a, 0x62, 0x79, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69,
+ 0x6e, 0x67, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x34,
+ 0x30, 0x30, 0x37, 0x3a, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x53, 0x63,
+ 0x6f, 0x70, 0x65, 0x64, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72,
+ 0x65, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x69, 0x70, 0x76, 0x34, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
+ 0x67, 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27,
+ 0x28, 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x31, 0x2d, 0x39,
+ 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x31, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x32, 0x5b, 0x30, 0x2d, 0x34,
+ 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x32, 0x35, 0x5b, 0x30, 0x2d,
+ 0x35, 0x5d, 0x29, 0x5c, 0x2e, 0x29, 0x7b, 0x33, 0x7d, 0x28, 0x5b, 0x30,
+ 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x31, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x7c, 0x31, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x7c, 0x32, 0x5b, 0x30, 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x7c, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x29, 0x28,
+ 0x25, 0x5b, 0x5c, 0x70, 0x7b, 0x4e, 0x7d, 0x5c, 0x70, 0x7b, 0x4c, 0x7d,
+ 0x5d, 0x2b, 0x29, 0x3f, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20,
+ 0x69, 0x70, 0x76, 0x34, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73,
+ 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x76, 0x34,
+ 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, 0x0a,
+ 0x64, 0x6f, 0x74, 0x74, 0x65, 0x64, 0x2d, 0x71, 0x75, 0x61, 0x64, 0x20,
+ 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x49, 0x50, 0x76, 0x34, 0x20, 0x61, 0x64, 0x64, 0x72,
+ 0x65, 0x73, 0x73, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x6c,
+ 0x75, 0x64, 0x65, 0x20, 0x61, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x0a, 0x69,
+ 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61,
+ 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x25, 0x20, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x7a, 0x6f,
+ 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x73, 0x20,
+ 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x69, 0x73, 0x61,
+ 0x6d, 0x62, 0x69, 0x67, 0x75, 0x61, 0x74, 0x65, 0x20, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x20, 0x20,
+ 0x46, 0x6f, 0x72, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x6c, 0x6f, 0x63,
+ 0x61, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73,
+ 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69,
+ 0x6e, 0x64, 0x65, 0x78, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x0a, 0x74, 0x79,
+ 0x70, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65,
+ 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+ 0x72, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d,
+ 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x0a, 0x69, 0x6e, 0x74, 0x65,
+ 0x72, 0x66, 0x61, 0x63, 0x65, 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x72, 0x65,
+ 0x73, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65,
+ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x0a, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
+ 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x64, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6f,
+ 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x7a, 0x6f, 0x6e,
+ 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x6c,
+ 0x0a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x3b, 0x7d, 0x74, 0x79,
+ 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x76, 0x36, 0x2d, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61, 0x74,
+ 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x28, 0x3a, 0x7c, 0x5b, 0x30,
+ 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c,
+ 0x34, 0x7d, 0x29, 0x3a, 0x29, 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d,
+ 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c, 0x34, 0x7d, 0x3a, 0x29,
+ 0x7b, 0x30, 0x2c, 0x35, 0x7d, 0x28, 0x28, 0x28, 0x5b, 0x30, 0x2d, 0x39,
+ 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c, 0x34, 0x7d,
+ 0x3a, 0x29, 0x3f, 0x28, 0x3a, 0x7c, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d,
+ 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c, 0x34, 0x7d, 0x29, 0x29,
+ 0x7c, 0x28, 0x28, 0x28, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x7c,
+ 0x32, 0x5b, 0x30, 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c,
+ 0x5b, 0x30, 0x31, 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x3f, 0x5b,
+ 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x5c, 0x2e, 0x29, 0x7b, 0x33, 0x7d, 0x28,
+ 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x7c, 0x32, 0x5b, 0x30, 0x2d,
+ 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x30, 0x31, 0x5d,
+ 0x3f, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39, 0x5d,
+ 0x29, 0x29, 0x29, 0x28, 0x25, 0x5b, 0x5c, 0x70, 0x7b, 0x4e, 0x7d, 0x5c,
+ 0x70, 0x7b, 0x4c, 0x7d, 0x5d, 0x2b, 0x29, 0x3f, 0x27, 0x3b, 0x70, 0x61,
+ 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x28, 0x5b, 0x5e, 0x3a,
+ 0x5d, 0x2b, 0x3a, 0x29, 0x7b, 0x36, 0x7d, 0x28, 0x28, 0x5b, 0x5e, 0x3a,
+ 0x5d, 0x2b, 0x3a, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x29, 0x7c, 0x28, 0x2e,
+ 0x2a, 0x5c, 0x2e, 0x2e, 0x2a, 0x29, 0x29, 0x29, 0x7c, 0x28, 0x28, 0x28,
+ 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x3a, 0x29, 0x2a, 0x5b, 0x5e, 0x3a, 0x5d,
+ 0x2b, 0x29, 0x3f, 0x3a, 0x3a, 0x28, 0x28, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b,
+ 0x3a, 0x29, 0x2a, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x29, 0x3f, 0x29, 0x28,
+ 0x25, 0x2e, 0x2b, 0x29, 0x3f, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65,
+ 0x20, 0x69, 0x70, 0x76, 0x36, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
+ 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65,
+ 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x76,
+ 0x36, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e,
+ 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x2c, 0x0a, 0x6d, 0x69, 0x78, 0x65, 0x64,
+ 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e,
+ 0x65, 0x64, 0x2d, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x20, 0x6e, 0x6f, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x49, 0x50, 0x76, 0x36, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x6d, 0x61, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65,
+ 0x20, 0x61, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x2c, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x25, 0x20, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20,
+ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65,
+ 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x69, 0x73, 0x61, 0x6d, 0x62, 0x69,
+ 0x67, 0x75, 0x61, 0x74, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x63, 0x61, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x20, 0x20, 0x46, 0x6f, 0x72,
+ 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+ 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x2c, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x0a, 0x74, 0x79, 0x70, 0x69, 0x63,
+ 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x20, 0x69, 0x6e,
+ 0x64, 0x65, 0x78, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f,
+ 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x61, 0x6e, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69,
+ 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x0a, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x77, 0x69,
+ 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x2e, 0x0a,
+ 0x0a, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63,
+ 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
+ 0x73, 0x65, 0x73, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x0a, 0x72, 0x65, 0x70,
+ 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x53,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x20, 0x6f, 0x66, 0x20,
+ 0x52, 0x46, 0x43, 0x20, 0x35, 0x39, 0x35, 0x32, 0x2e, 0x20, 0x20, 0x54,
+ 0x68, 0x65, 0x0a, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c,
+ 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x0a, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x74, 0x20, 0x61, 0x73, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x62, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x31, 0x31, 0x2e, 0x32, 0x20, 0x6f, 0x66, 0x20, 0x52,
+ 0x46, 0x43, 0x20, 0x34, 0x30, 0x30, 0x37, 0x2e, 0x22, 0x3b, 0x72, 0x65,
+ 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43,
+ 0x20, 0x34, 0x32, 0x39, 0x31, 0x3a, 0x20, 0x49, 0x50, 0x20, 0x56, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x36, 0x20, 0x41, 0x64, 0x64, 0x72,
+ 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x72, 0x63, 0x68, 0x69,
+ 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x52, 0x46, 0x43, 0x20,
+ 0x34, 0x30, 0x30, 0x37, 0x3a, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x53,
+ 0x63, 0x6f, 0x70, 0x65, 0x64, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
+ 0x73, 0x20, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x35, 0x39, 0x35, 0x32, 0x3a,
+ 0x20, 0x41, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49, 0x50,
+ 0x76, 0x36, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x54,
+ 0x65, 0x78, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65,
+ 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x7b,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70,
+ 0x76, 0x34, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2d, 0x6e,
+ 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70, 0x76, 0x36, 0x2d, 0x61, 0x64,
+ 0x64, 0x72, 0x65, 0x73, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x7a, 0x6f, 0x6e,
+ 0x65, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20, 0x69, 0x70, 0x2d, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x7a, 0x6f,
+ 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50,
+ 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x69, 0x73, 0x0a, 0x49, 0x50, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x75, 0x74, 0x72, 0x61, 0x6c, 0x2e, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75,
+ 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x65,
+ 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x50, 0x20, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f,
+ 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x63,
+ 0x6f, 0x70, 0x65, 0x64, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x65, 0x73, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x74, 0x20,
+ 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x6c, 0x6c,
+ 0x6f, 0x77, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x34,
+ 0x30, 0x30, 0x37, 0x3a, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x53, 0x63,
+ 0x6f, 0x70, 0x65, 0x64, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72,
+ 0x65, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x69, 0x70, 0x76, 0x34, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x2d, 0x6e, 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x7b, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70, 0x76, 0x34,
+ 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x70, 0x61,
+ 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5b, 0x30, 0x2d, 0x39, 0x5c,
+ 0x2e, 0x5d, 0x2a, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x41, 0x6e, 0x20, 0x49, 0x50,
+ 0x76, 0x34, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x20, 0x7a, 0x6f, 0x6e,
+ 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x20, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x64, 0x65, 0x72,
+ 0x69, 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x0a, 0x69, 0x70,
+ 0x76, 0x34, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20,
+ 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x20, 0x73, 0x69, 0x74, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x73, 0x0a, 0x6b, 0x6e, 0x6f, 0x77,
+ 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68,
+ 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6e, 0x6f, 0x20, 0x7a, 0x6f, 0x6e, 0x65,
+ 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65,
+ 0x65, 0x64, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65,
+ 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x76, 0x36, 0x2d, 0x61, 0x64, 0x64,
+ 0x72, 0x65, 0x73, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x7a, 0x6f, 0x6e, 0x65,
+ 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a,
+ 0x69, 0x70, 0x76, 0x36, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5b,
+ 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x3a, 0x5c, 0x2e,
+ 0x5d, 0x2a, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x41, 0x6e, 0x20, 0x49, 0x50, 0x76,
+ 0x36, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x20, 0x7a, 0x6f, 0x6e, 0x65,
+ 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69,
+ 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x64, 0x65, 0x72, 0x69,
+ 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x0a, 0x69, 0x70, 0x76,
+ 0x36, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x6d,
+ 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69,
+ 0x6e, 0x20, 0x73, 0x69, 0x74, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x7a,
+ 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x73, 0x0a, 0x6b, 0x6e, 0x6f, 0x77, 0x6e,
+ 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x65,
+ 0x6e, 0x63, 0x65, 0x20, 0x6e, 0x6f, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20,
+ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65, 0x65,
+ 0x64, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x34, 0x32, 0x39,
+ 0x31, 0x3a, 0x20, 0x49, 0x50, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x36, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x69,
+ 0x6e, 0x67, 0x20, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74,
+ 0x75, 0x72, 0x65, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x34, 0x30, 0x30, 0x37,
+ 0x3a, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x53, 0x63, 0x6f, 0x70, 0x65,
+ 0x64, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x41, 0x72,
+ 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x52,
+ 0x46, 0x43, 0x20, 0x35, 0x39, 0x35, 0x32, 0x3a, 0x20, 0x41, 0x20, 0x52,
+ 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x41,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x54, 0x65, 0x78, 0x74, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52,
+ 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x69, 0x70, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70, 0x76,
+ 0x34, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x3b, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x69, 0x70, 0x76, 0x36, 0x2d,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65,
+ 0x20, 0x69, 0x70, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x20, 0x70, 0x72, 0x65,
+ 0x66, 0x69, 0x78, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20, 0x49,
+ 0x50, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65,
+ 0x75, 0x74, 0x72, 0x61, 0x6c, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x72, 0x65,
+ 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x0a, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x49, 0x50, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x2e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x69, 0x70, 0x76, 0x34, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20,
+ 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28,
+ 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x31, 0x2d, 0x39, 0x5d,
+ 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x31, 0x5b, 0x30, 0x2d, 0x39, 0x5d,
+ 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x32, 0x5b, 0x30, 0x2d, 0x34, 0x5d,
+ 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35,
+ 0x5d, 0x29, 0x5c, 0x2e, 0x29, 0x7b, 0x33, 0x7d, 0x28, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x7c, 0x5b, 0x31, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x7c, 0x31, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x7c, 0x32, 0x5b, 0x30, 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x7c, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x29, 0x2f, 0x28,
+ 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7c, 0x28, 0x5b, 0x31, 0x2d,
+ 0x32, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7c, 0x28, 0x33, 0x5b,
+ 0x30, 0x2d, 0x32, 0x5d, 0x29, 0x29, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68,
+ 0x65, 0x20, 0x69, 0x70, 0x76, 0x34, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69,
+ 0x78, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65,
+ 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x76,
+ 0x34, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x70, 0x72,
+ 0x65, 0x66, 0x69, 0x78, 0x2e, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x70, 0x72,
+ 0x65, 0x66, 0x69, 0x78, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20,
+ 0x69, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x62, 0x79, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66,
+ 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61,
+ 0x63, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x75, 0x73,
+ 0x74, 0x20, 0x62, 0x65, 0x20, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x74, 0x68,
+ 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x20,
+ 0x74, 0x6f, 0x20, 0x33, 0x32, 0x2e, 0x0a, 0x0a, 0x41, 0x20, 0x70, 0x72,
+ 0x65, 0x66, 0x69, 0x78, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x20, 0x63,
+ 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x73, 0x20, 0x74,
+ 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x20, 0x61, 0x64, 0x64, 0x72,
+ 0x65, 0x73, 0x73, 0x0a, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x20, 0x31, 0x2d, 0x62, 0x69, 0x74,
+ 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d,
+ 0x6f, 0x73, 0x74, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x6e, 0x74, 0x20, 0x62, 0x69, 0x74, 0x20, 0x28, 0x4d, 0x53, 0x42,
+ 0x29, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x74, 0x6f, 0x20, 0x30, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20,
+ 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f,
+ 0x72, 0x6d, 0x61, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49,
+ 0x50, 0x76, 0x34, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x68,
+ 0x61, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20,
+ 0x6f, 0x66, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x49, 0x50, 0x76, 0x34, 0x20,
+ 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x73, 0x65, 0x74, 0x20,
+ 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74,
+ 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x61, 0x72,
+ 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x49, 0x50, 0x76,
+ 0x34, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x2e, 0x22, 0x3b, 0x7d,
+ 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x70, 0x76, 0x36,
+ 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x7b, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61,
+ 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x28, 0x3a, 0x7c, 0x5b,
+ 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30,
+ 0x2c, 0x34, 0x7d, 0x29, 0x3a, 0x29, 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x61,
+ 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c, 0x34, 0x7d, 0x3a,
+ 0x29, 0x7b, 0x30, 0x2c, 0x35, 0x7d, 0x28, 0x28, 0x28, 0x5b, 0x30, 0x2d,
+ 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c, 0x34,
+ 0x7d, 0x3a, 0x29, 0x3f, 0x28, 0x3a, 0x7c, 0x5b, 0x30, 0x2d, 0x39, 0x61,
+ 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x30, 0x2c, 0x34, 0x7d, 0x29,
+ 0x29, 0x7c, 0x28, 0x28, 0x28, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d,
+ 0x7c, 0x32, 0x5b, 0x30, 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d,
+ 0x7c, 0x5b, 0x30, 0x31, 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x3f,
+ 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x5c, 0x2e, 0x29, 0x7b, 0x33, 0x7d,
+ 0x28, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x7c, 0x32, 0x5b, 0x30,
+ 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x30, 0x31,
+ 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x29, 0x29, 0x29, 0x28, 0x2f, 0x28, 0x28, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x29, 0x7c, 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x32, 0x7d,
+ 0x29, 0x7c, 0x28, 0x31, 0x5b, 0x30, 0x2d, 0x31, 0x5d, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x29, 0x7c, 0x28, 0x31, 0x32, 0x5b, 0x30, 0x2d, 0x38, 0x5d,
+ 0x29, 0x29, 0x29, 0x27, 0x3b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e,
+ 0x20, 0x27, 0x28, 0x28, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x3a, 0x29, 0x7b,
+ 0x36, 0x7d, 0x28, 0x28, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x3a, 0x5b, 0x5e,
+ 0x3a, 0x5d, 0x2b, 0x29, 0x7c, 0x28, 0x2e, 0x2a, 0x5c, 0x2e, 0x2e, 0x2a,
+ 0x29, 0x29, 0x29, 0x7c, 0x28, 0x28, 0x28, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b,
+ 0x3a, 0x29, 0x2a, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x29, 0x3f, 0x3a, 0x3a,
+ 0x28, 0x28, 0x5b, 0x5e, 0x3a, 0x5d, 0x2b, 0x3a, 0x29, 0x2a, 0x5b, 0x5e,
+ 0x3a, 0x5d, 0x2b, 0x29, 0x3f, 0x29, 0x28, 0x2f, 0x2e, 0x2b, 0x29, 0x27,
+ 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20, 0x69, 0x70, 0x76, 0x36, 0x2d,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61,
+ 0x6e, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x2e, 0x0a, 0x54,
+ 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x6c, 0x65,
+ 0x6e, 0x67, 0x74, 0x68, 0x20, 0x69, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x6e, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d,
+ 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x20,
+ 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6c, 0x65,
+ 0x73, 0x73, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65,
+ 0x71, 0x75, 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x31, 0x32, 0x38, 0x2e,
+ 0x0a, 0x0a, 0x41, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x6c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x6e, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x49,
+ 0x50, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a, 0x6d, 0x61,
+ 0x73, 0x6b, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20,
+ 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73,
+ 0x20, 0x31, 0x2d, 0x62, 0x69, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x0a, 0x73, 0x69,
+ 0x67, 0x6e, 0x69, 0x66, 0x69, 0x63, 0x61, 0x6e, 0x74, 0x20, 0x62, 0x69,
+ 0x74, 0x20, 0x28, 0x4d, 0x53, 0x42, 0x29, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x62, 0x69,
+ 0x74, 0x73, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x30, 0x2e,
+ 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c,
+ 0x64, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x62,
+ 0x69, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x0a, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f,
+ 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6f, 0x6e,
+ 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20,
+ 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x70,
+ 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6c,
+ 0x6c, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x0a, 0x74, 0x68,
+ 0x65, 0x20, 0x49, 0x50, 0x76, 0x36, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x7a, 0x65,
+ 0x72, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20,
+ 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x0a, 0x49, 0x50, 0x76, 0x36, 0x20, 0x70, 0x72, 0x65,
+ 0x66, 0x69, 0x78, 0x2e, 0x20, 0x20, 0x46, 0x75, 0x72, 0x74, 0x68, 0x65,
+ 0x72, 0x6d, 0x6f, 0x72, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49,
+ 0x50, 0x76, 0x36, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x65, 0x64, 0x0a, 0x61, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x34, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x35, 0x39,
+ 0x35, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x35, 0x39, 0x35, 0x32,
+ 0x3a, 0x20, 0x41, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49,
+ 0x50, 0x76, 0x36, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20,
+ 0x54, 0x65, 0x78, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70,
+ 0x65, 0x64, 0x65, 0x66, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2d,
+ 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73,
+ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+ 0x68, 0x20, 0x22, 0x31, 0x2e, 0x2e, 0x32, 0x35, 0x33, 0x22, 0x3b, 0x70,
+ 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x28, 0x28, 0x5b,
+ 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x28,
+ 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5c, 0x2d,
+ 0x5f, 0x5d, 0x29, 0x7b, 0x30, 0x2c, 0x36, 0x31, 0x7d, 0x29, 0x3f, 0x5b,
+ 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x5c, 0x2e,
+ 0x29, 0x2a, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d,
+ 0x39, 0x5f, 0x5d, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30,
+ 0x2d, 0x39, 0x5c, 0x2d, 0x5f, 0x5d, 0x29, 0x7b, 0x30, 0x2c, 0x36, 0x31,
+ 0x7d, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d,
+ 0x39, 0x5d, 0x5c, 0x2e, 0x3f, 0x29, 0x7c, 0x5c, 0x2e, 0x27, 0x3b, 0x7d,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x22, 0x54, 0x68, 0x65, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2d,
+ 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65,
+ 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x44,
+ 0x4e, 0x53, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x6e, 0x61,
+ 0x6d, 0x65, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x0a, 0x6e, 0x61, 0x6d,
+ 0x65, 0x20, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20, 0x62, 0x65, 0x20,
+ 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x20, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66,
+ 0x69, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x65, 0x76, 0x65, 0x72,
+ 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x0a,
+ 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x64, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x61, 0x72,
+ 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x6f, 0x73, 0x65,
+ 0x6c, 0x79, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64,
+ 0x2e, 0x20, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x33,
+ 0x2e, 0x35, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x31, 0x30,
+ 0x33, 0x34, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64,
+ 0x73, 0x20, 0x61, 0x20, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x20, 0x28,
+ 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
+ 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x32, 0x2e, 0x31, 0x20,
+ 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x31, 0x31, 0x32, 0x33, 0x29,
+ 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65,
+ 0x72, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x20, 0x69, 0x73, 0x20,
+ 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x0a, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x75,
+ 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x70, 0x72, 0x61, 0x63, 0x74, 0x69,
+ 0x63, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
+ 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x75, 0x73, 0x65, 0x2c, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x73,
+ 0x69, 0x62, 0x6c, 0x65, 0x0a, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x20,
+ 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e,
+ 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x6f, 0x6c, 0x64, 0x20, 0x76,
+ 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73,
+ 0x20, 0x6f, 0x66, 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x73, 0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64,
+ 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x75, 0x73,
+ 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x6f, 0x72, 0x20,
+ 0x41, 0x41, 0x41, 0x41, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73,
+ 0x0a, 0x28, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73,
+ 0x29, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20,
+ 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x73, 0x75, 0x63,
+ 0x68, 0x20, 0x61, 0x73, 0x20, 0x53, 0x52, 0x56, 0x20, 0x72, 0x65, 0x63,
+ 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x20, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x0a,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x65, 0x72, 0x20, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x20,
+ 0x28, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x0a, 0x69,
+ 0x6e, 0x20, 0x52, 0x46, 0x43, 0x20, 0x39, 0x35, 0x32, 0x29, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x44, 0x4e, 0x53, 0x20,
+ 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46, 0x43, 0x73, 0x20,
+ 0x31, 0x30, 0x33, 0x34, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x31, 0x31, 0x32,
+ 0x33, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
+ 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74,
+ 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x74, 0x6f,
+ 0x72, 0x65, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+ 0x73, 0x20, 0x69, 0x6e, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20,
+ 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2d, 0x6e,
+ 0x61, 0x6d, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x61, 0x72, 0x65,
+ 0x20, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64,
+ 0x20, 0x74, 0x6f, 0x0a, 0x61, 0x64, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63,
+ 0x74, 0x65, 0x72, 0x20, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64,
+ 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x65, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c,
+ 0x69, 0x74, 0x79, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x65, 0x6e,
+ 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x4e,
+ 0x53, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x44, 0x4e, 0x53, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x6f, 0x6c, 0x20, 0x69, 0x73, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74,
+ 0x65, 0x64, 0x0a, 0x74, 0x6f, 0x20, 0x32, 0x35, 0x35, 0x20, 0x63, 0x68,
+ 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x20, 0x53,
+ 0x69, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63,
+ 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73,
+ 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73,
+ 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x61, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x62, 0x79,
+ 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x72,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x61, 0x69, 0x6c,
+ 0x69, 0x6e, 0x67, 0x20, 0x4e, 0x55, 0x4c, 0x4c, 0x0a, 0x62, 0x79, 0x74,
+ 0x65, 0x2c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x32, 0x35, 0x33, 0x20,
+ 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x20, 0x63,
+ 0x61, 0x6e, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x20, 0x69, 0x6e,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c,
+ 0x20, 0x64, 0x6f, 0x74, 0x74, 0x65, 0x64, 0x0a, 0x6e, 0x6f, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63,
+ 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x75, 0x73,
+ 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x6f, 0x6d, 0x61,
+ 0x69, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x62, 0x65, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x68, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x6e, 0x61,
+ 0x6d, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x73, 0x6f,
+ 0x6c, 0x76, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x0a, 0x49, 0x50, 0x20, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x20, 0x20, 0x4e,
+ 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x61, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2d,
+ 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, 0x6d,
+ 0x61, 0x79, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x20, 0x74,
+ 0x6f, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x6d, 0x75, 0x6c, 0x74,
+ 0x69, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x4e, 0x53, 0x20, 0x72, 0x65, 0x63,
+ 0x6f, 0x72, 0x64, 0x73, 0x20, 0x28, 0x65, 0x2e, 0x67, 0x2e, 0x2c, 0x20,
+ 0x41, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49, 0x50, 0x76, 0x34, 0x0a, 0x61,
+ 0x6e, 0x64, 0x20, 0x41, 0x41, 0x41, 0x41, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x49, 0x50, 0x76, 0x36, 0x29, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x6f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x0a,
+ 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x44, 0x4e, 0x53, 0x20, 0x72, 0x65,
+ 0x63, 0x6f, 0x72, 0x64, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x20, 0x70,
+ 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x62, 0x65, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x0a, 0x65, 0x78, 0x70, 0x6c,
+ 0x69, 0x63, 0x69, 0x74, 0x6c, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x61,
+ 0x79, 0x20, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2e, 0x0a, 0x0a,
+ 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x55, 0x53, 0x2d, 0x41, 0x53, 0x43, 0x49, 0x49, 0x20,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x20, 0x20, 0x54,
+ 0x68, 0x65, 0x69, 0x72, 0x20, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63,
+ 0x61, 0x6c, 0x0a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x75, 0x73,
+ 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65,
+ 0x20, 0x55, 0x53, 0x2d, 0x41, 0x53, 0x43, 0x49, 0x49, 0x20, 0x63, 0x68,
+ 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x20, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
+ 0x69, 0x7a, 0x65, 0x64, 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
+ 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62,
+ 0x65, 0x20, 0x41, 0x2d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x65, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x35, 0x38,
+ 0x39, 0x30, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x20, 0x39, 0x35, 0x32,
+ 0x3a, 0x20, 0x44, 0x6f, 0x44, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x54, 0x61, 0x62, 0x6c,
+ 0x65, 0x20, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x31, 0x30, 0x33, 0x34,
+ 0x3a, 0x20, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x4e, 0x61, 0x6d,
+ 0x65, 0x73, 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74,
+ 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x46, 0x61, 0x63, 0x69, 0x6c, 0x69,
+ 0x74, 0x69, 0x65, 0x73, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x31, 0x31, 0x32,
+ 0x33, 0x3a, 0x20, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x20, 0x2d,
+ 0x2d, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
+ 0x0a, 0x52, 0x46, 0x43, 0x20, 0x32, 0x37, 0x38, 0x32, 0x3a, 0x20, 0x41,
+ 0x20, 0x44, 0x4e, 0x53, 0x20, 0x52, 0x52, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28,
+ 0x44, 0x4e, 0x53, 0x20, 0x53, 0x52, 0x56, 0x29, 0x0a, 0x52, 0x46, 0x43,
+ 0x20, 0x35, 0x38, 0x39, 0x30, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x4e, 0x61, 0x6d, 0x65,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x49, 0x44, 0x4e, 0x41, 0x29, 0x3a,
+ 0x20, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x22,
+ 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x68, 0x6f,
+ 0x73, 0x74, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69,
+ 0x6f, 0x6e, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65,
+ 0x74, 0x3a, 0x69, 0x70, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x3b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x64,
+ 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x7d,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x22, 0x54, 0x68, 0x65, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x73, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x20,
+ 0x49, 0x50, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x6f,
+ 0x72, 0x20, 0x61, 0x20, 0x44, 0x4e, 0x53, 0x0a, 0x64, 0x6f, 0x6d, 0x61,
+ 0x69, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x22, 0x3b, 0x7d, 0x74,
+ 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x72, 0x69, 0x20, 0x7b,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x22, 0x54, 0x68, 0x65, 0x20, 0x75, 0x72, 0x69, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73,
+ 0x20, 0x61, 0x20, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x52,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x49, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x0a, 0x28, 0x55, 0x52, 0x49, 0x29,
+ 0x20, 0x61, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x53, 0x54, 0x44, 0x20, 0x36, 0x36, 0x2e, 0x0a, 0x0a,
+ 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x72, 0x69, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, 0x65, 0x20, 0x69,
+ 0x6e, 0x20, 0x55, 0x53, 0x2d, 0x41, 0x53, 0x43, 0x49, 0x49, 0x20, 0x65,
+ 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2c, 0x0a, 0x61, 0x6e, 0x64,
+ 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72,
+ 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20,
+ 0x52, 0x46, 0x43, 0x20, 0x33, 0x39, 0x38, 0x36, 0x20, 0x53, 0x65, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x36, 0x2e, 0x32, 0x2e, 0x31, 0x2c,
+ 0x20, 0x36, 0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x31, 0x2c, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x36, 0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x20, 0x20,
+ 0x41, 0x6c, 0x6c, 0x20, 0x75, 0x6e, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73,
+ 0x61, 0x72, 0x79, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x2d,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x2c, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2d, 0x69, 0x6e,
+ 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x0a, 0x63, 0x68,
+ 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65,
+ 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65,
+ 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68, 0x65, 0x78, 0x61, 0x64, 0x65, 0x63,
+ 0x69, 0x6d, 0x61, 0x6c, 0x0a, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x2c,
+ 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e,
+ 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f,
+ 0x20, 0x75, 0x70, 0x70, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x61,
+ 0x73, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x0a, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x36,
+ 0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65,
+ 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69,
+ 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f,
+ 0x20, 0x68, 0x65, 0x6c, 0x70, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
+ 0x65, 0x0a, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x55, 0x52, 0x49,
+ 0x73, 0x2e, 0x20, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61,
+ 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20,
+ 0x6e, 0x6f, 0x74, 0x0a, 0x73, 0x75, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65,
+ 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
+ 0x65, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x6e, 0x65, 0x73, 0x73,
+ 0x2e, 0x20, 0x20, 0x54, 0x77, 0x6f, 0x20, 0x55, 0x52, 0x49, 0x73, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x74, 0x65, 0x78,
+ 0x74, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x64, 0x69, 0x73, 0x74, 0x69,
+ 0x6e, 0x63, 0x74, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x73, 0x74, 0x69,
+ 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x0a, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61,
+ 0x6c, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x4f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x72, 0x69, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6d, 0x61,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x20, 0x74,
+ 0x68, 0x61, 0x74, 0x0a, 0x74, 0x68, 0x65, 0x79, 0x20, 0x70, 0x65, 0x72,
+ 0x6d, 0x69, 0x74, 0x2e, 0x20, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x20, 0x27, 0x64, 0x61, 0x74, 0x61,
+ 0x3a, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x27, 0x75, 0x72, 0x6e, 0x3a,
+ 0x27, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x0a, 0x6d, 0x69,
+ 0x67, 0x68, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61,
+ 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x0a,
+ 0x0a, 0x41, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x6c, 0x65, 0x6e, 0x67,
+ 0x74, 0x68, 0x20, 0x55, 0x52, 0x49, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f,
+ 0x74, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x55, 0x52,
+ 0x49, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x61, 0x6e,
+ 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x0a,
+ 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x27, 0x55, 0x52, 0x49,
+ 0x20, 0x61, 0x62, 0x73, 0x65, 0x6e, 0x74, 0x27, 0x20, 0x77, 0x68, 0x65,
+ 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x2e,
+ 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69,
+ 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73,
+ 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e,
+ 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x72, 0x69,
+ 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75,
+ 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x35, 0x30, 0x31, 0x37, 0x2e, 0x22, 0x3b,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52,
+ 0x46, 0x43, 0x20, 0x33, 0x39, 0x38, 0x36, 0x3a, 0x20, 0x55, 0x6e, 0x69,
+ 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x20, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
+ 0x20, 0x28, 0x55, 0x52, 0x49, 0x29, 0x3a, 0x20, 0x47, 0x65, 0x6e, 0x65,
+ 0x72, 0x69, 0x63, 0x20, 0x53, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x0a, 0x52,
+ 0x46, 0x43, 0x20, 0x33, 0x33, 0x30, 0x35, 0x3a, 0x20, 0x52, 0x65, 0x70,
+ 0x6f, 0x72, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x4a, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x57, 0x33, 0x43, 0x2f, 0x49,
+ 0x45, 0x54, 0x46, 0x20, 0x55, 0x52, 0x49, 0x20, 0x50, 0x6c, 0x61, 0x6e,
+ 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73,
+ 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x3a, 0x20, 0x55, 0x6e, 0x69, 0x66,
+ 0x6f, 0x72, 0x6d, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x20, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73,
+ 0x20, 0x28, 0x55, 0x52, 0x49, 0x73, 0x29, 0x2c, 0x20, 0x55, 0x52, 0x4c,
+ 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72,
+ 0x6d, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4e,
+ 0x61, 0x6d, 0x65, 0x73, 0x20, 0x28, 0x55, 0x52, 0x4e, 0x73, 0x29, 0x3a,
+ 0x20, 0x43, 0x6c, 0x61, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x52,
+ 0x46, 0x43, 0x20, 0x35, 0x30, 0x31, 0x37, 0x3a, 0x20, 0x4d, 0x49, 0x42,
+ 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x73, 0x20, 0x28, 0x55, 0x52, 0x49, 0x73, 0x29, 0x22,
+ 0x3b, 0x7d, 0x7d, 0x00
+};
diff --git a/models/ietf-inet-types@2013-07-15.yang b/models/ietf-inet-types@2013-07-15.yang
new file mode 100644
index 0000000..2f14270
--- /dev/null
+++ b/models/ietf-inet-types@2013-07-15.yang
@@ -0,0 +1,457 @@
+module ietf-inet-types {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+ prefix "inet";
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: David Kessens
+ <mailto:david.kessens@nsn.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>";
+
+ description
+ "This module contains a collection of generally useful derived
+ YANG data types for Internet addresses and related things.
+
+ Copyright (c) 2013 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6991; see
+ the RFC itself for full legal notices.";
+
+ revision 2013-07-15 {
+ description
+ "This revision adds the following new data types:
+ - ip-address-no-zone
+ - ipv4-address-no-zone
+ - ipv6-address-no-zone";
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ revision 2010-09-24 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6021: Common YANG Data Types";
+ }
+
+ /*** collection of types related to protocol fields ***/
+
+ typedef ip-version {
+ type enumeration {
+ enum unknown {
+ value "0";
+ description
+ "An unknown or unspecified version of the Internet
+ protocol.";
+ }
+ enum ipv4 {
+ value "1";
+ description
+ "The IPv4 protocol as defined in RFC 791.";
+ }
+ enum ipv6 {
+ value "2";
+ description
+ "The IPv6 protocol as defined in RFC 2460.";
+ }
+ }
+ description
+ "This value represents the version of the IP protocol.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetVersion textual convention of the SMIv2.";
+ reference
+ "RFC 791: Internet Protocol
+ RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ typedef dscp {
+ type uint8 {
+ range "0..63";
+ }
+ description
+ "The dscp type represents a Differentiated Services Code Point
+ that may be used for marking packets in a traffic stream.
+ In the value set and its semantics, this type is equivalent
+ to the Dscp textual convention of the SMIv2.";
+ reference
+ "RFC 3289: Management Information Base for the Differentiated
+ Services Architecture
+ RFC 2474: Definition of the Differentiated Services Field
+ (DS Field) in the IPv4 and IPv6 Headers
+ RFC 2780: IANA Allocation Guidelines For Values In
+ the Internet Protocol and Related Headers";
+ }
+
+ typedef ipv6-flow-label {
+ type uint32 {
+ range "0..1048575";
+ }
+ description
+ "The ipv6-flow-label type represents the flow identifier or Flow
+ Label in an IPv6 packet header that may be used to
+ discriminate traffic flows.
+
+ In the value set and its semantics, this type is equivalent
+ to the IPv6FlowLabel textual convention of the SMIv2.";
+ reference
+ "RFC 3595: Textual Conventions for IPv6 Flow Label
+ RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+ }
+
+ typedef port-number {
+ type uint16 {
+ range "0..65535";
+ }
+ description
+ "The port-number type represents a 16-bit port number of an
+ Internet transport-layer protocol such as UDP, TCP, DCCP, or
+ SCTP. Port numbers are assigned by IANA. A current list of
+ all assignments is available from <http://www.iana.org/>.
+
+ Note that the port number value zero is reserved by IANA. In
+ situations where the value zero does not make sense, it can
+ be excluded by subtyping the port-number type.
+ In the value set and its semantics, this type is equivalent
+ to the InetPortNumber textual convention of the SMIv2.";
+ reference
+ "RFC 768: User Datagram Protocol
+ RFC 793: Transmission Control Protocol
+ RFC 4960: Stream Control Transmission Protocol
+ RFC 4340: Datagram Congestion Control Protocol (DCCP)
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ /*** collection of types related to autonomous systems ***/
+
+ typedef as-number {
+ type uint32;
+ description
+ "The as-number type represents autonomous system numbers
+ which identify an Autonomous System (AS). An AS is a set
+ of routers under a single technical administration, using
+ an interior gateway protocol and common metrics to route
+ packets within the AS, and using an exterior gateway
+ protocol to route packets to other ASes. IANA maintains
+ the AS number space and has delegated large parts to the
+ regional registries.
+
+ Autonomous system numbers were originally limited to 16
+ bits. BGP extensions have enlarged the autonomous system
+ number space to 32 bits. This type therefore uses an uint32
+ base type without a range restriction in order to support
+ a larger autonomous system number space.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetAutonomousSystemNumber textual convention of
+ the SMIv2.";
+ reference
+ "RFC 1930: Guidelines for creation, selection, and registration
+ of an Autonomous System (AS)
+ RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+ RFC 4001: Textual Conventions for Internet Network Addresses
+ RFC 6793: BGP Support for Four-Octet Autonomous System (AS)
+ Number Space";
+ }
+
+ /*** collection of types related to IP addresses and hostnames ***/
+
+ typedef ip-address {
+ type union {
+ type inet:ipv4-address;
+ type inet:ipv6-address;
+ }
+ description
+ "The ip-address type represents an IP address and is IP
+ version neutral. The format of the textual representation
+ implies the IP version. This type supports scoped addresses
+ by allowing zone identifiers in the address format.";
+ reference
+ "RFC 4007: IPv6 Scoped Address Architecture";
+ }
+
+ typedef ipv4-address {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '(%[\p{N}\p{L}]+)?';
+ }
+ description
+ "The ipv4-address type represents an IPv4 address in
+ dotted-quad notation. The IPv4 address may include a zone
+ index, separated by a % sign.
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format for the zone index is the numerical
+ format";
+ }
+
+ typedef ipv6-address {
+ type string {
+ pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ + '(%[\p{N}\p{L}]+)?';
+ pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ + '(%.+)?';
+ }
+ description
+ "The ipv6-address type represents an IPv6 address in full,
+ mixed, shortened, and shortened-mixed notation. The IPv6
+ address may include a zone index, separated by a % sign.
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format of IPv6 addresses uses the textual
+ representation defined in Section 4 of RFC 5952. The
+ canonical format for the zone index is the numerical
+ format as described in Section 11.2 of RFC 4007.";
+ reference
+ "RFC 4291: IP Version 6 Addressing Architecture
+ RFC 4007: IPv6 Scoped Address Architecture
+ RFC 5952: A Recommendation for IPv6 Address Text
+ Representation";
+ }
+
+ typedef ip-address-no-zone {
+ type union {
+ type inet:ipv4-address-no-zone;
+ type inet:ipv6-address-no-zone;
+ }
+ description
+ "The ip-address-no-zone type represents an IP address and is
+ IP version neutral. The format of the textual representation
+ implies the IP version. This type does not support scoped
+ addresses since it does not allow zone identifiers in the
+ address format.";
+ reference
+ "RFC 4007: IPv6 Scoped Address Architecture";
+ }
+
+ typedef ipv4-address-no-zone {
+ type inet:ipv4-address {
+ pattern '[0-9\.]*';
+ }
+ description
+ "An IPv4 address without a zone index. This type, derived from
+ ipv4-address, may be used in situations where the zone is
+ known from the context and hence no zone index is needed.";
+ }
+
+ typedef ipv6-address-no-zone {
+ type inet:ipv6-address {
+ pattern '[0-9a-fA-F:\.]*';
+ }
+ description
+ "An IPv6 address without a zone index. This type, derived from
+ ipv6-address, may be used in situations where the zone is
+ known from the context and hence no zone index is needed.";
+ reference
+ "RFC 4291: IP Version 6 Addressing Architecture
+ RFC 4007: IPv6 Scoped Address Architecture
+ RFC 5952: A Recommendation for IPv6 Address Text
+ Representation";
+ }
+
+ typedef ip-prefix {
+ type union {
+ type inet:ipv4-prefix;
+ type inet:ipv6-prefix;
+ }
+ description
+ "The ip-prefix type represents an IP prefix and is IP
+ version neutral. The format of the textual representations
+ implies the IP version.";
+ }
+
+ typedef ipv4-prefix {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+ }
+ description
+ "The ipv4-prefix type represents an IPv4 address prefix.
+ The prefix length is given by the number following the
+ slash character and must be less than or equal to 32.
+
+ A prefix length value of n corresponds to an IP address
+ mask that has n contiguous 1-bits from the most
+ significant bit (MSB) and all other bits set to 0.
+
+ The canonical format of an IPv4 prefix has all bits of
+ the IPv4 address set to zero that are not part of the
+ IPv4 prefix.";
+ }
+
+ typedef ipv6-prefix {
+ type string {
+ pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+ pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ + '(/.+)';
+ }
+ description
+ "The ipv6-prefix type represents an IPv6 address prefix.
+ The prefix length is given by the number following the
+ slash character and must be less than or equal to 128.
+
+ A prefix length value of n corresponds to an IP address
+ mask that has n contiguous 1-bits from the most
+ significant bit (MSB) and all other bits set to 0.
+
+ The IPv6 address should have all bits that do not belong
+ to the prefix set to zero.
+
+ The canonical format of an IPv6 prefix has all bits of
+ the IPv6 address set to zero that are not part of the
+ IPv6 prefix. Furthermore, the IPv6 address is represented
+ as defined in Section 4 of RFC 5952.";
+ reference
+ "RFC 5952: A Recommendation for IPv6 Address Text
+ Representation";
+ }
+
+ /*** collection of domain name and URI types ***/
+
+ typedef domain-name {
+ type string {
+ length "1..253";
+ pattern
+ '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+ + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+ + '|\.';
+ }
+ description
+ "The domain-name type represents a DNS domain name. The
+ name SHOULD be fully qualified whenever possible.
+
+ Internet domain names are only loosely specified. Section
+ 3.5 of RFC 1034 recommends a syntax (modified in Section
+ 2.1 of RFC 1123). The pattern above is intended to allow
+ for current practice in domain name use, and some possible
+ future expansion. It is designed to hold various types of
+ domain names, including names used for A or AAAA records
+ (host names) and other records, such as SRV records. Note
+ that Internet host names have a stricter syntax (described
+ in RFC 952) than the DNS recommendations in RFCs 1034 and
+ 1123, and that systems that want to store host names in
+ schema nodes using the domain-name type are recommended to
+ adhere to this stricter standard to ensure interoperability.
+
+ The encoding of DNS names in the DNS protocol is limited
+ to 255 characters. Since the encoding consists of labels
+ prefixed by a length bytes and there is a trailing NULL
+ byte, only 253 characters can appear in the textual dotted
+ notation.
+
+ The description clause of schema nodes using the domain-name
+ type MUST describe when and how these names are resolved to
+ IP addresses. Note that the resolution of a domain-name value
+ may require to query multiple DNS records (e.g., A for IPv4
+ and AAAA for IPv6). The order of the resolution process and
+ which DNS record takes precedence can either be defined
+ explicitly or may depend on the configuration of the
+ resolver.
+
+ Domain-name values use the US-ASCII encoding. Their canonical
+ format uses lowercase US-ASCII characters. Internationalized
+ domain names MUST be A-labels as per RFC 5890.";
+ reference
+ "RFC 952: DoD Internet Host Table Specification
+ RFC 1034: Domain Names - Concepts and Facilities
+ RFC 1123: Requirements for Internet Hosts -- Application
+ and Support
+ RFC 2782: A DNS RR for specifying the location of services
+ (DNS SRV)
+ RFC 5890: Internationalized Domain Names in Applications
+ (IDNA): Definitions and Document Framework";
+ }
+
+ typedef host {
+ type union {
+ type inet:ip-address;
+ type inet:domain-name;
+ }
+ description
+ "The host type represents either an IP address or a DNS
+ domain name.";
+ }
+
+ typedef uri {
+ type string;
+ description
+ "The uri type represents a Uniform Resource Identifier
+ (URI) as defined by STD 66.
+
+ Objects using the uri type MUST be in US-ASCII encoding,
+ and MUST be normalized as described by RFC 3986 Sections
+ 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary
+ percent-encoding is removed, and all case-insensitive
+ characters are set to lowercase except for hexadecimal
+ digits, which are normalized to uppercase as described in
+ Section 6.2.2.1.
+
+ The purpose of this normalization is to help provide
+ unique URIs. Note that this normalization is not
+ sufficient to provide uniqueness. Two URIs that are
+ textually distinct after this normalization may still be
+ equivalent.
+
+ Objects using the uri type may restrict the schemes that
+ they permit. For example, 'data:' and 'urn:' schemes
+ might not be appropriate.
+
+ A zero-length URI is not a valid URI. This can be used to
+ express 'URI absent' where required.
+
+ In the value set and its semantics, this type is equivalent
+ to the Uri SMIv2 textual convention defined in RFC 5017.";
+ reference
+ "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+ RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+ Group: Uniform Resource Identifiers (URIs), URLs,
+ and Uniform Resource Names (URNs): Clarifications
+ and Recommendations
+ RFC 5017: MIB Textual Conventions for Uniform Resource
+ Identifiers (URIs)";
+ }
+
+}
diff --git a/models/ietf-yang-library@2019-01-04.h b/models/ietf-yang-library@2019-01-04.h
new file mode 100644
index 0000000..2344df3
--- /dev/null
+++ b/models/ietf-yang-library@2019-01-04.h
@@ -0,0 +1,1497 @@
+unsigned char ietf_yang_library_2019_01_04_yang[] = {
+ 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65,
+ 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72,
+ 0x61, 0x72, 0x79, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x79,
+ 0x61, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x31, 0x2e, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c,
+ 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e,
+ 0x67, 0x6c, 0x69, 0x62, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x66,
+ 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
+ 0x52, 0x46, 0x43, 0x20, 0x36, 0x39, 0x39, 0x31, 0x3a, 0x20, 0x43, 0x6f,
+ 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61,
+ 0x74, 0x61, 0x20, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69,
+ 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x69,
+ 0x6e, 0x65, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69,
+ 0x78, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x52,
+ 0x46, 0x43, 0x20, 0x36, 0x39, 0x39, 0x31, 0x3a, 0x20, 0x43, 0x6f, 0x6d,
+ 0x6d, 0x6f, 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74,
+ 0x61, 0x20, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d,
+ 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x64, 0x61,
+ 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x20, 0x64, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20,
+ 0x38, 0x33, 0x34, 0x32, 0x3a, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x41,
+ 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x4e, 0x4d, 0x44,
+ 0x41, 0x29, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69,
+ 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x22, 0x49, 0x45, 0x54, 0x46, 0x20, 0x4e, 0x45, 0x54, 0x43,
+ 0x4f, 0x4e, 0x46, 0x20, 0x28, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x29, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x20,
+ 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x22, 0x57, 0x47, 0x20, 0x57, 0x65, 0x62, 0x3a,
+ 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x64, 0x61, 0x74, 0x61, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x2e,
+ 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x67, 0x2f,
+ 0x6e, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x2f, 0x3e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x57, 0x47, 0x20, 0x4c, 0x69, 0x73,
+ 0x74, 0x3a, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a,
+ 0x6e, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x40, 0x69, 0x65, 0x74, 0x66,
+ 0x2e, 0x6f, 0x72, 0x67, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20,
+ 0x20, 0x41, 0x6e, 0x64, 0x79, 0x20, 0x42, 0x69, 0x65, 0x72, 0x6d, 0x61,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69,
+ 0x6c, 0x74, 0x6f, 0x3a, 0x61, 0x6e, 0x64, 0x79, 0x40, 0x79, 0x75, 0x6d,
+ 0x61, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4d, 0x61, 0x72, 0x74, 0x69,
+ 0x6e, 0x20, 0x42, 0x6a, 0x6f, 0x72, 0x6b, 0x6c, 0x75, 0x6e, 0x64, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74,
+ 0x6f, 0x3a, 0x6d, 0x62, 0x6a, 0x40, 0x74, 0x61, 0x69, 0x6c, 0x2d, 0x66,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20,
+ 0x20, 0x4a, 0x75, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x20, 0x53, 0x63, 0x68,
+ 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f,
+ 0x3a, 0x6a, 0x2e, 0x73, 0x63, 0x68, 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65,
+ 0x6c, 0x64, 0x65, 0x72, 0x40, 0x6a, 0x61, 0x63, 0x6f, 0x62, 0x73, 0x2d,
+ 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x69, 0x74, 0x79, 0x2e, 0x64,
+ 0x65, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4b, 0x65,
+ 0x6e, 0x74, 0x20, 0x57, 0x61, 0x74, 0x73, 0x65, 0x6e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a,
+ 0x6b, 0x65, 0x6e, 0x74, 0x2b, 0x69, 0x65, 0x74, 0x66, 0x40, 0x77, 0x61,
+ 0x74, 0x73, 0x65, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x3e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x3a, 0x20, 0x20, 0x20, 0x52, 0x6f, 0x62, 0x65, 0x72, 0x74, 0x20,
+ 0x57, 0x69, 0x6c, 0x74, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x72, 0x77, 0x69,
+ 0x6c, 0x74, 0x6f, 0x6e, 0x40, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69,
+ 0x64, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2c, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72,
+ 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x20, 0x75, 0x73,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x27, 0x4d, 0x55, 0x53, 0x54, 0x27,
+ 0x2c, 0x20, 0x27, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x27,
+ 0x2c, 0x20, 0x27, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x27,
+ 0x2c, 0x20, 0x27, 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x27, 0x2c, 0x20, 0x27,
+ 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x4e, 0x4f, 0x54, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x48, 0x4f,
+ 0x55, 0x4c, 0x44, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x48, 0x4f, 0x55, 0x4c,
+ 0x44, 0x20, 0x4e, 0x4f, 0x54, 0x27, 0x2c, 0x20, 0x27, 0x52, 0x45, 0x43,
+ 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x44, 0x45, 0x44, 0x27, 0x2c, 0x20, 0x27,
+ 0x4e, 0x4f, 0x54, 0x20, 0x52, 0x45, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e,
+ 0x44, 0x45, 0x44, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x27, 0x4d, 0x41, 0x59, 0x27, 0x2c, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x27, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x27, 0x20,
+ 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x64, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x74, 0x6f, 0x20,
+ 0x62, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74,
+ 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x20, 0x42, 0x43, 0x50, 0x20, 0x31, 0x34, 0x20, 0x28, 0x52,
+ 0x46, 0x43, 0x20, 0x32, 0x31, 0x31, 0x39, 0x29, 0x20, 0x28, 0x52, 0x46,
+ 0x43, 0x20, 0x38, 0x31, 0x37, 0x34, 0x29, 0x20, 0x77, 0x68, 0x65, 0x6e,
+ 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x77,
+ 0x68, 0x65, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72,
+ 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x61, 0x70, 0x69,
+ 0x74, 0x61, 0x6c, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x73, 0x68, 0x6f,
+ 0x77, 0x6e, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
+ 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x39,
+ 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, 0x72, 0x73,
+ 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x20, 0x20,
+ 0x41, 0x6c, 0x6c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x72,
+ 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72,
+ 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x6f, 0x64,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69,
+ 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20,
+ 0x70, 0x75, 0x72, 0x73, 0x75, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
+ 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x42,
+ 0x53, 0x44, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x20, 0x66,
+ 0x6f, 0x72, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e, 0x63, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x27, 0x73, 0x20, 0x4c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x50, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x6f, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x44, 0x6f,
+ 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x2e, 0x69, 0x65, 0x74,
+ 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
+ 0x65, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x29, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x76,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f,
+ 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x38, 0x35, 0x32, 0x35, 0x3b, 0x20,
+ 0x73, 0x65, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x52, 0x46, 0x43, 0x20, 0x69, 0x74, 0x73, 0x65,
+ 0x6c, 0x66, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20,
+ 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65,
+ 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
+ 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x39,
+ 0x2d, 0x30, 0x31, 0x2d, 0x30, 0x34, 0x20, 0x7b, 0x0a, 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, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x73, 0x75, 0x70, 0x70,
+ 0x6f, 0x72, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74,
+ 0x69, 0x70, 0x6c, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f,
+ 0x72, 0x65, 0x73, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x72, 0x64, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20,
+ 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65,
+ 0x20, 0x28, 0x4e, 0x4d, 0x44, 0x41, 0x29, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x38, 0x35, 0x32, 0x35, 0x3a, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x34, 0x2d, 0x30, 0x39, 0x20, 0x7b,
+ 0x0a, 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, 0x22, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
+ 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x37, 0x38, 0x39,
+ 0x35, 0x3a, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x4d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a,
+ 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x73, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x72, 0x65, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x72, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
+ 0x67, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5c, 0x64,
+ 0x7b, 0x34, 0x7d, 0x2d, 0x5c, 0x64, 0x7b, 0x32, 0x7d, 0x2d, 0x5c, 0x64,
+ 0x7b, 0x32, 0x7d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 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, 0x22, 0x52, 0x65, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x70, 0x65,
+ 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x64, 0x61, 0x74, 0x65, 0x20, 0x69,
+ 0x6e, 0x20, 0x59, 0x59, 0x59, 0x59, 0x2d, 0x4d, 0x4d, 0x2d, 0x44, 0x44,
+ 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x47,
+ 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x73,
+ 0x20, 0x7b, 0x0a, 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, 0x22, 0x50, 0x61, 0x72, 0x61,
+ 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x59,
+ 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67,
+ 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x54,
+ 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x72, 0x65, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73,
+ 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x20,
+ 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73,
+ 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2c, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x69, 0x73, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61,
+ 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e,
+ 0x67, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c,
+ 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0a, 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, 0x22, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x70, 0x61, 0x72,
+ 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20,
+ 0x61, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6c, 0x6f, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e,
+ 0x65, 0x74, 0x3a, 0x75, 0x72, 0x69, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x73, 0x20, 0x61, 0x20, 0x55, 0x52, 0x4c, 0x20, 0x74, 0x68, 0x61, 0x74,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x73, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f,
+ 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65,
+ 0x6e, 0x74, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x61, 0x20, 0x55, 0x52, 0x4c, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x76, 0x61,
+ 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72,
+ 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x6e, 0x74, 0x72,
+ 0x79, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x69, 0x6d, 0x70, 0x6c, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x70, 0x61,
+ 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x7b, 0x0a, 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, 0x22, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
+ 0x72, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6d,
+ 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x66, 0x65, 0x61,
+ 0x74, 0x75, 0x72, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e,
+ 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20,
+ 0x61, 0x6c, 0x6c, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x66, 0x65, 0x61,
+ 0x74, 0x75, 0x72, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x66,
+ 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x62,
+ 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x2c, 0x20, 0x72, 0x65, 0x67, 0x61, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73,
+ 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65,
+ 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
+ 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69,
+ 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x64,
+ 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61,
+ 0x74, 0x68, 0x20, 0x22, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
+ 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, 0x20, 0x22, 0x4c, 0x69, 0x73, 0x74,
+ 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63,
+ 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74,
+ 0x68, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x20, 0x20,
+ 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x6c,
+ 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61,
+ 0x6d, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x4d, 0x41, 0x59,
+ 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68,
+ 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20,
+ 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27, 0x20, 0x65, 0x6e, 0x74,
+ 0x72, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x4d, 0x55,
+ 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x28, 0x64, 0x69, 0x72, 0x65,
+ 0x63, 0x74, 0x6c, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x64, 0x69,
+ 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x65,
+ 0x76, 0x69, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x6f, 0x62,
+ 0x75, 0x73, 0x74, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x20,
+ 0x6d, 0x61, 0x79, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20,
+ 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x73, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x68, 0x61, 0x6e, 0x64,
+ 0x6c, 0x65, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x74, 0x75, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74,
+ 0x65, 0x73, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x28, 0x64,
+ 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x6f, 0x72, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69,
+ 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x29, 0x20, 0x67,
+ 0x72, 0x61, 0x63, 0x65, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67,
+ 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x61, 0x72, 0x61, 0x6d,
+ 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x7b, 0x0a, 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,
+ 0x22, 0x41, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x61,
+ 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x61,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2e,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x41, 0x6e, 0x20, 0x61, 0x72, 0x62, 0x69,
+ 0x74, 0x72, 0x61, 0x72, 0x79, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x20, 0x73, 0x65, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b,
+ 0x65, 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x20,
+ 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, 0x20, 0x22, 0x41, 0x6e, 0x20, 0x65,
+ 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73,
+ 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65,
+ 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x70, 0x65, 0x72, 0x20,
+ 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x35, 0x2e, 0x36, 0x2e,
+ 0x35, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35,
+ 0x30, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x70, 0x61,
+ 0x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x6f, 0x66, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
+ 0x64, 0x20, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65,
+ 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
+ 0x6c, 0x65, 0x61, 0x66, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d,
+ 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x75, 0x72, 0x69, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20,
+ 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x58, 0x4d, 0x4c, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75,
+ 0x73, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73,
+ 0x74, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6b, 0x65, 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 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, 0x20, 0x20,
+ 0x20, 0x22, 0x45, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20,
+ 0x6f, 0x6e, 0x65, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x65,
+ 0x61, 0x66, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c,
+ 0x69, 0x73, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x2d, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2d, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
+ 0x72, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74,
+ 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x2d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x22,
+ 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x22, 0x3b, 0x0a, 0x20, 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, 0x20,
+ 0x22, 0x41, 0x6e, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x6e,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x69,
+ 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x75,
+ 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64,
+ 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x62, 0x75, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74,
+ 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61,
+ 0x6e, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2d,
+ 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f,
+ 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
+ 0x20, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x4d, 0x41,
+ 0x59, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x2e, 0x20, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72,
+ 0x20, 0x69, 0x66, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x69, 0x6d, 0x70,
+ 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x62, 0x75, 0x74, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x64, 0x69, 0x66, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x2d,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20,
+ 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
+ 0x6e, 0x67, 0x74, 0x68, 0x20, 0x22, 0x30, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 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, 0x20,
+ 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+ 0x68, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x20, 0x72,
+ 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x20, 0x70,
+ 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e,
+ 0x65, 0x74, 0x3a, 0x75, 0x72, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61,
+ 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22,
+ 0x54, 0x68, 0x65, 0x20, 0x58, 0x4d, 0x4c, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+ 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73,
+ 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x65,
+ 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x73,
+ 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65,
+ 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 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, 0x20, 0x20, 0x20, 0x22, 0x45,
+ 0x61, 0x63, 0x68, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x72, 0x65,
+ 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x65,
+ 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x73,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67,
+ 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x79, 0x61, 0x6e, 0x67,
+ 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2d, 0x70, 0x61, 0x72,
+ 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x7b, 0x0a, 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, 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20,
+ 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x69,
+ 0x73, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x65,
+ 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x6f, 0x20, 0x69, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20,
+ 0x62, 0x65, 0x20, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e,
+ 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72,
+ 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d,
+ 0x73, 0x65, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65,
+ 0x22, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22,
+ 0x41, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61,
+ 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65,
+ 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20,
+ 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x68, 0x61, 0x76,
+ 0x65, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x2e, 0x65, 0x2e,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f,
+ 0x74, 0x68, 0x65, 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+ 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x2d,
+ 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x73, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65,
+ 0x22, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22,
+ 0x41, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20,
+ 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
+ 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f,
+ 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x73, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x76,
+ 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x63,
+ 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2c, 0x20, 0x69, 0x2e, 0x65,
+ 0x2e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63,
+ 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x61, 0x74, 0x69, 0x73, 0x66,
+ 0x79, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69,
+ 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x73, 0x70, 0x65,
+ 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 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, 0x20, 0x20, 0x20, 0x22, 0x41, 0x6e,
+ 0x20, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69,
+ 0x73, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x73, 0x65,
+ 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x66,
+ 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20,
+ 0x22, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x2d, 0x73, 0x65, 0x74, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 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, 0x20,
+ 0x20, 0x20, 0x22, 0x41, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x63,
+ 0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x49, 0x66, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x69, 0x6d, 0x70,
+ 0x6f, 0x72, 0x74, 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x20,
+ 0x69, 0x6e, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74,
+ 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x66,
+ 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74,
+ 0x6f, 0x72, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65,
+ 0x22, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22,
+ 0x41, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20,
+ 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x73, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x20, 0x69, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70,
+ 0x6f, 0x72, 0x74, 0x73, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x69,
+ 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x20, 0x6f,
+ 0x6e, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x70, 0x65,
+ 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x74, 0x20,
+ 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x45, 0x61,
+ 0x63, 0x68, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x20, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x61, 0x6d, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x64, 0x73, 0x3a, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74,
+ 0x6f, 0x72, 0x65, 0x2d, 0x72, 0x65, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 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, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68,
+ 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74,
+ 0x6f, 0x72, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68,
+ 0x20, 0x22, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20,
+ 0x20, 0x20, 0x22, 0x41, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x6c, 0x6c, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x69, 0x6d, 0x70, 0x6f,
+ 0x72, 0x74, 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6d,
+ 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x61,
+ 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x66, 0x65,
+ 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64,
+ 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x54, 0x6f, 0x70,
+ 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61,
+ 0x69, 0x6e, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a,
+ 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c,
+ 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x66,
+ 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 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, 0x22, 0x43,
+ 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x68, 0x6f, 0x6c,
+ 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74,
+ 0x69, 0x72, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6c, 0x69, 0x62,
+ 0x72, 0x61, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x79,
+ 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2d,
+ 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20,
+ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x69, 0x64, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x64,
+ 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a,
+ 0x20, 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, 0x20, 0x22, 0x41, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
+ 0x74, 0x65, 0x64, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x27, 0x2f, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62,
+ 0x72, 0x61, 0x72, 0x79, 0x27, 0x20, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+ 0x4d, 0x55, 0x53, 0x54, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x69,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73,
+ 0x65, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x27, 0x2f, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72,
+ 0x61, 0x72, 0x79, 0x27, 0x20, 0x74, 0x72, 0x65, 0x65, 0x2c, 0x20, 0x65,
+ 0x78, 0x63, 0x65, 0x70, 0x74, 0x20, 0x27, 0x2f, 0x79, 0x61, 0x6e, 0x67,
+ 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
+ 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x69, 0x64, 0x27, 0x2c, 0x20, 0x68, 0x61,
+ 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f,
+ 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x4e, 0x6f,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62,
+ 0x72, 0x61, 0x72, 0x79, 0x2d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20,
+ 0x7b, 0x0a, 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, 0x22, 0x47, 0x65, 0x6e, 0x65, 0x72,
+ 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61,
+ 0x72, 0x79, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+ 0x74, 0x2d, 0x69, 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x22,
+ 0x2f, 0x79, 0x61, 0x6e, 0x67, 0x6c, 0x69, 0x62, 0x3a, 0x79, 0x61, 0x6e,
+ 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2f, 0x79, 0x61,
+ 0x6e, 0x67, 0x6c, 0x69, 0x62, 0x3a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+ 0x74, 0x2d, 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79,
+ 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6c, 0x69,
+ 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+ 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6c, 0x69,
+ 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69,
+ 0x73, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20,
+ 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x69, 0x6e, 0x67, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a,
+ 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x6f, 0x75,
+ 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d,
+ 0x6c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70,
+ 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 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, 0x22, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+ 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65,
+ 0x73, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20,
+ 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x20, 0x69, 0x74,
+ 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x75, 0x73,
+ 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x61,
+ 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6d, 0x6f, 0x6e, 0x69, 0x74,
+ 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x74, 0x72,
+ 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69,
+ 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2d, 0x6c, 0x65,
+ 0x61, 0x66, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65,
+ 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x43, 0x6f, 0x6d, 0x6d, 0x6f,
+ 0x6e, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75,
+ 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61,
+ 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41,
+ 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72,
+ 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x72, 0x65, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x22,
+ 0x30, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20,
+ 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75,
+ 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x41, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x6c, 0x65, 0x6e, 0x67,
+ 0x74, 0x68, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73,
+ 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x20,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x20,
+ 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67,
+ 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64,
+ 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
+ 0x74, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20,
+ 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x75, 0x72, 0x69, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22,
+ 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x55,
+ 0x52, 0x4c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59,
+ 0x41, 0x4e, 0x47, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x6c, 0x65, 0x61, 0x66, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x6e,
+ 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69,
+ 0x73, 0x20, 0x61, 0x20, 0x55, 0x52, 0x4c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x76,
+ 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x6e, 0x74,
+ 0x72, 0x79, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73,
+ 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70,
+ 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x45, 0x61, 0x63, 0x68, 0x20, 0x65,
+ 0x6e, 0x74, 0x72, 0x79, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6f, 0x6e, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65,
+ 0x6e, 0x74, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x73, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65,
+ 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74,
+ 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
+ 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65,
+ 0x74, 0x3a, 0x75, 0x72, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74,
+ 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74,
+ 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x58, 0x4d, 0x4c,
+ 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x66,
+ 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61,
+ 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f,
+ 0x66, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x66, 0x65, 0x61, 0x74, 0x75,
+ 0x72, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f,
+ 0x6d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x62,
+ 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x2c, 0x20, 0x72, 0x65, 0x67, 0x61, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73,
+ 0x20, 0x6f, 0x66, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20,
+ 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20,
+ 0x61, 0x6e, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
+ 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69,
+ 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x20,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61,
+ 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f,
+ 0x66, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x6f, 0x64,
+ 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x6e, 0x74,
+ 0x72, 0x79, 0x2e, 0x20, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d,
+ 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x63, 0x61, 0x6e,
+ 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
+ 0x6c, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2c, 0x20,
+ 0x73, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20,
+ 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x4d, 0x41, 0x59, 0x20, 0x61, 0x70,
+ 0x70, 0x65, 0x61, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69,
+ 0x6e, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x27,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27, 0x20, 0x65, 0x6e, 0x74, 0x72,
+ 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, 0x65,
+ 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x74, 0x79, 0x70, 0x65,
+ 0x27, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c,
+ 0x20, 0x62, 0x65, 0x20, 0x27, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x27, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65,
+ 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61,
+ 0x66, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+ 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e,
+ 0x63, 0x65, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x69, 0x6d,
+ 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x64, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70,
+ 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20,
+ 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2d, 0x61,
+ 0x63, 0x63, 0x65, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x62,
+ 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e,
+ 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x6e,
+ 0x74, 0x72, 0x79, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x20, 0x64, 0x65, 0x76, 0x69,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x59, 0x41, 0x4e,
+ 0x47, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e,
+ 0x31, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2c, 0x20, 0x74,
+ 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x74, 0x20, 0x6d,
+ 0x6f, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27, 0x20, 0x65,
+ 0x6e, 0x74, 0x72, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x63, 0x6f,
+ 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x27, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x27, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61,
+ 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d,
+ 0x65, 0x2c, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x59, 0x41, 0x4e,
+ 0x47, 0x20, 0x31, 0x2e, 0x31, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x6f, 0x6e,
+ 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f,
+ 0x66, 0x20, 0x61, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69,
+ 0x73, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65,
+ 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+ 0x2c, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x53, 0x48, 0x4f, 0x55,
+ 0x4c, 0x44, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x62, 0x65, 0x20, 0x6d, 0x6f,
+ 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x61,
+ 0x6e, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x27, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x61, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61,
+ 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x69, 0x6d, 0x70, 0x6f,
+ 0x72, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 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, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20,
+ 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x62, 0x75, 0x74,
+ 0x20, 0x64, 0x6f, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x6f, 0x6c, 0x2d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x69, 0x62, 0x6c,
+ 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x66, 0x72,
+ 0x6f, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69,
+ 0x70, 0x6c, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27,
+ 0x20, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x4d, 0x41,
+ 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x73,
+ 0x74, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x61, 0x6e,
+ 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x20, 0x69, 0x66, 0x20, 0x6d, 0x75,
+ 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x61, 0x6d, 0x65,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20,
+ 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x64, 0x69, 0x66, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6d,
+ 0x70, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72,
+ 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x64,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f,
+ 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6c, 0x61,
+ 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65,
+ 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64,
+ 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x22,
+ 0x45, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x72,
+ 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x6e,
+ 0x65, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x6f, 0x6e, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x73, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70,
+ 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65,
+ 0x73, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d, 0x6c, 0x65, 0x61,
+ 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20,
+ 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x4c, 0x65, 0x67,
+ 0x61, 0x63, 0x79, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+ 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b,
+ 0x0a, 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, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69,
+ 0x6e, 0x73, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
+ 0x67, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d,
+ 0x73, 0x65, 0x74, 0x2d, 0x69, 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73,
+ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72,
+ 0x79, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20,
+ 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a,
+ 0x20, 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, 0x20, 0x22, 0x43, 0x6f, 0x6e,
+ 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x72,
+ 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20,
+ 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20,
+ 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x27, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x69, 0x6e,
+ 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x20,
+ 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x4c,
+ 0x65, 0x67, 0x61, 0x63, 0x79, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e,
+ 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
+ 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20,
+ 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a,
+ 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, 0x22, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
+ 0x74, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x63, 0x68, 0x61, 0x6e,
+ 0x67, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x2d, 0x73, 0x65, 0x74, 0x2d, 0x69, 0x64, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x72, 0x65, 0x66, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61,
+ 0x74, 0x68, 0x20, 0x22, 0x2f, 0x79, 0x61, 0x6e, 0x67, 0x6c, 0x69, 0x62,
+ 0x3a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2d, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x2f, 0x79, 0x61, 0x6e, 0x67, 0x6c, 0x69, 0x62, 0x3a, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x2d, 0x69, 0x64,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d,
+ 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75,
+ 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65,
+ 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 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, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d,
+ 0x73, 0x65, 0x74, 0x2d, 0x69, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20,
+ 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x67, 0x65, 0x6e, 0x65,
+ 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x00
+};
diff --git a/models/ietf-yang-library@2019-01-04.yang b/models/ietf-yang-library@2019-01-04.yang
new file mode 100644
index 0000000..debfae7
--- /dev/null
+++ b/models/ietf-yang-library@2019-01-04.yang
@@ -0,0 +1,544 @@
+ module ietf-yang-library {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library";
+ prefix yanglib;
+
+ import ietf-yang-types {
+ prefix yang;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+ import ietf-inet-types {
+ prefix inet;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+ import ietf-datastores {
+ prefix ds;
+ reference
+ "RFC 8342: Network Management Datastore Architecture
+ (NMDA)";
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+
+ Author: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Author: Kent Watsen
+ <mailto:kent+ietf@watsen.net>
+
+ Author: Robert Wilton
+ <mailto:rwilton@cisco.com>";
+ description
+ "This module provides information about the YANG modules,
+ datastores, and datastore schemas used by a network
+ management server.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.
+
+ Copyright (c) 2019 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8525; see
+ the RFC itself for full legal notices.";
+
+ revision 2019-01-04 {
+ description
+ "Added support for multiple datastores according to the
+ Network Management Datastore Architecture (NMDA).";
+ reference
+ "RFC 8525: YANG Library";
+ }
+ revision 2016-04-09 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7895: YANG Module Library";
+ }
+
+ /*
+ * Typedefs
+ */
+
+ typedef revision-identifier {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}';
+ }
+ description
+ "Represents a specific date in YYYY-MM-DD format.";
+ }
+
+ /*
+ * Groupings
+ */
+
+ grouping module-identification-leafs {
+ description
+ "Parameters for identifying YANG modules and submodules.";
+ leaf name {
+ type yang:yang-identifier;
+ mandatory true;
+ description
+ "The YANG module or submodule name.";
+ }
+ leaf revision {
+ type revision-identifier;
+ description
+ "The YANG module or submodule revision date. If no revision
+ statement is present in the YANG module or submodule, this
+ leaf is not instantiated.";
+ }
+ }
+
+ grouping location-leaf-list {
+ description
+ "Common leaf-list parameter for the locations of modules and
+ submodules.";
+ leaf-list location {
+ type inet:uri;
+ description
+ "Contains a URL that represents the YANG schema
+ resource for this module or submodule.
+
+ This leaf will only be present if there is a URL
+ available for retrieval of the schema for this entry.";
+ }
+ }
+
+ grouping module-implementation-parameters {
+ description
+ "Parameters for describing the implementation of a module.";
+ leaf-list feature {
+ type yang:yang-identifier;
+ description
+ "List of all YANG feature names from this module that are
+ supported by the server, regardless whether they are defined
+ in the module or any included submodule.";
+ }
+ leaf-list deviation {
+ type leafref {
+ path "../../module/name";
+ }
+ description
+ "List of all YANG deviation modules used by this server to
+ modify the conformance of the module associated with this
+ entry. Note that the same module can be used for deviations
+ for multiple modules, so the same entry MAY appear within
+ multiple 'module' entries.
+
+ This reference MUST NOT (directly or indirectly)
+ refer to the module being deviated.
+
+ Robust clients may want to make sure that they handle a
+ situation where a module deviates itself (directly or
+ indirectly) gracefully.";
+ }
+ }
+
+ grouping module-set-parameters {
+ description
+ "A set of parameters that describe a module set.";
+ leaf name {
+ type string;
+ description
+ "An arbitrary name of the module set.";
+ }
+ list module {
+ key "name";
+ description
+ "An entry in this list represents a module implemented by the
+ server, as per Section 5.6.5 of RFC 7950, with a particular
+ set of supported features and deviations.";
+ reference
+ "RFC 7950: The YANG 1.1 Data Modeling Language";
+ uses module-identification-leafs;
+ leaf namespace {
+ type inet:uri;
+ mandatory true;
+ description
+ "The XML namespace identifier for this module.";
+ }
+ uses location-leaf-list;
+ list submodule {
+ key "name";
+ description
+ "Each entry represents one submodule within the
+ parent module.";
+ uses module-identification-leafs;
+ uses location-leaf-list;
+ }
+ uses module-implementation-parameters;
+ }
+ list import-only-module {
+ key "name revision";
+ description
+ "An entry in this list indicates that the server imports
+ reusable definitions from the specified revision of the
+ module but does not implement any protocol-accessible
+ objects from this revision.
+
+ Multiple entries for the same module name MAY exist. This
+ can occur if multiple modules import the same module but
+ specify different revision dates in the import statements.";
+ leaf name {
+ type yang:yang-identifier;
+ description
+ "The YANG module name.";
+ }
+ leaf revision {
+ type union {
+ type revision-identifier;
+ type string {
+ length "0";
+ }
+ }
+ description
+ "The YANG module revision date.
+ A zero-length string is used if no revision statement
+ is present in the YANG module.";
+ }
+ leaf namespace {
+ type inet:uri;
+ mandatory true;
+ description
+ "The XML namespace identifier for this module.";
+ }
+ uses location-leaf-list;
+ list submodule {
+ key "name";
+ description
+ "Each entry represents one submodule within the
+ parent module.";
+ uses module-identification-leafs;
+ uses location-leaf-list;
+ }
+ }
+ }
+
+ grouping yang-library-parameters {
+ description
+ "The YANG library data structure is represented as a grouping
+ so it can be reused in configuration or another monitoring
+ data structure.";
+ list module-set {
+ key "name";
+ description
+ "A set of modules that may be used by one or more schemas.
+
+ A module set does not have to be referentially complete,
+ i.e., it may define modules that contain import statements
+ for other modules not included in the module set.";
+ uses module-set-parameters;
+ }
+ list schema {
+ key "name";
+ description
+ "A datastore schema that may be used by one or more
+ datastores.
+
+ The schema must be valid and referentially complete, i.e.,
+ it must contain modules to satisfy all used import
+ statements for all modules specified in the schema.";
+ leaf name {
+ type string;
+ description
+ "An arbitrary name of the schema.";
+ }
+ leaf-list module-set {
+ type leafref {
+ path "../../module-set/name";
+ }
+ description
+ "A set of module-sets that are included in this schema.
+ If a non-import-only module appears in multiple module
+ sets, then the module revision and the associated features
+ and deviations must be identical.";
+ }
+ }
+ list datastore {
+ key "name";
+ description
+ "A datastore supported by this server.
+
+ Each datastore indicates which schema it supports.
+
+ The server MUST instantiate one entry in this list per
+ specific datastore it supports.
+ Each datastore entry with the same datastore schema SHOULD
+ reference the same schema.";
+ leaf name {
+ type ds:datastore-ref;
+ description
+ "The identity of the datastore.";
+ }
+ leaf schema {
+ type leafref {
+ path "../../schema/name";
+ }
+ mandatory true;
+ description
+ "A reference to the schema supported by this datastore.
+ All non-import-only modules of the schema are implemented
+ with their associated features and deviations.";
+ }
+ }
+ }
+
+ /*
+ * Top-level container
+ */
+
+ container yang-library {
+ config false;
+ description
+ "Container holding the entire YANG library of this server.";
+ uses yang-library-parameters;
+ leaf content-id {
+ type string;
+ mandatory true;
+ description
+ "A server-generated identifier of the contents of the
+ '/yang-library' tree. The server MUST change the value of
+ this leaf if the information represented by the
+ '/yang-library' tree, except '/yang-library/content-id', has
+ changed.";
+ }
+ }
+
+ /*
+ * Notifications
+ */
+
+ notification yang-library-update {
+ description
+ "Generated when any YANG library information on the
+ server has changed.";
+ leaf content-id {
+ type leafref {
+ path "/yanglib:yang-library/yanglib:content-id";
+ }
+ mandatory true;
+ description
+ "Contains the YANG library content identifier for the updated
+ YANG library at the time the notification is generated.";
+ }
+ }
+
+ /*
+ * Legacy groupings
+ */
+
+ grouping module-list {
+ status deprecated;
+ description
+ "The module data structure is represented as a grouping
+ so it can be reused in configuration or another monitoring
+ data structure.";
+
+ grouping common-leafs {
+ status deprecated;
+ description
+ "Common parameters for YANG modules and submodules.";
+ leaf name {
+ type yang:yang-identifier;
+ status deprecated;
+ description
+ "The YANG module or submodule name.";
+ }
+ leaf revision {
+ type union {
+ type revision-identifier;
+ type string {
+ length "0";
+ }
+ }
+ status deprecated;
+ description
+ "The YANG module or submodule revision date.
+ A zero-length string is used if no revision statement
+ is present in the YANG module or submodule.";
+ }
+ }
+
+ grouping schema-leaf {
+ status deprecated;
+ description
+ "Common schema leaf parameter for modules and submodules.";
+ leaf schema {
+ type inet:uri;
+ description
+ "Contains a URL that represents the YANG schema
+ resource for this module or submodule.
+
+ This leaf will only be present if there is a URL
+ available for retrieval of the schema for this entry.";
+ }
+ }
+ list module {
+ key "name revision";
+ status deprecated;
+ description
+ "Each entry represents one revision of one module
+ currently supported by the server.";
+ uses common-leafs {
+ status deprecated;
+ }
+ uses schema-leaf {
+ status deprecated;
+ }
+ leaf namespace {
+ type inet:uri;
+ mandatory true;
+ status deprecated;
+ description
+ "The XML namespace identifier for this module.";
+ }
+ leaf-list feature {
+ type yang:yang-identifier;
+ status deprecated;
+ description
+ "List of YANG feature names from this module that are
+ supported by the server, regardless of whether they are
+ defined in the module or any included submodule.";
+ }
+ list deviation {
+ key "name revision";
+ status deprecated;
+ description
+ "List of YANG deviation module names and revisions
+ used by this server to modify the conformance of
+ the module associated with this entry. Note that
+ the same module can be used for deviations for
+ multiple modules, so the same entry MAY appear
+ within multiple 'module' entries.
+
+ The deviation module MUST be present in the 'module'
+ list, with the same name and revision values.
+ The 'conformance-type' value will be 'implement' for
+ the deviation module.";
+ uses common-leafs {
+ status deprecated;
+ }
+ }
+ leaf conformance-type {
+ type enumeration {
+ enum implement {
+ description
+ "Indicates that the server implements one or more
+ protocol-accessible objects defined in the YANG module
+ identified in this entry. This includes deviation
+ statements defined in the module.
+
+ For YANG version 1.1 modules, there is at most one
+ 'module' entry with conformance type 'implement' for a
+ particular module name, since YANG 1.1 requires that
+ at most one revision of a module is implemented.
+
+ For YANG version 1 modules, there SHOULD NOT be more
+ than one 'module' entry for a particular module
+ name.";
+ }
+ enum import {
+ description
+ "Indicates that the server imports reusable definitions
+ from the specified revision of the module but does
+ not implement any protocol-accessible objects from
+ this revision.
+
+ Multiple 'module' entries for the same module name MAY
+ exist. This can occur if multiple modules import the
+ same module but specify different revision dates in
+ the import statements.";
+ }
+ }
+ mandatory true;
+ status deprecated;
+ description
+ "Indicates the type of conformance the server is claiming
+ for the YANG module identified by this entry.";
+ }
+ list submodule {
+ key "name revision";
+ status deprecated;
+ description
+ "Each entry represents one submodule within the
+ parent module.";
+ uses common-leafs {
+ status deprecated;
+ }
+ uses schema-leaf {
+ status deprecated;
+ }
+ }
+ }
+ }
+
+ /*
+ * Legacy operational state data nodes
+ */
+
+ container modules-state {
+ config false;
+ status deprecated;
+ description
+ "Contains YANG module monitoring information.";
+ leaf module-set-id {
+ type string;
+ mandatory true;
+ status deprecated;
+ description
+ "Contains a server-specific identifier representing
+ the current set of modules and submodules. The
+ server MUST change the value of this leaf if the
+ information represented by the 'module' list instances
+ has changed.";
+ }
+ uses module-list {
+ status deprecated;
+ }
+ }
+
+ /*
+ * Legacy notifications
+ */
+
+ notification yang-library-change {
+ status deprecated;
+ description
+ "Generated when the set of modules and submodules supported
+ by the server has changed.";
+ leaf module-set-id {
+ type leafref {
+ path "/yanglib:modules-state/yanglib:module-set-id";
+ }
+ mandatory true;
+ status deprecated;
+ description
+ "Contains the module-set-id value representing the
+ set of modules and submodules supported at the server
+ at the time the notification is generated.";
+ }
+ }
+ }
diff --git a/models/ietf-yang-metadata@2016-08-05.h b/models/ietf-yang-metadata@2016-08-05.h
new file mode 100644
index 0000000..5b2665b
--- /dev/null
+++ b/models/ietf-yang-metadata@2016-08-05.h
@@ -0,0 +1,220 @@
+unsigned char ietf_yang_metadata_2016_08_05_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x7b, 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, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79,
+ 0x61, 0x6e, 0x67, 0x2d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+ 0x22, 0x3b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x6d, 0x64, 0x3b,
+ 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x22, 0x49, 0x45, 0x54, 0x46, 0x20, 0x4e, 0x45, 0x54, 0x4d, 0x4f,
+ 0x44, 0x20, 0x28, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20, 0x44,
+ 0x61, 0x74, 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x67,
+ 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x29, 0x20, 0x57,
+ 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70,
+ 0x22, 0x3b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x22, 0x57,
+ 0x47, 0x20, 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x74, 0x72,
+ 0x61, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f,
+ 0x72, 0x67, 0x2f, 0x77, 0x67, 0x2f, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64,
+ 0x2f, 0x3e, 0x0a, 0x0a, 0x57, 0x47, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x3a,
+ 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6e, 0x65,
+ 0x74, 0x6d, 0x6f, 0x64, 0x40, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72,
+ 0x67, 0x3e, 0x0a, 0x0a, 0x57, 0x47, 0x20, 0x43, 0x68, 0x61, 0x69, 0x72,
+ 0x3a, 0x20, 0x4c, 0x6f, 0x75, 0x20, 0x42, 0x65, 0x72, 0x67, 0x65, 0x72,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6c, 0x62, 0x65, 0x72,
+ 0x67, 0x65, 0x72, 0x40, 0x6c, 0x61, 0x62, 0x6e, 0x2e, 0x6e, 0x65, 0x74,
+ 0x3e, 0x0a, 0x0a, 0x57, 0x47, 0x20, 0x43, 0x68, 0x61, 0x69, 0x72, 0x3a,
+ 0x20, 0x4b, 0x65, 0x6e, 0x74, 0x20, 0x57, 0x61, 0x74, 0x73, 0x65, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6b, 0x77, 0x61, 0x74,
+ 0x73, 0x65, 0x6e, 0x40, 0x6a, 0x75, 0x6e, 0x69, 0x70, 0x65, 0x72, 0x2e,
+ 0x6e, 0x65, 0x74, 0x3e, 0x0a, 0x0a, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72,
+ 0x3a, 0x20, 0x20, 0x20, 0x4c, 0x61, 0x64, 0x69, 0x73, 0x6c, 0x61, 0x76,
+ 0x20, 0x4c, 0x68, 0x6f, 0x74, 0x6b, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c,
+ 0x74, 0x6f, 0x3a, 0x6c, 0x68, 0x6f, 0x74, 0x6b, 0x61, 0x40, 0x6e, 0x69,
+ 0x63, 0x2e, 0x63, 0x7a, 0x3e, 0x22, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73,
+ 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20,
+ 0x27, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x27, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x0a, 0x66, 0x6f,
+ 0x72, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6d,
+ 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x0a, 0x43, 0x6f,
+ 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x31, 0x36, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x20, 0x20, 0x41, 0x6c, 0x6c, 0x20,
+ 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72,
+ 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x64, 0x20, 0x70, 0x75, 0x72, 0x73, 0x75, 0x61, 0x6e, 0x74,
+ 0x20, 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62,
+ 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x0a, 0x74, 0x68, 0x65, 0x20,
+ 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x69, 0x6d, 0x70,
+ 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x42, 0x53, 0x44, 0x20, 0x4c,
+ 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x73, 0x65, 0x74, 0x0a, 0x66,
+ 0x6f, 0x72, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e, 0x63, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x27, 0x73, 0x20, 0x4c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x50, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x52, 0x65, 0x6c,
+ 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x49, 0x45, 0x54,
+ 0x46, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a,
+ 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x65, 0x65, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67,
+ 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2d, 0x69, 0x6e, 0x66,
+ 0x6f, 0x29, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20, 0x76, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x32, 0x0a, 0x28, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x66,
+ 0x63, 0x2d, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67,
+ 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x72, 0x66, 0x63, 0x37, 0x39, 0x35,
+ 0x32, 0x29, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x52, 0x46, 0x43, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x0a, 0x66,
+ 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x67, 0x61,
+ 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x22, 0x3b,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31,
+ 0x36, 0x2d, 0x30, 0x38, 0x2d, 0x30, 0x35, 0x20, 0x7b, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x22, 0x49, 0x6e,
+ 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x32,
+ 0x3a, 0x20, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x4d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x59,
+ 0x41, 0x4e, 0x47, 0x22, 0x3b, 0x7d, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x7b, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6c,
+ 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x20, 0x69, 0x6e, 0x0a, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65,
+ 0x20, 0x27, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61,
+ 0x72, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x0a, 0x61, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x74, 0x6f, 0x70, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20,
+ 0x6f, 0x66, 0x20, 0x61, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x2e, 0x65, 0x2e, 0x2c,
+ 0x20, 0x69, 0x74, 0x0a, 0x62, 0x65, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x20,
+ 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e,
+ 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x41, 0x42, 0x4e, 0x46, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x0a, 0x27, 0x62, 0x6f, 0x64, 0x79, 0x2d, 0x73, 0x74, 0x6d, 0x74,
+ 0x73, 0x27, 0x20, 0x28, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x31, 0x34, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39,
+ 0x35, 0x30, 0x29, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x61, 0x72,
+ 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x27, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x2e, 0x20, 0x20, 0x53, 0x79, 0x6e, 0x74, 0x61, 0x63, 0x74,
+ 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69,
+ 0x73, 0x20, 0x61, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x61, 0x73, 0x0a, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x36, 0x2e, 0x32, 0x20, 0x6f, 0x66,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x2e, 0x0a, 0x0a,
+ 0x41, 0x6e, 0x20, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x27, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69,
+ 0x74, 0x73, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x74, 0x68,
+ 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x66,
+ 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x77,
+ 0x68, 0x69, 0x63, 0x68, 0x0a, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69,
+ 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x0a,
+ 0x77, 0x61, 0x79, 0x20, 0x61, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x27, 0x74, 0x79, 0x70, 0x65, 0x27, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65,
+ 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x74, 0x68,
+ 0x65, 0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x0a,
+ 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x75, 0x73,
+ 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c,
+ 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61,
+ 0x72, 0x64, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x73, 0x75, 0x62, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x28, 0x61,
+ 0x6c, 0x6c, 0x0a, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x29, 0x3a, 0x20, 0x27, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x69, 0x66,
+ 0x2d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x27, 0x2c, 0x20, 0x27,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x27, 0x2c, 0x0a,
+ 0x27, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x27, 0x2c, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x27, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x27, 0x2e, 0x0a, 0x0a,
+ 0x41, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x6e,
+ 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f,
+ 0x72, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x70, 0x61, 0x72,
+ 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x0a, 0x69, 0x6e,
+ 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x68,
+ 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65,
+ 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x61, 0x6d, 0x6f, 0x6e, 0x67, 0x0a,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73,
+ 0x65, 0x64, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x73, 0x2c, 0x20, 0x65, 0x2e, 0x67, 0x2e, 0x2c, 0x20, 0x69,
+ 0x6e, 0x20, 0x61, 0x20, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20,
+ 0x3c, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3e, 0x0a, 0x6d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61,
+ 0x72, 0x79, 0x20, 0x28, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30,
+ 0x29, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x0a, 0x74,
+ 0x68, 0x65, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
+ 0x68, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69,
+ 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e,
+ 0x79, 0x0a, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x64,
+ 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x0a,
+ 0x0a, 0x58, 0x4d, 0x4c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
+ 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x65,
+ 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x61,
+ 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61,
+ 0x72, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69,
+ 0x6e, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x32, 0x2e, 0x22,
+ 0x3b, 0x7d, 0x7d, 0x00
+};
diff --git a/models/ietf-yang-metadata@2016-08-05.yang b/models/ietf-yang-metadata@2016-08-05.yang
new file mode 100644
index 0000000..f59426a
--- /dev/null
+++ b/models/ietf-yang-metadata@2016-08-05.yang
@@ -0,0 +1,80 @@
+module ietf-yang-metadata {
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-metadata";
+ prefix md;
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netmod/>
+
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Lou Berger
+ <mailto:lberger@labn.net>
+
+ WG Chair: Kent Watsen
+ <mailto:kwatsen@juniper.net>
+
+ Editor: Ladislav Lhotka
+ <mailto:lhotka@nic.cz>";
+ description
+ "This YANG module defines an 'extension' statement that allows
+ for defining metadata annotations.
+
+ Copyright (c) 2016 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Simplified BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7952
+ (http://www.rfc-editor.org/info/rfc7952); see the RFC itself
+ for full legal notices.";
+
+ revision 2016-08-05 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7952: Defining and Using Metadata with YANG";
+ }
+
+ extension annotation {
+ argument name;
+ description
+ "This extension allows for defining metadata annotations in
+ YANG modules. The 'md:annotation' statement can appear only
+ at the top level of a YANG module or submodule, i.e., it
+ becomes a new alternative in the ABNF production rule for
+ 'body-stmts' (Section 14 in RFC 7950).
+
+ The argument of the 'md:annotation' statement defines the name
+ of the annotation. Syntactically, it is a YANG identifier as
+ defined in Section 6.2 of RFC 7950.
+
+ An annotation defined with this 'extension' statement inherits
+ the namespace and other context from the YANG module in which
+ it is defined.
+
+ The data type of the annotation value is specified in the same
+ way as for a leaf data node using the 'type' statement.
+
+ The semantics of the annotation and other documentation can be
+ specified using the following standard YANG substatements (all
+ are optional): 'description', 'if-feature', 'reference',
+ 'status', and 'units'.
+
+ A server announces support for a particular annotation by
+ including the module in which the annotation is defined among
+ the advertised YANG modules, e.g., in a NETCONF <hello>
+ message or in the YANG library (RFC 7950). The annotation can
+ then be attached to any instance of a data node defined in any
+ YANG module that is advertised by the server.
+
+ XML encoding and JSON encoding of annotations are defined in
+ RFC 7952.";
+ }
+}
diff --git a/models/ietf-yang-schema-mount@2019-01-14.h b/models/ietf-yang-schema-mount@2019-01-14.h
new file mode 100644
index 0000000..82db243
--- /dev/null
+++ b/models/ietf-yang-schema-mount@2019-01-14.h
@@ -0,0 +1,651 @@
+unsigned char ietf_yang_schema_mount_2019_01_14_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x79, 0x61,
+ 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31,
+ 0x2e, 0x31, 0x3b, 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, 0x69, 0x65, 0x74,
+ 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x2d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x6d,
+ 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72,
+ 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x69, 0x6e, 0x65, 0x74, 0x2d,
+ 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x69, 0x6e, 0x65, 0x74, 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, 0x39, 0x39, 0x31, 0x3a, 0x20, 0x43, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61,
+ 0x20, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d,
+ 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69,
+ 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x74, 0x79, 0x70,
+ 0x65, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65,
+ 0x66, 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e, 0x67, 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,
+ 0x39, 0x39, 0x31, 0x3a, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x54, 0x79,
+ 0x70, 0x65, 0x73, 0x22, 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, 0x49, 0x45, 0x54, 0x46, 0x20,
+ 0x4e, 0x45, 0x54, 0x4d, 0x4f, 0x44, 0x20, 0x28, 0x4e, 0x45, 0x54, 0x43,
+ 0x4f, 0x4e, 0x46, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4d, 0x6f, 0x64,
+ 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61,
+ 0x67, 0x65, 0x29, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x20,
+ 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x63,
+ 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
+ 0x57, 0x47, 0x20, 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20, 0x3c, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x74,
+ 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e,
+ 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x67, 0x2f, 0x6e, 0x65, 0x74, 0x6d, 0x6f,
+ 0x64, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x57, 0x47, 0x20,
+ 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c,
+ 0x74, 0x6f, 0x3a, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64, 0x40, 0x69, 0x65,
+ 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20,
+ 0x4d, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x20, 0x42, 0x6a, 0x6f, 0x72, 0x6b,
+ 0x6c, 0x75, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69,
+ 0x6c, 0x74, 0x6f, 0x3a, 0x6d, 0x62, 0x6a, 0x40, 0x74, 0x61, 0x69, 0x6c,
+ 0x2d, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20,
+ 0x4c, 0x61, 0x64, 0x69, 0x73, 0x6c, 0x61, 0x76, 0x20, 0x4c, 0x68, 0x6f,
+ 0x74, 0x6b, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c,
+ 0x74, 0x6f, 0x3a, 0x6c, 0x68, 0x6f, 0x74, 0x6b, 0x61, 0x40, 0x6e, 0x69,
+ 0x63, 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, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20,
+ 0x61, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20,
+ 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6d, 0x6f, 0x64,
+ 0x65, 0x6c, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x59, 0x41, 0x4e,
+ 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x69, 0x6e,
+ 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x2e, 0x20, 0x20, 0x49, 0x74, 0x20, 0x61, 0x6c, 0x73, 0x6f,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x6f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x68, 0x61, 0x74,
+ 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c,
+ 0x6c, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20,
+ 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x77, 0x6f, 0x72,
+ 0x64, 0x73, 0x20, 0x27, 0x4d, 0x55, 0x53, 0x54, 0x27, 0x2c, 0x20, 0x27,
+ 0x4d, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x27, 0x2c, 0x20, 0x27,
+ 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x27, 0x2c, 0x20, 0x27,
+ 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x48, 0x41,
+ 0x4c, 0x4c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x4f, 0x54, 0x27,
+ 0x2c, 0x20, 0x27, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x27, 0x2c, 0x20,
+ 0x27, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20, 0x4e, 0x4f, 0x54, 0x27,
+ 0x2c, 0x20, 0x27, 0x52, 0x45, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x44,
+ 0x45, 0x44, 0x27, 0x2c, 0x20, 0x27, 0x4e, 0x4f, 0x54, 0x20, 0x52, 0x45,
+ 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x44, 0x45, 0x44, 0x27, 0x2c, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x4d, 0x41, 0x59, 0x27, 0x2c, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x27, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41,
+ 0x4c, 0x27, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x64,
+ 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20,
+ 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70,
+ 0x72, 0x65, 0x74, 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x20, 0x42, 0x43, 0x50, 0x20, 0x31, 0x34, 0x20, 0x28, 0x52,
+ 0x46, 0x43, 0x20, 0x32, 0x31, 0x31, 0x39, 0x29, 0x20, 0x28, 0x52, 0x46,
+ 0x43, 0x20, 0x38, 0x31, 0x37, 0x34, 0x29, 0x20, 0x77, 0x68, 0x65, 0x6e,
+ 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x77,
+ 0x68, 0x65, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68,
+ 0x65, 0x79, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x20, 0x69, 0x6e,
+ 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x61, 0x70, 0x69, 0x74, 0x61, 0x6c,
+ 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x73, 0x68, 0x6f, 0x77, 0x6e, 0x20,
+ 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63,
+ 0x29, 0x20, 0x32, 0x30, 0x31, 0x39, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65,
+ 0x2e, 0x20, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74,
+ 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72,
+ 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x70, 0x75, 0x72,
+ 0x73, 0x75, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e,
+ 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x66, 0x69, 0x65, 0x64, 0x20, 0x42, 0x53, 0x44, 0x20, 0x4c, 0x69, 0x63,
+ 0x65, 0x6e, 0x73, 0x65, 0x20, 0x73, 0x65, 0x74, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x66, 0x6f, 0x72, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x53,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e, 0x63, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x27, 0x73, 0x20, 0x4c, 0x65, 0x67, 0x61, 0x6c,
+ 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x6f, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x44, 0x6f,
+ 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x65, 0x65, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f,
+ 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2d, 0x69,
+ 0x6e, 0x66, 0x6f, 0x29, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x59, 0x41, 0x4e,
+ 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20,
+ 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20,
+ 0x38, 0x35, 0x32, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+ 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x46, 0x43, 0x20, 0x69,
+ 0x74, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66, 0x75,
+ 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x6e, 0x6f, 0x74,
+ 0x69, 0x63, 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x72,
+ 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x39,
+ 0x2d, 0x30, 0x31, 0x2d, 0x31, 0x34, 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,
+ 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, 0x38, 0x35, 0x32, 0x38, 0x3a, 0x20, 0x59,
+ 0x41, 0x4e, 0x47, 0x20, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x4d,
+ 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f,
+ 0x69, 0x6e, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72,
+ 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 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, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x27, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x27, 0x20, 0x69, 0x73,
+ 0x20, 0x61, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2e, 0x65, 0x2e,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x27, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e,
+ 0x67, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
+ 0x27, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f,
+ 0x69, 0x6e, 0x74, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x20,
+ 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61,
+ 0x20, 0x59, 0x41, 0x4e, 0x47, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2c, 0x20, 0x6e, 0x65, 0x69, 0x74, 0x68,
+ 0x65, 0x72, 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x6c,
+ 0x79, 0x20, 0x6e, 0x6f, 0x72, 0x20, 0x76, 0x69, 0x61, 0x20, 0x61, 0x20,
+ 0x27, 0x75, 0x73, 0x65, 0x73, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x27, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74,
+ 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x4d, 0x41, 0x59, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65,
+ 0x6e, 0x74, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x69, 0x6e, 0x65, 0x72, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x27,
+ 0x6c, 0x69, 0x73, 0x74, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4d, 0x55,
+ 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x77, 0x68,
+ 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x4e,
+ 0x4f, 0x54, 0x20, 0x62, 0x65, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x75,
+ 0x6e, 0x74, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x27, 0x20, 0x73, 0x74,
+ 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x61,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x6e, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
+ 0x27, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x6c, 0x69, 0x73, 0x74, 0x27, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x49, 0x66, 0x20, 0x61, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20,
+ 0x69, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75,
+ 0x70, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x68,
+ 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, 0x6f, 0x75,
+ 0x70, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64,
+ 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6c,
+ 0x61, 0x63, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x20, 0x68, 0x69, 0x65, 0x72, 0x61, 0x72, 0x63, 0x68,
+ 0x79, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x20, 0x6d, 0x61, 0x79,
+ 0x20, 0x62, 0x65, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64,
+ 0x2e, 0x20, 0x20, 0x41, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68,
+ 0x20, 0x61, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69,
+ 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x27, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d, 0x6d, 0x6f, 0x75,
+ 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f,
+ 0x69, 0x6e, 0x74, 0x27, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68,
+ 0x69, 0x63, 0x68, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6d, 0x6f, 0x64,
+ 0x65, 0x6c, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e,
+ 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e,
+ 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x6f, 0x74,
+ 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x27,
+ 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x64,
+ 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x64, 0x65, 0x66, 0x69,
+ 0x6e, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6e, 0x6f, 0x64,
+ 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20,
+ 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73,
+ 0x0a, 0x20, 0x20, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x2d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x66,
+ 0x61, 0x6c, 0x73, 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, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x75, 0x6e,
+ 0x74, 0x65, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6d, 0x6f, 0x64,
+ 0x65, 0x6c, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
+ 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b,
+ 0x65, 0x79, 0x20, 0x22, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x3b,
+ 0x0a, 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, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x6c, 0x69, 0x73,
+ 0x74, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x20, 0x61,
+ 0x20, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20,
+ 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x70, 0x72,
+ 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
+ 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x58, 0x50, 0x61,
+ 0x74, 0x68, 0x20, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x70, 0x61, 0x72, 0x65, 0x6e,
+ 0x74, 0x2d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x27,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63,
+ 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+ 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x55,
+ 0x52, 0x49, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x2d,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 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, 0x4e, 0x61, 0x6d, 0x65, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x2e,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x75, 0x72,
+ 0x69, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x65, 0x74, 0x3a, 0x75, 0x72,
+ 0x69, 0x3b, 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, 0x4e, 0x61,
+ 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x55, 0x52, 0x49, 0x20,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x22, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x22, 0x3b, 0x0a, 0x0a, 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, 0x22, 0x45, 0x61, 0x63, 0x68,
+ 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63,
+ 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x70, 0x61, 0x72,
+ 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70,
+ 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x6d, 0x6f, 0x75,
+ 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x4d, 0x55, 0x53,
+ 0x54, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x27,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x6f,
+ 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x27, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61,
+ 0x72, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x27, 0x69,
+ 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x27, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x79, 0x61,
+ 0x6e, 0x67, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b, 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, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d,
+ 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x79,
+ 0x61, 0x6e, 0x67, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x3b, 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, 0x4c,
+ 0x61, 0x62, 0x65, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x75, 0x73, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
+ 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e,
+ 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
+ 0x74, 0x20, 0x22, 0x74, 0x72, 0x75, 0x65, 0x22, 0x3b, 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, 0x49, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x69, 0x73, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x74, 0x6f, 0x20, 0x27, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x27, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65,
+ 0x61, 0x64, 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x28, 0x27, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x27, 0x29,
+ 0x2c, 0x20, 0x72, 0x65, 0x67, 0x61, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x27, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x27, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72,
+ 0x74, 0x79, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x6f, 0x69,
+ 0x63, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d, 0x72, 0x65,
+ 0x66, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72,
+ 0x75, 0x65, 0x3b, 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, 0x41,
+ 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x69,
+ 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x69,
+ 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x22, 0x41, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
+ 0x74, 0x65, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2d, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x20, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x20,
+ 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x75, 0x6e,
+ 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 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, 0x20, 0x20, 0x22, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x68, 0x61,
+ 0x73, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74,
+ 0x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x27, 0x69, 0x65, 0x74, 0x66,
+ 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72,
+ 0x79, 0x27, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f,
+ 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2c, 0x20, 0x61,
+ 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74,
+ 0x61, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69,
+ 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61,
+ 0x62, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f,
+ 0x75, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
+ 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x68,
+ 0x61, 0x76, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x74, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x20, 0x6d,
+ 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
+ 0x72, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2d, 0x73, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+ 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x74, 0x6f, 0x67,
+ 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x27, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x2d, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x27, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d,
+ 0x61, 0x6b, 0x65, 0x20, 0x75, 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69,
+ 0x6e, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 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, 0x20, 0x20, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6d, 0x6f,
+ 0x75, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x6c, 0x65, 0x61,
+ 0x73, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x27, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e,
+ 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x27, 0x20, 0x61,
+ 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20,
+ 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x69, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
+ 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72,
+ 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74,
+ 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+ 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x20, 0x20, 0x57,
+ 0x68, 0x65, 0x6e, 0x20, 0x58, 0x50, 0x61, 0x74, 0x68, 0x20, 0x65, 0x78,
+ 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x61, 0x72, 0x65,
+ 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x64, 0x2c, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x27, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x2d,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x27, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x61, 0x6b, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20,
+ 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x44,
+ 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x73,
+ 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x6f, 0x69, 0x6e,
+ 0x74, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20,
+ 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x66, 0x65, 0x72,
+ 0x65, 0x6e, 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x78, 0x70, 0x61, 0x74, 0x68, 0x31, 0x2e,
+ 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x20, 0x22, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20,
+ 0x58, 0x50, 0x61, 0x74, 0x68, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x65, 0x78,
+ 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x76,
+ 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67,
+ 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x65, 0x78, 0x74, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x20, 0x74, 0x72, 0x65, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20,
+ 0x69, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, 0x63,
+ 0x63, 0x65, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x65,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x72,
+ 0x65, 0x6e, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x72, 0x65,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x77, 0x69, 0x74, 0x68,
+ 0x6f, 0x75, 0x74, 0x2a, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6e, 0x6f, 0x64,
+ 0x65, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69,
+ 0x6e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x69,
+ 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e,
+ 0x74, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x65, 0x78, 0x74, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+ 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x62, 0x6f,
+ 0x74, 0x68, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x6f, 0x20, 0x31, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x69,
+ 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6d,
+ 0x70, 0x74, 0x79, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x57, 0x33, 0x43,
+ 0x20, 0x58, 0x50, 0x61, 0x74, 0x68, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x64,
+ 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x54, 0x52, 0x2f, 0x31,
+ 0x39, 0x39, 0x39, 0x2f, 0x52, 0x45, 0x43, 0x2d, 0x78, 0x70, 0x61, 0x74,
+ 0x68, 0x2d, 0x31, 0x39, 0x39, 0x39, 0x31, 0x31, 0x31, 0x36, 0x29, 0x20,
+ 0x61, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x30, 0x20, 0x6f, 0x66, 0x20,
+ 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20,
+ 0x6f, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x27, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x27,
+ 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20,
+ 0x27, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d, 0x6d, 0x6f, 0x75, 0x6e,
+ 0x74, 0x73, 0x27, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x45, 0x61, 0x63,
+ 0x68, 0x20, 0x58, 0x50, 0x61, 0x74, 0x68, 0x20, 0x65, 0x78, 0x70, 0x72,
+ 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20,
+ 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20,
+ 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x28, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x79, 0x20,
+ 0x65, 0x6d, 0x70, 0x74, 0x79, 0x29, 0x2e, 0x20, 0x20, 0x46, 0x6f, 0x72,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74,
+ 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x58, 0x50, 0x61, 0x74, 0x68,
+ 0x20, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x68, 0x6f, 0x73, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+ 0x78, 0x74, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
+ 0x65, 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2c, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20,
+ 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x6e, 0x6f,
+ 0x64, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74,
+ 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68,
+ 0x20, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x20, 0x6e, 0x6f,
+ 0x64, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x65,
+ 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x20, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
+ 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20,
+ 0x27, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x27,
+ 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c,
+ 0x66, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x61,
+ 0x20, 0x27, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x27, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6d, 0x61, 0x79,
+ 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x6f,
+ 0x64, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x72,
+ 0x65, 0x20, 0x62, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x74, 0x20, 0x69, 0x6e,
+ 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x63,
+ 0x63, 0x65, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x65,
+ 0x65, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x61, 0x20,
+ 0x27, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x27, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20,
+ 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x7d, 0x0a, 0x00
+};
diff --git a/models/ietf-yang-schema-mount@2019-01-14.yang b/models/ietf-yang-schema-mount@2019-01-14.yang
new file mode 100644
index 0000000..c49458a
--- /dev/null
+++ b/models/ietf-yang-schema-mount@2019-01-14.yang
@@ -0,0 +1,224 @@
+module ietf-yang-schema-mount {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount";
+ prefix yangmnt;
+
+ import ietf-inet-types {
+ prefix inet;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ import ietf-yang-types {
+ prefix yang;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+
+ Editor: Ladislav Lhotka
+ <mailto:lhotka@nic.cz>";
+
+ description
+ "This module defines a YANG extension statement that can be used
+ to incorporate data models defined in other YANG modules in a
+ module. It also defines operational state data that specify the
+ overall structure of the data model.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.
+
+ Copyright (c) 2019 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Simplified BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8528;
+ see the RFC itself for full legal notices.";
+
+ revision 2019-01-14 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8528: YANG Schema Mount";
+ }
+
+ /*
+ * Extensions
+ */
+
+ extension mount-point {
+ argument label;
+ description
+ "The argument 'label' is a YANG identifier, i.e., it is of the
+ type 'yang:yang-identifier'.
+
+ The 'mount-point' statement MUST NOT be used in a YANG
+ version 1 module, neither explicitly nor via a 'uses'
+ statement.
+ The 'mount-point' statement MAY be present as a substatement
+ of 'container' and 'list' and MUST NOT be present elsewhere.
+ There MUST NOT be more than one 'mount-point' statement in a
+ given 'container' or 'list' statement.
+
+ If a mount point is defined within a grouping, its label is
+ bound to the module where the grouping is used.
+
+ A mount point defines a place in the node hierarchy where
+ other data models may be attached. A server that implements a
+ module with a mount point populates the
+ '/schema-mounts/mount-point' list with detailed information on
+ which data models are mounted at each mount point.
+
+ Note that the 'mount-point' statement does not define a new
+ data node.";
+ }
+
+ /*
+ * State data nodes
+ */
+
+ container schema-mounts {
+ config false;
+ description
+ "Contains information about the structure of the overall
+ mounted data model implemented in the server.";
+ list namespace {
+ key "prefix";
+ description
+ "This list provides a mapping of namespace prefixes that are
+ used in XPath expressions of 'parent-reference' leafs to the
+ corresponding namespace URI references.";
+ leaf prefix {
+ type yang:yang-identifier;
+ description
+ "Namespace prefix.";
+ }
+ leaf uri {
+ type inet:uri;
+ description
+ "Namespace URI reference.";
+ }
+ }
+ list mount-point {
+ key "module label";
+
+ description
+ "Each entry of this list specifies a schema for a particular
+ mount point.
+
+ Each mount point MUST be defined using the 'mount-point'
+ extension in one of the modules listed in the server's
+ YANG library instance with conformance type 'implement'.";
+ leaf module {
+ type yang:yang-identifier;
+ description
+ "Name of a module containing the mount point.";
+ }
+ leaf label {
+ type yang:yang-identifier;
+ description
+ "Label of the mount point defined using the 'mount-point'
+ extension.";
+ }
+ leaf config {
+ type boolean;
+ default "true";
+ description
+ "If this leaf is set to 'false', then all data nodes in the
+ mounted schema are read-only ('config false'), regardless
+ of their 'config' property.";
+ }
+ choice schema-ref {
+ mandatory true;
+ description
+ "Alternatives for specifying the schema.";
+ container inline {
+ presence
+ "A complete self-contained schema is mounted at the
+ mount point.";
+ description
+ "This node indicates that the server has mounted at least
+ the module 'ietf-yang-library' at the mount point, and
+ its instantiation provides the information about the
+ mounted schema.
+
+ Different instances of the mount point may have
+ different schemas mounted.";
+ }
+ container shared-schema {
+ presence
+ "The mounted schema together with the 'parent-reference'
+ make up the schema for this mount point.";
+
+ description
+ "This node indicates that the server has mounted at least
+ the module 'ietf-yang-library' at the mount point, and
+ its instantiation provides the information about the
+ mounted schema. When XPath expressions in the mounted
+ schema are evaluated, the 'parent-reference' leaf-list
+ is taken into account.
+
+ Different instances of the mount point MUST have the
+ same schema mounted.";
+ leaf-list parent-reference {
+ type yang:xpath1.0;
+ description
+ "Entries of this leaf-list are XPath 1.0 expressions
+ that are evaluated in the following context:
+
+ - The context node is the node in the parent data tree
+ where the mount-point is defined.
+
+ - The accessible tree is the parent data tree
+ *without* any nodes defined in modules that are
+ mounted inside the parent schema.
+
+ - The context position and context size are both equal
+ to 1.
+
+ - The set of variable bindings is empty.
+
+ - The function library is the core function library
+ defined in the W3C XPath 1.0 document
+ (http://www.w3.org/TR/1999/REC-xpath-19991116) and
+ the functions defined in Section 10 of RFC 7950.
+
+ - The set of namespace declarations is defined by the
+ 'namespace' list under 'schema-mounts'.
+
+ Each XPath expression MUST evaluate to a node-set
+ (possibly empty). For the purposes of evaluating
+ XPath expressions whose context nodes are defined in
+ the mounted schema, the union of all these node-sets
+ together with ancestor nodes are added to the
+ accessible data tree.
+
+ Note that in the case 'ietf-yang-schema-mount' is
+ itself mounted, a 'parent-reference' in the mounted
+ module may refer to nodes that were brought into the
+ accessible tree through a 'parent-reference' in the
+ parent schema.";
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/models/ietf-yang-structure-ext@2020-06-17.h b/models/ietf-yang-structure-ext@2020-06-17.h
new file mode 100644
index 0000000..f90d254
--- /dev/null
+++ b/models/ietf-yang-structure-ext@2020-06-17.h
@@ -0,0 +1,635 @@
+char ietf_yang_structure_ext_2020_06_17_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x2d, 0x65, 0x78, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x79,
+ 0x61, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x31, 0x2e, 0x31, 0x3b, 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, 0x69, 0x65,
+ 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x73, 0x74, 0x72, 0x75,
+ 0x63, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x65, 0x78, 0x74, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x73, 0x78, 0x3b,
+ 0x0a, 0x0a, 0x20, 0x20, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x45,
+ 0x54, 0x46, 0x20, 0x4e, 0x45, 0x54, 0x4d, 0x4f, 0x44, 0x20, 0x28, 0x4e,
+ 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20,
+ 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x6e,
+ 0x67, 0x75, 0x61, 0x67, 0x65, 0x29, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x69,
+ 0x6e, 0x67, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x57, 0x47, 0x20, 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20,
+ 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x61, 0x74,
+ 0x61, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x69, 0x65, 0x74,
+ 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x67, 0x2f, 0x6e, 0x65, 0x74,
+ 0x6d, 0x6f, 0x64, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x57,
+ 0x47, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x20, 0x3c, 0x6d, 0x61,
+ 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64, 0x40,
+ 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x3e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20,
+ 0x20, 0x20, 0x41, 0x6e, 0x64, 0x79, 0x20, 0x42, 0x69, 0x65, 0x72, 0x6d,
+ 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74,
+ 0x6f, 0x3a, 0x61, 0x6e, 0x64, 0x79, 0x40, 0x79, 0x75, 0x6d, 0x61, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20,
+ 0x20, 0x20, 0x4d, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x20, 0x42, 0x6a, 0x6f,
+ 0x72, 0x6b, 0x6c, 0x75, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d,
+ 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6d, 0x62, 0x6a, 0x2b, 0x69, 0x65,
+ 0x74, 0x66, 0x40, 0x34, 0x36, 0x36, 0x38, 0x2e, 0x73, 0x65, 0x3e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x3a, 0x20, 0x20, 0x20, 0x4b, 0x65, 0x6e, 0x74, 0x20, 0x57, 0x61, 0x74,
+ 0x73, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c,
+ 0x74, 0x6f, 0x3a, 0x6b, 0x65, 0x6e, 0x74, 0x2b, 0x69, 0x65, 0x74, 0x66,
+ 0x40, 0x77, 0x61, 0x74, 0x73, 0x65, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x3e,
+ 0x22, 0x3b, 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, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x65,
+ 0x70, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x73,
+ 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69,
+ 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x73, 0x74,
+ 0x72, 0x61, 0x63, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x74,
+ 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x27, 0x4d, 0x55, 0x53, 0x54, 0x27,
+ 0x2c, 0x20, 0x27, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x27,
+ 0x2c, 0x20, 0x27, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x27,
+ 0x2c, 0x20, 0x27, 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x27, 0x2c, 0x20, 0x27,
+ 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e,
+ 0x4f, 0x54, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44,
+ 0x27, 0x2c, 0x20, 0x27, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20, 0x4e,
+ 0x4f, 0x54, 0x27, 0x2c, 0x20, 0x27, 0x52, 0x45, 0x43, 0x4f, 0x4d, 0x4d,
+ 0x45, 0x4e, 0x44, 0x45, 0x44, 0x27, 0x2c, 0x20, 0x27, 0x4e, 0x4f, 0x54,
+ 0x20, 0x52, 0x45, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x44, 0x45, 0x44,
+ 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x4d, 0x41, 0x59,
+ 0x27, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x27, 0x4f, 0x50, 0x54, 0x49,
+ 0x4f, 0x4e, 0x41, 0x4c, 0x27, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61,
+ 0x72, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x74,
+ 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62,
+ 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x42, 0x43, 0x50, 0x20, 0x31, 0x34,
+ 0x20, 0x28, 0x52, 0x46, 0x43, 0x20, 0x32, 0x31, 0x31, 0x39, 0x29, 0x20,
+ 0x28, 0x52, 0x46, 0x43, 0x20, 0x38, 0x31, 0x37, 0x34, 0x29, 0x20, 0x77,
+ 0x68, 0x65, 0x6e, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x6c,
+ 0x79, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72,
+ 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x61, 0x70, 0x69,
+ 0x74, 0x61, 0x6c, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x73, 0x68, 0x6f,
+ 0x77, 0x6e, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74,
+ 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x32, 0x30, 0x20, 0x49, 0x45,
+ 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73,
+ 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20,
+ 0x61, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63,
+ 0x6f, 0x64, 0x65, 0x2e, 0x20, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x72, 0x69,
+ 0x67, 0x68, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65,
+ 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x64,
+ 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x69,
+ 0x6e, 0x61, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x6f, 0x64,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69,
+ 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20,
+ 0x70, 0x75, 0x72, 0x73, 0x75, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x65, 0x72,
+ 0x6d, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x69, 0x6d,
+ 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x42, 0x53, 0x44, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x73, 0x65, 0x74, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x74, 0x68, 0x20, 0x69,
+ 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e,
+ 0x63, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, 0x54,
+ 0x46, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x27, 0x73, 0x20, 0x4c, 0x65,
+ 0x67, 0x61, 0x6c, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x6c, 0x61,
+ 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x49, 0x45, 0x54, 0x46,
+ 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x2e, 0x69, 0x65, 0x74, 0x66,
+ 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
+ 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x29, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x59,
+ 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69,
+ 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46,
+ 0x43, 0x20, 0x38, 0x37, 0x39, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x72, 0x66, 0x63, 0x2d, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x2e,
+ 0x6f, 0x72, 0x67, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x72, 0x66, 0x63,
+ 0x38, 0x37, 0x39, 0x31, 0x29, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x52, 0x46, 0x43, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c,
+ 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66,
+ 0x75, 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x6e, 0x6f,
+ 0x74, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x22, 0x3b, 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, 0x49, 0x6e, 0x69,
+ 0x74, 0x69, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 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, 0x38, 0x37, 0x39, 0x31, 0x3a, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x53, 0x74,
+ 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x45, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d,
+ 0x65, 0x6e, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x79, 0x69, 0x6e, 0x2d, 0x65, 0x6c, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x72, 0x75, 0x65, 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, 0x54, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73,
+ 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66,
+ 0x79, 0x20, 0x61, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x63,
+ 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69,
+ 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x2e, 0x20, 0x20, 0x49, 0x74, 0x20,
+ 0x69, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
+ 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x68, 0x69, 0x65, 0x72, 0x61,
+ 0x72, 0x63, 0x68, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x20, 0x69, 0x6e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74,
+ 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
+ 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x6f, 0x72, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69,
+ 0x66, 0x69, 0x63, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x6f, 0x72,
+ 0x6d, 0x61, 0x74, 0x2e, 0x20, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74,
+ 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x61,
+ 0x20, 0x27, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x27,
+ 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x70, 0x65,
+ 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x65, 0x6e,
+ 0x65, 0x72, 0x69, 0x63, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x74,
+ 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x77, 0x68, 0x6f,
+ 0x73, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72,
+ 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x27, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65,
+ 0x27, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x6f, 0x65, 0x73,
+ 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20,
+ 0x61, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x2e, 0x20, 0x20, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x4d,
+ 0x55, 0x53, 0x54, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64,
+ 0x69, 0x6e, 0x67, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x2c, 0x20, 0x69,
+ 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x65, 0x64,
+ 0x69, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x69, 0x66, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69,
+ 0x63, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61,
+ 0x74, 0x6f, 0x72, 0x79, 0x20, 0x27, 0x6e, 0x61, 0x6d, 0x65, 0x27, 0x20,
+ 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6f,
+ 0x6e, 0x6c, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x73,
+ 0x20, 0x61, 0x20, 0x74, 0x6f, 0x70, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c,
+ 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2c, 0x20,
+ 0x69, 0x2e, 0x65, 0x2e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20,
+ 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x74, 0x6f, 0x20, 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27,
+ 0x20, 0x6f, 0x72, 0x20, 0x27, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x27, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x42, 0x4e, 0x46, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x62,
+ 0x65, 0x6c, 0x6f, 0x77, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x72,
+ 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x3a, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a,
+ 0x6d, 0x75, 0x73, 0x74, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x5d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73,
+ 0x74, 0x6d, 0x74, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x5b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x5d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x28, 0x74, 0x79,
+ 0x70, 0x65, 0x64, 0x65, 0x66, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x20, 0x2f,
+ 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x74,
+ 0x6d, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x64, 0x65, 0x66,
+ 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x41, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x77, 0x61, 0x79, 0x20,
+ 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x27, 0x61, 0x6e, 0x79, 0x64, 0x61,
+ 0x74, 0x61, 0x27, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x20, 0x20, 0x54,
+ 0x68, 0x69, 0x73, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x61,
+ 0x73, 0x20, 0x61, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x65, 0x72, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73,
+ 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x63, 0x68,
+ 0x69, 0x6c, 0x64, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x61,
+ 0x73, 0x20, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x61, 0x63, 0x68,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x69,
+ 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
+ 0x69, 0x6e, 0x67, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73,
+ 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x58, 0x50,
+ 0x61, 0x74, 0x68, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x74, 0x68, 0x61, 0x74,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6c, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x2d, 0x64, 0x65, 0x66, 0x2d, 0x73, 0x74, 0x6d, 0x74,
+ 0x20, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61,
+ 0x6c, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69,
+ 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69,
+ 0x6e, 0x67, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x6d, 0x75,
+ 0x73, 0x74, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x77, 0x68, 0x65,
+ 0x6e, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x70, 0x61, 0x74, 0x68,
+ 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x6d, 0x69, 0x6e, 0x2d, 0x65,
+ 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x74,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2d, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x6d, 0x61, 0x6e,
+ 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d,
+ 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x2d, 0x73, 0x74, 0x6d, 0x74,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2d, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x2d, 0x62, 0x79,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c,
+ 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x64,
+ 0x65, 0x66, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x20, 0x73, 0x75, 0x62, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x72,
+ 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x65,
+ 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65,
+ 0x6e, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69,
+ 0x6e, 0x20, 0x61, 0x20, 0x27, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x27, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
+ 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73,
+ 0x74, 0x6d, 0x74, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72,
+ 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x68,
+ 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6b, 0x65, 0x79, 0x2d, 0x73, 0x74,
+ 0x6d, 0x74, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20,
+ 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x73,
+ 0x74, 0x6d, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x67, 0x6e, 0x6f, 0x72,
+ 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x65, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e,
+ 0x74, 0x2d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x79, 0x69, 0x6e, 0x2d, 0x65, 0x6c, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x20, 0x74, 0x72, 0x75, 0x65, 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, 0x54, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65,
+ 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79,
+ 0x20, 0x61, 0x6e, 0x20, 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x59, 0x41,
+ 0x4e, 0x47, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x73, 0x74, 0x72, 0x75, 0x63,
+ 0x74, 0x75, 0x72, 0x65, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x2e, 0x20, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e,
+ 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x62, 0x65, 0x20, 0x68, 0x69, 0x65, 0x72, 0x61, 0x72, 0x63, 0x68,
+ 0x69, 0x63, 0x61, 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, 0x6e,
+ 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+ 0x20, 0x6f, 0x72, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x65, 0x6e, 0x63,
+ 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+ 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x6d, 0x6f, 0x73, 0x74, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72,
+ 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x61, 0x75,
+ 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x27, 0x2e,
+ 0x20, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e,
+ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x70,
+ 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65,
+ 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x79, 0x6e, 0x74,
+ 0x61, 0x78, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69,
+ 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f,
+ 0x20, 0x62, 0x65, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63,
+ 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x27, 0x70, 0x61, 0x74, 0x68, 0x27, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d,
+ 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f,
+ 0x72, 0x79, 0x20, 0x27, 0x70, 0x61, 0x74, 0x68, 0x27, 0x20, 0x70, 0x61,
+ 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74,
+ 0x75, 0x61, 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6e, 0x6f, 0x64,
+ 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x62, 0x65,
+ 0x69, 0x6e, 0x67, 0x20, 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x65,
+ 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x62,
+ 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x69, 0x64, 0x20, 0x73, 0x74, 0x72,
+ 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74,
+ 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69,
+ 0x72, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65,
+ 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2d, 0x6e, 0x6f, 0x64, 0x65,
+ 0x69, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+ 0x75, 0x72, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x75, 0x67, 0x6d, 0x65,
+ 0x6e, 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x72, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
+ 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41,
+ 0x4e, 0x47, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61,
+ 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6f, 0x6e,
+ 0x6c, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x73, 0x20,
+ 0x61, 0x20, 0x74, 0x6f, 0x70, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x69,
+ 0x2e, 0x65, 0x2e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x73,
+ 0x75, 0x62, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x74, 0x6f, 0x20, 0x27, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x27, 0x20,
+ 0x6f, 0x72, 0x20, 0x27, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x27, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x54, 0x68, 0x65, 0x20, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x74, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x4d, 0x55, 0x53, 0x54, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x41, 0x42, 0x4e, 0x46, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x62, 0x65,
+ 0x6c, 0x6f, 0x77, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
+ 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x3a, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x73,
+ 0x74, 0x61, 0x74, 0x75, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x5d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5b,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
+ 0x73, 0x74, 0x6d, 0x74, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x5d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x2a, 0x28,
+ 0x64, 0x61, 0x74, 0x61, 0x2d, 0x64, 0x65, 0x66, 0x2d, 0x73, 0x74, 0x6d,
+ 0x74, 0x20, 0x2f, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2d, 0x73, 0x74, 0x6d,
+ 0x74, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, 0x61,
+ 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74,
+ 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20,
+ 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x64, 0x6f, 0x63,
+ 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72,
+ 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x58, 0x50, 0x61,
+ 0x74, 0x68, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64,
+ 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x2c, 0x20,
+ 0x73, 0x75, 0x63, 0x68, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x6f, 0x63,
+ 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x65,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x2d, 0x64, 0x65, 0x66, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x20, 0x73, 0x75,
+ 0x62, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69,
+ 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e,
+ 0x74, 0x65, 0x64, 0x20, 0x27, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27,
+ 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x75,
+ 0x63, 0x74, 0x75, 0x72, 0x65, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x20, 0x69,
+ 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x77,
+ 0x61, 0x79, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x61,
+ 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x27, 0x20, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x64, 0x65,
+ 0x66, 0x69, 0x6e, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x36, 0x2e, 0x34, 0x2e, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x5b, 0x52, 0x46,
+ 0x43, 0x37, 0x39, 0x35, 0x30, 0x5d, 0x2e, 0x20, 0x54, 0x68, 0x69, 0x73,
+ 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x20,
+ 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x65,
+ 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+ 0x74, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67,
+ 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x6d, 0x75, 0x73, 0x74,
+ 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x2d,
+ 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x2d, 0x73,
+ 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x2d, 0x20, 0x6d, 0x69, 0x6e, 0x2d, 0x65, 0x6c, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20,
+ 0x6d, 0x61, 0x78, 0x2d, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+ 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61,
+ 0x74, 0x6f, 0x72, 0x79, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x75,
+ 0x6e, 0x69, 0x71, 0x75, 0x65, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20,
+ 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x2d, 0x62, 0x79, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20,
+ 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77,
+ 0x69, 0x6e, 0x67, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x64, 0x65, 0x66,
+ 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x20, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20,
+ 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20,
+ 0x75, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20,
+ 0x61, 0x6e, 0x20, 0x27, 0x61, 0x75, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
+ 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x27, 0x20, 0x65,
+ 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x54, 0x68,
+ 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x20,
+ 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69,
+ 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20,
+ 0x61, 0x20, 0x6b, 0x65, 0x79, 0x2d, 0x73, 0x74, 0x6d, 0x74, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x54, 0x68, 0x65,
+ 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x73, 0x74, 0x6d, 0x74,
+ 0x20, 0x69, 0x73, 0x20, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x20,
+ 0x69, 0x66, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2e, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x45, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x3a, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20,
+ 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70,
+ 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e,
+ 0x67, 0x2d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2d,
+ 0x65, 0x78, 0x74, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x20, 0x73, 0x78, 0x3b, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+ 0x78, 0x3a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20,
+ 0x66, 0x6f, 0x6f, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x20, 0x7b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20,
+ 0x66, 0x6f, 0x6f, 0x2d, 0x63, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x7d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x20, 0x62, 0x61, 0x72, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d,
+ 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61,
+ 0x6e, 0x67, 0x2d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65,
+ 0x2d, 0x65, 0x78, 0x74, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69,
+ 0x78, 0x20, 0x73, 0x78, 0x3b, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69,
+ 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x66, 0x6f, 0x6f, 0x3b, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x78, 0x3a, 0x61, 0x75, 0x67,
+ 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x20, 0x2f, 0x66, 0x6f, 0x6f, 0x3a, 0x66, 0x6f, 0x6f, 0x2d,
+ 0x64, 0x61, 0x74, 0x61, 0x2f, 0x66, 0x6f, 0x6f, 0x3a, 0x66, 0x6f, 0x6f,
+ 0x2d, 0x63, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x20, 0x61, 0x64, 0x64, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x31,
+ 0x20, 0x7b, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x33,
+ 0x32, 0x3b, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66,
+ 0x20, 0x61, 0x64, 0x64, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x32, 0x20, 0x7b,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x3b, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a,
+ 0x7d, 0x0a, 0x00
+};
diff --git a/models/ietf-yang-structure-ext@2020-06-17.yang b/models/ietf-yang-structure-ext@2020-06-17.yang
new file mode 100644
index 0000000..e3452a4
--- /dev/null
+++ b/models/ietf-yang-structure-ext@2020-06-17.yang
@@ -0,0 +1,206 @@
+module ietf-yang-structure-ext {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-structure-ext";
+ prefix sx;
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Author: Martin Bjorklund
+ <mailto:mbj+ietf@4668.se>
+
+ Author: Kent Watsen
+ <mailto:kent+ietf@watsen.net>";
+ description
+ "This module contains conceptual YANG specifications for defining
+ abstract data structures.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.
+
+ Copyright (c) 2020 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Simplified BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8791
+ (https://www.rfc-editor.org/info/rfc8791); see the RFC itself
+ for full legal notices.";
+
+ revision 2020-06-17 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8791: YANG Data Structure Extensions.";
+ }
+
+ extension structure {
+ argument name {
+ yin-element true;
+ }
+ description
+ "This extension is used to specify a YANG data structure that
+ represents conceptual data defined in YANG. It is intended to
+ describe hierarchical data independent of protocol context or
+ specific message encoding format. Data definition statements
+ within a 'structure' extension statement specify the generic
+ syntax for the specific YANG data structure, whose name is the
+ argument of the 'structure' extension statement.
+
+ Note that this extension does not define a media type. A
+ specification using this extension MUST specify the message
+ encoding rules, including the content media type, if
+ applicable.
+
+ The mandatory 'name' parameter value identifies the YANG data
+ structure that is being defined.
+
+ This extension is only valid as a top-level statement, i.e.,
+ given as a substatement to 'module' or 'submodule'.
+
+ The substatements of this extension MUST follow the ABNF
+ rules below, where the rules are defined in RFC 7950:
+
+ *must-stmt
+ [status-stmt]
+ [description-stmt]
+ [reference-stmt]
+ *(typedef-stmt / grouping-stmt)
+ *data-def-stmt
+
+ A YANG data structure defined with this extension statement is
+ encoded in the same way as an 'anydata' node. This means
+ that the name of the structure is encoded as a 'container',
+ with the instantiated child statements encoded as child nodes
+ to this node.
+
+ The module name and namespace value for the YANG module using
+ the extension statement are assigned to each of the data
+ definition statements resulting from the YANG data structure.
+
+ The XPath document element is the extension statement itself,
+ such that the child nodes of the document element are
+ represented by the data-def-stmt substatements within this
+ extension. This conceptual document is the context for the
+ following YANG statements:
+
+ - must-stmt
+ - when-stmt
+ - path-stmt
+ - min-elements-stmt
+ - max-elements-stmt
+ - mandatory-stmt
+ - unique-stmt
+ - ordered-by
+ - instance-identifier data type
+
+ The following data-def-stmt substatements are constrained
+ when used within a 'structure' extension statement.
+
+ - The list-stmt is not required to have a key-stmt defined.
+ - The config-stmt is ignored if present.
+ ";
+ }
+
+ extension augment-structure {
+ argument path {
+ yin-element true;
+ }
+ description
+ "This extension is used to specify an augmentation to a YANG
+ data structure defined with the 'structure' statement. It is
+ intended to describe hierarchical data independent of protocol
+ context or specific message encoding format.
+
+ This statement has almost the same structure as the
+ 'augment-stmt'. Data definition statements within this
+ statement specify the semantics and generic syntax for the
+ additional data to be added to the specific YANG data
+ structure, identified by the 'path' argument.
+
+ The mandatory 'path' parameter value identifies the YANG
+ conceptual data node that is being augmented and is
+ represented as an absolute-schema-nodeid string, where the
+ first node in the absolute-schema-nodeid string identifies the
+ YANG data structure to augment, and the rest of the nodes in
+ the string identifies the node within the YANG structure to
+ augment.
+
+ This extension is only valid as a top-level statement, i.e.,
+ given as a substatement to 'module' or 'submodule'.
+
+ The substatements of this extension MUST follow the ABNF
+ rules below, where the rules are defined in RFC 7950:
+
+ [status-stmt]
+ [description-stmt]
+ [reference-stmt]
+ 1*(data-def-stmt / case-stmt)
+
+ The module name and namespace value for the YANG module using
+ the extension statement are assigned to instance document data
+ conforming to the data definition statements within this
+ extension.
+
+ The XPath document element is the augmented extension
+ statement itself, such that the child nodes of the document
+ element are represented by the data-def-stmt substatements
+ within the augmented 'structure' statement.
+
+ The context node of the 'augment-structure' statement is
+ derived in the same way as the 'augment' statement, as defined
+ in Section 6.4.1 of [RFC7950]. This conceptual node is
+ considered the context node for the following YANG statements:
+
+ - must-stmt
+ - when-stmt
+ - path-stmt
+ - min-elements-stmt
+ - max-elements-stmt
+ - mandatory-stmt
+ - unique-stmt
+ - ordered-by
+ - instance-identifier data type
+
+ The following data-def-stmt substatements are constrained
+ when used within an 'augment-structure' extension statement.
+
+ - The list-stmt is not required to have a key-stmt defined.
+ - The config-stmt is ignored if present.
+
+ Example:
+
+ module foo {
+ import ietf-yang-structure-ext { prefix sx; }
+
+ sx:structure foo-data {
+ container foo-con { }
+ }
+ }
+
+ module bar {
+ import ietf-yang-structure-ext { prefix sx; }
+ import foo { prefix foo; }
+
+ sx:augment-structure /foo:foo-data/foo:foo-con {
+ leaf add-leaf1 { type int32; }
+ leaf add-leaf2 { type string; }
+ }
+ }
+ ";
+ }
+}
diff --git a/models/ietf-yang-types@2013-07-15.h b/models/ietf-yang-types@2013-07-15.h
new file mode 100644
index 0000000..45ce2d0
--- /dev/null
+++ b/models/ietf-yang-types@2013-07-15.h
@@ -0,0 +1,1292 @@
+unsigned char ietf_yang_types_2013_07_15_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d,
+ 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b,
+ 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, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67,
+ 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x70, 0x72, 0x65, 0x66,
+ 0x69, 0x78, 0x20, 0x22, 0x79, 0x61, 0x6e, 0x67, 0x22, 0x3b, 0x6f, 0x72,
+ 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x22,
+ 0x49, 0x45, 0x54, 0x46, 0x20, 0x4e, 0x45, 0x54, 0x4d, 0x4f, 0x44, 0x20,
+ 0x28, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20, 0x44, 0x61, 0x74,
+ 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x4c,
+ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x29, 0x20, 0x57, 0x6f, 0x72,
+ 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x3b,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x22, 0x57, 0x47, 0x20,
+ 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x69, 0x65, 0x74,
+ 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x67, 0x2f, 0x6e, 0x65, 0x74,
+ 0x6d, 0x6f, 0x64, 0x2f, 0x3e, 0x0a, 0x57, 0x47, 0x20, 0x4c, 0x69, 0x73,
+ 0x74, 0x3a, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a,
+ 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x64, 0x40, 0x69, 0x65, 0x74, 0x66, 0x2e,
+ 0x6f, 0x72, 0x67, 0x3e, 0x0a, 0x0a, 0x57, 0x47, 0x20, 0x43, 0x68, 0x61,
+ 0x69, 0x72, 0x3a, 0x20, 0x44, 0x61, 0x76, 0x69, 0x64, 0x20, 0x4b, 0x65,
+ 0x73, 0x73, 0x65, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f,
+ 0x3a, 0x64, 0x61, 0x76, 0x69, 0x64, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65,
+ 0x6e, 0x73, 0x40, 0x6e, 0x73, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x0a,
+ 0x0a, 0x57, 0x47, 0x20, 0x43, 0x68, 0x61, 0x69, 0x72, 0x3a, 0x20, 0x4a,
+ 0x75, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x20, 0x53, 0x63, 0x68, 0x6f, 0x65,
+ 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69,
+ 0x6c, 0x74, 0x6f, 0x3a, 0x6a, 0x2e, 0x73, 0x63, 0x68, 0x6f, 0x65, 0x6e,
+ 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65, 0x72, 0x40, 0x6a, 0x61, 0x63, 0x6f,
+ 0x62, 0x73, 0x2d, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x69, 0x74,
+ 0x79, 0x2e, 0x64, 0x65, 0x3e, 0x0a, 0x0a, 0x45, 0x64, 0x69, 0x74, 0x6f,
+ 0x72, 0x3a, 0x20, 0x20, 0x20, 0x4a, 0x75, 0x65, 0x72, 0x67, 0x65, 0x6e,
+ 0x20, 0x53, 0x63, 0x68, 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64,
+ 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x6f, 0x3a, 0x6a, 0x2e,
+ 0x73, 0x63, 0x68, 0x6f, 0x65, 0x6e, 0x77, 0x61, 0x65, 0x6c, 0x64, 0x65,
+ 0x72, 0x40, 0x6a, 0x61, 0x63, 0x6f, 0x62, 0x73, 0x2d, 0x75, 0x6e, 0x69,
+ 0x76, 0x65, 0x72, 0x73, 0x69, 0x74, 0x79, 0x2e, 0x64, 0x65, 0x3e, 0x22,
+ 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x79,
+ 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x64, 0x65, 0x72, 0x69,
+ 0x76, 0x65, 0x64, 0x0a, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x43, 0x6f,
+ 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x31, 0x33, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x61, 0x73, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x20, 0x20, 0x41, 0x6c, 0x6c, 0x20,
+ 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72,
+ 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x6f, 0x72, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x64, 0x20, 0x70, 0x75, 0x72, 0x73, 0x75, 0x61, 0x6e, 0x74,
+ 0x20, 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62,
+ 0x6a, 0x65, 0x63, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20,
+ 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x69, 0x6d, 0x70,
+ 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x42, 0x53, 0x44, 0x20, 0x4c,
+ 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, 0x73, 0x65, 0x74, 0x20, 0x66,
+ 0x6f, 0x72, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e, 0x63, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x27, 0x73, 0x20, 0x4c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x50, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x52, 0x65, 0x6c,
+ 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x49, 0x45, 0x54,
+ 0x46, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x0a,
+ 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x65, 0x65, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67,
+ 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2d, 0x69, 0x6e, 0x66,
+ 0x6f, 0x29, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20, 0x76, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x52, 0x46, 0x43, 0x20, 0x36, 0x39, 0x39, 0x31, 0x3b, 0x20, 0x73,
+ 0x65, 0x65, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x52, 0x46, 0x43, 0x20, 0x69,
+ 0x74, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66, 0x75,
+ 0x6c, 0x6c, 0x20, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x20, 0x6e, 0x6f, 0x74,
+ 0x69, 0x63, 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30, 0x37, 0x2d,
+ 0x31, 0x35, 0x20, 0x7b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e,
+ 0x67, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x73, 0x3a, 0x0a, 0x2d, 0x20, 0x79, 0x61, 0x6e, 0x67,
+ 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x0a,
+ 0x2d, 0x20, 0x68, 0x65, 0x78, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x0a, 0x2d, 0x20, 0x75, 0x75, 0x69, 0x64, 0x0a, 0x2d, 0x20, 0x64, 0x6f,
+ 0x74, 0x74, 0x65, 0x64, 0x2d, 0x71, 0x75, 0x61, 0x64, 0x22, 0x3b, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46,
+ 0x43, 0x20, 0x36, 0x39, 0x39, 0x31, 0x3a, 0x20, 0x43, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61,
+ 0x20, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x7d, 0x72, 0x65, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x30, 0x2d, 0x30,
+ 0x39, 0x2d, 0x32, 0x34, 0x20, 0x7b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x22, 0x49, 0x6e, 0x69, 0x74, 0x69,
+ 0x61, 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e,
+ 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20,
+ 0x22, 0x52, 0x46, 0x43, 0x20, 0x36, 0x30, 0x32, 0x31, 0x3a, 0x20, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x44,
+ 0x61, 0x74, 0x61, 0x20, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x7d,
+ 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x73, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x65, 0x67, 0x61,
+ 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72,
+ 0x0a, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x74, 0x6f,
+ 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x72,
+ 0x65, 0x61, 0x73, 0x65, 0x73, 0x20, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x20,
+ 0x69, 0x74, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x65, 0x73, 0x20, 0x61,
+ 0x0a, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x5e, 0x33, 0x32, 0x2d, 0x31,
+ 0x20, 0x28, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36, 0x37, 0x32, 0x39, 0x35,
+ 0x20, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x29, 0x2c, 0x20, 0x77,
+ 0x68, 0x65, 0x6e, 0x20, 0x69, 0x74, 0x0a, 0x77, 0x72, 0x61, 0x70, 0x73,
+ 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65,
+ 0x61, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x0a, 0x0a,
+ 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x20, 0x68, 0x61, 0x76,
+ 0x65, 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,
+ 0x20, 0x27, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x27, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68,
+ 0x75, 0x73, 0x2c, 0x20, 0x61, 0x0a, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20,
+ 0x28, 0x69, 0x6e, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x29,
+ 0x20, 0x6e, 0x6f, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+ 0x20, 0x20, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75,
+ 0x69, 0x74, 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x74, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c,
+ 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x69, 0x6e,
+ 0x67, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d,
+ 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x20, 0x61,
+ 0x74, 0x20, 0x72, 0x65, 0x2d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x0a, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x61, 0x74, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74,
+ 0x69, 0x6d, 0x65, 0x73, 0x20, 0x61, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63,
+ 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x20, 0x20,
+ 0x49, 0x66, 0x20, 0x73, 0x75, 0x63, 0x68, 0x0a, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20,
+ 0x6f, 0x63, 0x63, 0x75, 0x72, 0x2c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65,
+ 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x0a,
+ 0x61, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64,
+ 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x61, 0x74, 0x20, 0x74,
+ 0x69, 0x6d, 0x65, 0x73, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x0a, 0x72, 0x65, 0x2d, 0x69, 0x6e, 0x69, 0x74, 0x69,
+ 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74,
+ 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x73, 0x68, 0x6f, 0x75,
+ 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
+ 0x64, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6e, 0x20, 0x61,
+ 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73,
+ 0x74, 0x20, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75,
+ 0x69, 0x74, 0x79, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20,
+ 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64,
+ 0x65, 0x73, 0x2e, 0x20, 0x20, 0x41, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20, 0x4e, 0x4f, 0x54, 0x20,
+ 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x0a, 0x63,
+ 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x2e, 0x0a,
+ 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74,
+ 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69,
+ 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74,
+ 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x6f, 0x75, 0x6e,
+ 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e,
+ 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a,
+ 0x22, 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37, 0x38, 0x3a, 0x20, 0x53,
+ 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49,
+ 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x53, 0x4d, 0x49,
+ 0x76, 0x32, 0x29, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65,
+ 0x66, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64,
+ 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x7b,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x3b, 0x64, 0x65, 0x66, 0x61,
+ 0x75, 0x6c, 0x74, 0x20, 0x22, 0x30, 0x22, 0x3b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65,
+ 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x2d,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33,
+ 0x32, 0x0a, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x27,
+ 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x27, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x0a, 0x0a, 0x41, 0x20,
+ 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x28, 0x30, 0x29,
+ 0x20, 0x6f, 0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x74, 0x68,
+ 0x65, 0x72, 0x65, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x63,
+ 0x72, 0x65, 0x61, 0x73, 0x65, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x74, 0x6f,
+ 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x75, 0x6e, 0x74, 0x69,
+ 0x6c, 0x20, 0x69, 0x74, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x65, 0x73,
+ 0x0a, 0x61, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x5e, 0x33, 0x32,
+ 0x2d, 0x31, 0x20, 0x28, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36, 0x37, 0x32,
+ 0x39, 0x35, 0x20, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x29, 0x2c,
+ 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x74, 0x0a, 0x77, 0x72, 0x61,
+ 0x70, 0x73, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63,
+ 0x72, 0x65, 0x61, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x67, 0x61, 0x69,
+ 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e,
+ 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x74,
+ 0x68, 0x61, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x69, 0x73, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d,
+ 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
+ 0x74, 0x6f, 0x20, 0x77, 0x72, 0x61, 0x70, 0x2c, 0x20, 0x69, 0x74, 0x20,
+ 0x63, 0x61, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a,
+ 0x27, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x27, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x64, 0x65, 0x6c,
+ 0x74, 0x61, 0x2e, 0x20, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69,
+ 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x61, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f,
+ 0x20, 0x62, 0x65, 0x20, 0x61, 0x77, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75,
+ 0x6d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x74, 0x69,
+ 0x6d, 0x65, 0x0a, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x70,
+ 0x6f, 0x6c, 0x6c, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f,
+ 0x20, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x63, 0x74,
+ 0x75, 0x61, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20,
+ 0x74, 0x6f, 0x6f, 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x72, 0x20,
+ 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x20,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69,
+ 0x6d, 0x75, 0x6d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x0a, 0x0a, 0x49,
+ 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
+ 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20,
+ 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74,
+ 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20,
+ 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5a, 0x65, 0x72, 0x6f, 0x42, 0x61,
+ 0x73, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x33, 0x32,
+ 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46,
+ 0x43, 0x20, 0x34, 0x35, 0x30, 0x32, 0x3a, 0x20, 0x52, 0x65, 0x6d, 0x6f,
+ 0x74, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x4d,
+ 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x4d, 0x61,
+ 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, 0x61, 0x73, 0x65,
+ 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x22, 0x3b,
+ 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65,
+ 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x65, 0x67,
+ 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65,
+ 0x72, 0x0a, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x74,
+ 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x63,
+ 0x72, 0x65, 0x61, 0x73, 0x65, 0x73, 0x20, 0x75, 0x6e, 0x74, 0x69, 0x6c,
+ 0x20, 0x69, 0x74, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x65, 0x73, 0x20,
+ 0x61, 0x0a, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x5e, 0x36, 0x34, 0x2d,
+ 0x31, 0x20, 0x28, 0x31, 0x38, 0x34, 0x34, 0x36, 0x37, 0x34, 0x34, 0x30,
+ 0x37, 0x33, 0x37, 0x30, 0x39, 0x35, 0x35, 0x31, 0x36, 0x31, 0x35, 0x20,
+ 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x29, 0x2c, 0x0a, 0x77, 0x68,
+ 0x65, 0x6e, 0x20, 0x69, 0x74, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20,
+ 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61,
+ 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x66,
+ 0x72, 0x6f, 0x6d, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x0a, 0x0a, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65,
+ 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20,
+ 0x27, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x27, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x75,
+ 0x73, 0x2c, 0x20, 0x61, 0x0a, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x28,
+ 0x69, 0x6e, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x29, 0x20,
+ 0x6e, 0x6f, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x20,
+ 0x20, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x69,
+ 0x74, 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x6e, 0x6f, 0x74, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x6c,
+ 0x79, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x69, 0x6e, 0x67,
+ 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61,
+ 0x6c, 0x6c, 0x79, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x20, 0x61, 0x74,
+ 0x20, 0x72, 0x65, 0x2d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69,
+ 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x0a, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x61, 0x74, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x69,
+ 0x6d, 0x65, 0x73, 0x20, 0x61, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69,
+ 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x61, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20,
+ 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74,
+ 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x20, 0x20, 0x49,
+ 0x66, 0x20, 0x73, 0x75, 0x63, 0x68, 0x0a, 0x6f, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x6f,
+ 0x63, 0x63, 0x75, 0x72, 0x2c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63,
+ 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x0a, 0x61,
+ 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x61, 0x74, 0x20, 0x74, 0x69,
+ 0x6d, 0x65, 0x73, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68,
+ 0x61, 0x6e, 0x0a, 0x72, 0x65, 0x2d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
+ 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, 0x68,
+ 0x65, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x73, 0x68, 0x6f, 0x75, 0x6c,
+ 0x64, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,
+ 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x70,
+ 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74,
+ 0x20, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x69,
+ 0x74, 0x79, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62,
+ 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x73, 0x2e, 0x20, 0x20, 0x41, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
+ 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x62,
+ 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x0a, 0x63, 0x6f,
+ 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x2e, 0x0a, 0x0a,
+ 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73,
+ 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73,
+ 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a,
+ 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+ 0x65, 0x72, 0x36, 0x34, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22,
+ 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22,
+ 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37, 0x38, 0x3a, 0x20, 0x53, 0x74,
+ 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x4d,
+ 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49, 0x6e,
+ 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x53, 0x4d, 0x49, 0x76,
+ 0x32, 0x29, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66,
+ 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x2d,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x3b, 0x64, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x20, 0x22, 0x30, 0x22, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20,
+ 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x2d, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73,
+ 0x20, 0x61, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34,
+ 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x68, 0x61, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x27, 0x69,
+ 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x27, 0x20, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x0a, 0x0a, 0x41, 0x20, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20,
+ 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x28, 0x30, 0x29, 0x20,
+ 0x6f, 0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x61, 0x6e, 0x64, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65,
+ 0x72, 0x65, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x72,
+ 0x65, 0x61, 0x73, 0x65, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x74, 0x6f, 0x6e,
+ 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x75, 0x6e, 0x74, 0x69, 0x6c,
+ 0x20, 0x69, 0x74, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x65, 0x73, 0x0a,
+ 0x61, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x5e, 0x36, 0x34, 0x2d,
+ 0x31, 0x20, 0x28, 0x31, 0x38, 0x34, 0x34, 0x36, 0x37, 0x34, 0x34, 0x30,
+ 0x37, 0x33, 0x37, 0x30, 0x39, 0x35, 0x35, 0x31, 0x36, 0x31, 0x35, 0x20,
+ 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x29, 0x2c, 0x0a, 0x77, 0x68,
+ 0x65, 0x6e, 0x20, 0x69, 0x74, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20,
+ 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61,
+ 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x66,
+ 0x72, 0x6f, 0x6d, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x0a, 0x0a, 0x50,
+ 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74,
+ 0x20, 0x61, 0x6e, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72,
+ 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x69, 0x6e, 0x69,
+ 0x6d, 0x75, 0x6d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x74, 0x6f, 0x20,
+ 0x77, 0x72, 0x61, 0x70, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x63, 0x61, 0x6e,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x27, 0x69, 0x6e,
+ 0x69, 0x74, 0x69, 0x61, 0x6c, 0x27, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x2e,
+ 0x20, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6d, 0x70, 0x6f,
+ 0x72, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20,
+ 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x73,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65,
+ 0x20, 0x61, 0x77, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x74,
+ 0x69, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x0a,
+ 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x70, 0x6f, 0x6c, 0x6c,
+ 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x69,
+ 0x73, 0x63, 0x61, 0x72, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c,
+ 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x6f,
+ 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65,
+ 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d,
+ 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d,
+ 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75,
+ 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x5a, 0x65, 0x72, 0x6f, 0x42, 0x61, 0x73, 0x65, 0x64,
+ 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x36, 0x34, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x32,
+ 0x38, 0x35, 0x36, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c,
+ 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x43, 0x61, 0x70,
+ 0x61, 0x63, 0x69, 0x74, 0x79, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x54, 0x79,
+ 0x70, 0x65, 0x73, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65,
+ 0x66, 0x20, 0x67, 0x61, 0x75, 0x67, 0x65, 0x33, 0x32, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x3b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22,
+ 0x54, 0x68, 0x65, 0x20, 0x67, 0x61, 0x75, 0x67, 0x65, 0x33, 0x32, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x65,
+ 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67,
+ 0x65, 0x72, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x0a, 0x6d, 0x61,
+ 0x79, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65, 0x20, 0x6f,
+ 0x72, 0x20, 0x64, 0x65, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20,
+ 0x62, 0x75, 0x74, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x20, 0x6e, 0x65,
+ 0x76, 0x65, 0x72, 0x20, 0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x0a, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x72, 0x20, 0x66, 0x61, 0x6c, 0x6c,
+ 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x20, 0x61, 0x20, 0x6d, 0x69, 0x6e,
+ 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, 0x63, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x20, 0x62, 0x65, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72,
+ 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x32, 0x5e, 0x33, 0x32, 0x2d, 0x31,
+ 0x20, 0x28, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36, 0x37, 0x32, 0x39, 0x35,
+ 0x20, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x29, 0x2c, 0x20, 0x61,
+ 0x6e, 0x64, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d,
+ 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x63, 0x61, 0x6e,
+ 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c,
+ 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x30, 0x2e, 0x20, 0x20,
+ 0x54, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66,
+ 0x0a, 0x61, 0x20, 0x67, 0x61, 0x75, 0x67, 0x65, 0x33, 0x32, 0x20, 0x68,
+ 0x61, 0x73, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d,
+ 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x68, 0x65,
+ 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e,
+ 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x62, 0x65,
+ 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x65, 0x64, 0x20,
+ 0x69, 0x73, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c,
+ 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x78, 0x69,
+ 0x6d, 0x75, 0x6d, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x68, 0x61, 0x73, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6d,
+ 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x77, 0x68, 0x65, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x65,
+ 0x6c, 0x65, 0x64, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c,
+ 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65,
+ 0x71, 0x75, 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x73, 0x20,
+ 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x2e, 0x0a, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e,
+ 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x65,
+ 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x65, 0x64, 0x20,
+ 0x73, 0x75, 0x62, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x6c, 0x79,
+ 0x20, 0x64, 0x65, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65, 0x73, 0x0a, 0x62,
+ 0x65, 0x6c, 0x6f, 0x77, 0x20, 0x28, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61,
+ 0x73, 0x65, 0x73, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x29, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x28,
+ 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x29, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x67, 0x61, 0x75, 0x67,
+ 0x65, 0x33, 0x32, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x64, 0x65, 0x63,
+ 0x72, 0x65, 0x61, 0x73, 0x65, 0x73, 0x20, 0x28, 0x69, 0x6e, 0x63, 0x72,
+ 0x65, 0x61, 0x73, 0x65, 0x73, 0x29, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65,
+ 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71,
+ 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x47, 0x61, 0x75, 0x67, 0x65, 0x33, 0x32, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65,
+ 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x32,
+ 0x35, 0x37, 0x38, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x28, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x29, 0x22, 0x3b, 0x7d,
+ 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x67, 0x61, 0x75, 0x67,
+ 0x65, 0x36, 0x34, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x69,
+ 0x6e, 0x74, 0x36, 0x34, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20, 0x67, 0x61,
+ 0x75, 0x67, 0x65, 0x36, 0x34, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72,
+ 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20,
+ 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65,
+ 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2c, 0x20, 0x77, 0x68,
+ 0x69, 0x63, 0x68, 0x0a, 0x6d, 0x61, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x72,
+ 0x65, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x63, 0x72,
+ 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x73, 0x68,
+ 0x61, 0x6c, 0x6c, 0x20, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x65, 0x78,
+ 0x63, 0x65, 0x65, 0x64, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d,
+ 0x75, 0x6d, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x6e, 0x6f,
+ 0x72, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x77,
+ 0x20, 0x61, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d,
+ 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x0a, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x67,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20,
+ 0x32, 0x5e, 0x36, 0x34, 0x2d, 0x31, 0x20, 0x28, 0x31, 0x38, 0x34, 0x34,
+ 0x36, 0x37, 0x34, 0x34, 0x30, 0x37, 0x33, 0x37, 0x30, 0x39, 0x35, 0x35,
+ 0x31, 0x36, 0x31, 0x35, 0x29, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x74,
+ 0x68, 0x65, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20,
+ 0x62, 0x65, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x20, 0x30, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x0a, 0x61, 0x20, 0x67,
+ 0x61, 0x75, 0x67, 0x65, 0x36, 0x34, 0x20, 0x68, 0x61, 0x73, 0x20, 0x69,
+ 0x74, 0x73, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x65, 0x76, 0x65,
+ 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20,
+ 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x65, 0x64, 0x20, 0x69, 0x73, 0x20, 0x67,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20,
+ 0x6f, 0x72, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20,
+ 0x69, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x0a,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68,
+ 0x61, 0x73, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d,
+ 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x68, 0x65,
+ 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e,
+ 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x62, 0x65,
+ 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x65, 0x64, 0x20,
+ 0x69, 0x73, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c,
+ 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6d, 0x69, 0x6e, 0x69,
+ 0x6d, 0x75, 0x6d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x0a, 0x49,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20,
+ 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x65, 0x64, 0x20, 0x73, 0x75, 0x62, 0x73,
+ 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x20, 0x64, 0x65, 0x63,
+ 0x72, 0x65, 0x61, 0x73, 0x65, 0x73, 0x0a, 0x62, 0x65, 0x6c, 0x6f, 0x77,
+ 0x20, 0x28, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65, 0x73, 0x20,
+ 0x61, 0x62, 0x6f, 0x76, 0x65, 0x29, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d,
+ 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x28, 0x6d, 0x69, 0x6e, 0x69,
+ 0x6d, 0x75, 0x6d, 0x29, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x65, 0x0a, 0x67, 0x61, 0x75, 0x67, 0x65, 0x36, 0x34, 0x20,
+ 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x64, 0x65, 0x63, 0x72, 0x65, 0x61, 0x73,
+ 0x65, 0x73, 0x20, 0x28, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x65,
+ 0x73, 0x29, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74,
+ 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61,
+ 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x42, 0x61, 0x73, 0x65, 0x64,
+ 0x47, 0x61, 0x75, 0x67, 0x65, 0x36, 0x34, 0x20, 0x53, 0x4d, 0x49, 0x76,
+ 0x32, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x65, 0x64, 0x0a, 0x69, 0x6e, 0x20, 0x52, 0x46, 0x43, 0x20,
+ 0x32, 0x38, 0x35, 0x36, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x32, 0x38, 0x35,
+ 0x36, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43,
+ 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
+ 0x6c, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x43, 0x61, 0x70, 0x61, 0x63,
+ 0x69, 0x74, 0x79, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x54, 0x79, 0x70, 0x65,
+ 0x73, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74,
+ 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x28, 0x5b, 0x30, 0x2d, 0x31, 0x5d,
+ 0x28, 0x5c, 0x2e, 0x5b, 0x31, 0x2d, 0x33, 0x5d, 0x3f, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x29, 0x29, 0x7c, 0x28, 0x32, 0x5c, 0x2e, 0x28, 0x30, 0x7c,
+ 0x28, 0x5b, 0x31, 0x2d, 0x39, 0x5d, 0x5c, 0x64, 0x2a, 0x29, 0x29, 0x29,
+ 0x29, 0x28, 0x5c, 0x2e, 0x28, 0x30, 0x7c, 0x28, 0x5b, 0x31, 0x2d, 0x39,
+ 0x5d, 0x5c, 0x64, 0x2a, 0x29, 0x29, 0x29, 0x2a, 0x27, 0x3b, 0x7d, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22,
+ 0x54, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x73, 0x20, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61,
+ 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x0a, 0x61, 0x73, 0x73, 0x69, 0x67,
+ 0x6e, 0x65, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x69, 0x6e,
+ 0x20, 0x61, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2d, 0x68, 0x69, 0x65, 0x72, 0x61, 0x72, 0x63, 0x68,
+ 0x69, 0x63, 0x61, 0x6c, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x72,
+ 0x65, 0x65, 0x2e, 0x0a, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x6e, 0x6f, 0x74, 0x65, 0x64,
+ 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
+ 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x6c, 0x0a, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x65, 0x67, 0x61,
+ 0x74, 0x69, 0x76, 0x65, 0x20, 0x73, 0x75, 0x62, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x73, 0x2e, 0x20, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x73, 0x75,
+ 0x62, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
+ 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20,
+ 0x4e, 0x4f, 0x54, 0x20, 0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x20, 0x32,
+ 0x5e, 0x33, 0x32, 0x2d, 0x31, 0x20, 0x28, 0x34, 0x32, 0x39, 0x34, 0x39,
+ 0x36, 0x37, 0x32, 0x39, 0x35, 0x29, 0x2e, 0x20, 0x20, 0x53, 0x75, 0x62,
+ 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73,
+ 0x0a, 0x61, 0x72, 0x65, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
+ 0x20, 0x64, 0x6f, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69, 0x6e,
+ 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x0a, 0x77,
+ 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x0a, 0x0a,
+ 0x54, 0x68, 0x65, 0x20, 0x41, 0x53, 0x4e, 0x2e, 0x31, 0x20, 0x73, 0x74,
+ 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x0a, 0x73, 0x75,
+ 0x62, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
+ 0x20, 0x74, 0x6f, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x6f, 0x72,
+ 0x20, 0x32, 0x2e, 0x20, 0x20, 0x46, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x6d, 0x6f, 0x72, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x0a, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20,
+ 0x73, 0x75, 0x62, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x72, 0x61, 0x6e, 0x67, 0x65, 0x0a, 0x30, 0x20, 0x74, 0x6f, 0x20, 0x33,
+ 0x39, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72,
+ 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x30, 0x20, 0x6f,
+ 0x72, 0x20, 0x31, 0x2e, 0x20, 0x20, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x6c,
+ 0x79, 0x2c, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x41, 0x53, 0x4e, 0x2e, 0x31,
+ 0x20, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
+ 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x0a, 0x68, 0x61, 0x73,
+ 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x61, 0x74, 0x20, 0x6c,
+ 0x65, 0x61, 0x73, 0x74, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x73, 0x75, 0x62,
+ 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73,
+ 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65,
+ 0x72, 0x6e, 0x0a, 0x63, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x0a, 0x41, 0x6c, 0x74,
+ 0x68, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75,
+ 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x75, 0x62, 0x2d,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74,
+ 0x65, 0x64, 0x2c, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x64,
+ 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x20, 0x73, 0x68, 0x6f,
+ 0x75, 0x6c, 0x64, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6d,
+ 0x61, 0x79, 0x20, 0x62, 0x65, 0x0a, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x20, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x20,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x31, 0x32, 0x38,
+ 0x0a, 0x73, 0x75, 0x62, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x75,
+ 0x70, 0x65, 0x72, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x20, 0x4f, 0x42, 0x4a, 0x45,
+ 0x43, 0x54, 0x20, 0x49, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x46, 0x49, 0x45,
+ 0x52, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x73, 0x69, 0x6e, 0x63, 0x65,
+ 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72,
+ 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f,
+ 0x20, 0x31, 0x32, 0x38, 0x20, 0x73, 0x75, 0x62, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x20, 0x48,
+ 0x65, 0x6e, 0x63, 0x65, 0x2c, 0x0a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20, 0x4e,
+ 0x4f, 0x54, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74,
+ 0x6f, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x20, 0x4f, 0x42,
+ 0x4a, 0x45, 0x43, 0x54, 0x0a, 0x49, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x46,
+ 0x49, 0x45, 0x52, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3b, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2d, 0x31, 0x32, 0x38, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x53, 0x48, 0x4f, 0x55, 0x4c, 0x44, 0x20,
+ 0x62, 0x65, 0x0a, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x74,
+ 0x65, 0x61, 0x64, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x49, 0x53, 0x4f, 0x39, 0x38, 0x33, 0x34,
+ 0x2d, 0x31, 0x3a, 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f,
+ 0x67, 0x79, 0x20, 0x2d, 0x2d, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x53,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2d,
+ 0x2d, 0x20, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x75, 0x72, 0x65, 0x73,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x4f, 0x53,
+ 0x49, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69,
+ 0x65, 0x73, 0x3a, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20,
+ 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x75, 0x72, 0x65, 0x73, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x70, 0x0a, 0x61, 0x72, 0x63, 0x73, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x53, 0x4e, 0x2e, 0x31,
+ 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x49, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x74, 0x72, 0x65, 0x65, 0x22,
+ 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x6f, 0x62,
+ 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x2d, 0x31, 0x32, 0x38, 0x20, 0x7b, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x7b, 0x70, 0x61, 0x74,
+ 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5c, 0x64, 0x2a, 0x28, 0x5c, 0x2e,
+ 0x5c, 0x64, 0x2a, 0x29, 0x7b, 0x31, 0x2c, 0x31, 0x32, 0x37, 0x7d, 0x27,
+ 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20,
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x31, 0x32, 0x38,
+ 0x0a, 0x73, 0x75, 0x62, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61,
+ 0x6e, 0x74, 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69,
+ 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x20, 0x49, 0x44, 0x45,
+ 0x4e, 0x54, 0x49, 0x46, 0x49, 0x45, 0x52, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76,
+ 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37, 0x38, 0x3a,
+ 0x20, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f,
+ 0x66, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x53,
+ 0x4d, 0x49, 0x76, 0x32, 0x29, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65,
+ 0x64, 0x65, 0x66, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x69, 0x64, 0x65,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x7b, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x6c, 0x65,
+ 0x6e, 0x67, 0x74, 0x68, 0x20, 0x22, 0x31, 0x2e, 0x2e, 0x6d, 0x61, 0x78,
+ 0x22, 0x3b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5b,
+ 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5f, 0x5d, 0x5b, 0x61, 0x2d, 0x7a,
+ 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5f, 0x2e, 0x5d, 0x2a,
+ 0x27, 0x3b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x2e,
+ 0x7c, 0x2e, 0x2e, 0x7c, 0x5b, 0x5e, 0x78, 0x58, 0x5d, 0x2e, 0x2a, 0x7c,
+ 0x2e, 0x5b, 0x5e, 0x6d, 0x4d, 0x5d, 0x2e, 0x2a, 0x7c, 0x2e, 0x2e, 0x5b,
+ 0x5e, 0x6c, 0x4c, 0x5d, 0x2e, 0x2a, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x41, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61,
+ 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x72, 0x27, 0x0a, 0x72, 0x75, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x32,
+ 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x36, 0x30, 0x32, 0x30,
+ 0x2e, 0x20, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x0a, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6e, 0x20,
+ 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 0x65, 0x74, 0x69, 0x63, 0x20, 0x63,
+ 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x20,
+ 0x61, 0x6e, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72,
+ 0x65, 0x0a, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20, 0x62,
+ 0x79, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61,
+ 0x72, 0x79, 0x20, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 0x65, 0x74, 0x69,
+ 0x63, 0x20, 0x6f, 0x72, 0x0a, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2c,
+ 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73,
+ 0x2c, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2c, 0x20, 0x6f,
+ 0x72, 0x20, 0x64, 0x6f, 0x74, 0x73, 0x2e, 0x0a, 0x0a, 0x41, 0x20, 0x59,
+ 0x41, 0x4e, 0x47, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x20,
+ 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61,
+ 0x6e, 0x79, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x0a,
+ 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72,
+ 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x75, 0x70, 0x70, 0x65,
+ 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63,
+ 0x74, 0x65, 0x72, 0x0a, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
+ 0x20, 0x27, 0x78, 0x6d, 0x6c, 0x27, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 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, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4e, 0x65,
+ 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 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, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x64, 0x61, 0x74, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69, 0x6d,
+ 0x65, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20,
+ 0x27, 0x5c, 0x64, 0x7b, 0x34, 0x7d, 0x2d, 0x5c, 0x64, 0x7b, 0x32, 0x7d,
+ 0x2d, 0x5c, 0x64, 0x7b, 0x32, 0x7d, 0x54, 0x5c, 0x64, 0x7b, 0x32, 0x7d,
+ 0x3a, 0x5c, 0x64, 0x7b, 0x32, 0x7d, 0x3a, 0x5c, 0x64, 0x7b, 0x32, 0x7d,
+ 0x28, 0x5c, 0x2e, 0x5c, 0x64, 0x2b, 0x29, 0x3f, 0x28, 0x5a, 0x7c, 0x5b,
+ 0x5c, 0x2b, 0x5c, 0x2d, 0x5d, 0x5c, 0x64, 0x7b, 0x32, 0x7d, 0x3a, 0x5c,
+ 0x64, 0x7b, 0x32, 0x7d, 0x29, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x65,
+ 0x20, 0x64, 0x61, 0x74, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69,
+ 0x6d, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61,
+ 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x49, 0x53, 0x4f, 0x20, 0x38, 0x36, 0x30, 0x31,
+ 0x0a, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x61, 0x74, 0x65,
+ 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20,
+ 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x47, 0x72,
+ 0x65, 0x67, 0x6f, 0x72, 0x69, 0x61, 0x6e, 0x20, 0x63, 0x61, 0x6c, 0x65,
+ 0x6e, 0x64, 0x61, 0x72, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70,
+ 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65,
+ 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x64, 0x61, 0x74, 0x65, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x70,
+ 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e,
+ 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x35, 0x2e, 0x36,
+ 0x20, 0x6f, 0x66, 0x20, 0x52, 0x46, 0x43, 0x20, 0x33, 0x33, 0x33, 0x39,
+ 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x65, 0x2d,
+ 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69,
+ 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x20, 0x58, 0x4d,
+ 0x4c, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66,
+ 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x74,
+ 0x61, 0x62, 0x6c, 0x65, 0x20, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x3a, 0x0a, 0x0a, 0x28, 0x61, 0x29, 0x20, 0x54, 0x68,
+ 0x65, 0x20, 0x64, 0x61, 0x74, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74,
+ 0x69, 0x6d, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x64, 0x6f, 0x65,
+ 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20,
+ 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x79, 0x65, 0x61,
+ 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x28, 0x62, 0x29, 0x20, 0x54, 0x68, 0x65,
+ 0x20, 0x64, 0x61, 0x74, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69,
+ 0x6d, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x6f, 0x66, 0x66, 0x73,
+ 0x65, 0x74, 0x20, 0x2d, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x20, 0x69, 0x6e,
+ 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x75,
+ 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x69, 0x6d, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x28, 0x73,
+ 0x65, 0x65, 0x20, 0x52, 0x46, 0x43, 0x20, 0x33, 0x33, 0x33, 0x39, 0x29,
+ 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x30, 0x30, 0x3a, 0x30,
+ 0x30, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x2b, 0x30, 0x30, 0x3a, 0x30, 0x30,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x5a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x61, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x74,
+ 0x69, 0x6d, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x20,
+ 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x2e, 0x0a, 0x0a, 0x28,
+ 0x63, 0x29, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6f, 0x6e,
+ 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20,
+ 0x28, 0x73, 0x65, 0x65, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x29, 0x20,
+ 0x6f, 0x66, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x61, 0x6e, 0x64, 0x2d,
+ 0x74, 0x69, 0x6d, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x73,
+ 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x61,
+ 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x74, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x20,
+ 0x58, 0x4d, 0x4c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x77, 0x68,
+ 0x69, 0x63, 0x68, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73,
+ 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x74,
+ 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x55, 0x54, 0x43, 0x20,
+ 0x75, 0x73, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x6f, 0x66, 0x66, 0x73,
+ 0x65, 0x74, 0x20, 0x27, 0x5a, 0x27, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x69,
+ 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f,
+ 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74,
+ 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x44, 0x61, 0x74, 0x65,
+ 0x41, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x75, 0x61, 0x6c, 0x0a, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d,
+ 0x49, 0x76, 0x32, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x52, 0x46,
+ 0x43, 0x20, 0x33, 0x33, 0x33, 0x39, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20,
+ 0x61, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x0a,
+ 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x62, 0x65,
+ 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x2d, 0x64,
+ 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x75, 0x6c, 0x6c,
+ 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x73, 0x65, 0x63,
+ 0x66, 0x72, 0x61, 0x63, 0x2e, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x63,
+ 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72,
+ 0x6d, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x61, 0x74, 0x65,
+ 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20,
+ 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x0a, 0x7a,
+ 0x6f, 0x6e, 0x65, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x61, 0x20, 0x6e,
+ 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
+ 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x61, 0x6c, 0x63,
+ 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67,
+ 0x0a, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x27,
+ 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64,
+ 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65,
+ 0x74, 0x20, 0x74, 0x6f, 0x20, 0x55, 0x54, 0x43, 0x20, 0x74, 0x69, 0x6d,
+ 0x65, 0x2e, 0x20, 0x20, 0x41, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
+ 0x20, 0x6f, 0x66, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x27, 0x73, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20,
+ 0x74, 0x6f, 0x20, 0x55, 0x54, 0x43, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
+ 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x63, 0x61, 0x75, 0x73, 0x65, 0x20, 0x64,
+ 0x61, 0x74, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69, 0x6d, 0x65,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x0a, 0x74, 0x6f, 0x20, 0x63,
+ 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x72, 0x64,
+ 0x69, 0x6e, 0x67, 0x6c, 0x79, 0x2e, 0x20, 0x20, 0x53, 0x75, 0x63, 0x68,
+ 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x6d, 0x69, 0x67,
+ 0x68, 0x74, 0x20, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x20, 0x70, 0x65,
+ 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x0a, 0x69,
+ 0x6e, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x61, 0x20, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20,
+ 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c,
+ 0x79, 0x20, 0x64, 0x61, 0x79, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x73,
+ 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x0a, 0x28,
+ 0x44, 0x53, 0x54, 0x29, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x7a, 0x6f,
+ 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x63, 0x68,
+ 0x61, 0x6e, 0x67, 0x65, 0x73, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f,
+ 0x72, 0x6d, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x64, 0x61, 0x74,
+ 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61,
+ 0x6e, 0x20, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x74, 0x69,
+ 0x6d, 0x65, 0x20, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x28, 0x75, 0x73, 0x75,
+ 0x61, 0x6c, 0x6c, 0x79, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x69,
+ 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x29, 0x20, 0x75, 0x73, 0x65, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x6f, 0x66,
+ 0x66, 0x73, 0x65, 0x74, 0x0a, 0x2d, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x2e,
+ 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a,
+ 0x22, 0x52, 0x46, 0x43, 0x20, 0x33, 0x33, 0x33, 0x39, 0x3a, 0x20, 0x44,
+ 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x54, 0x69, 0x6d, 0x65,
+ 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x3a, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
+ 0x61, 0x6d, 0x70, 0x73, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37,
+ 0x39, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43,
+ 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x0a, 0x58, 0x53, 0x44,
+ 0x2d, 0x54, 0x59, 0x50, 0x45, 0x53, 0x3a, 0x20, 0x58, 0x4d, 0x4c, 0x20,
+ 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x50, 0x61, 0x72, 0x74, 0x20,
+ 0x32, 0x3a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x74, 0x79, 0x70, 0x65, 0x73,
+ 0x20, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x45, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65,
+ 0x66, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x20,
+ 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+ 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x22, 0x54, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x74, 0x69,
+ 0x63, 0x6b, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70,
+ 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x6f,
+ 0x6e, 0x2d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a,
+ 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2c, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x6f, 0x20, 0x32, 0x5e, 0x33, 0x32, 0x20, 0x28, 0x34, 0x32,
+ 0x39, 0x34, 0x39, 0x36, 0x37, 0x32, 0x39, 0x36, 0x20, 0x64, 0x65, 0x63,
+ 0x69, 0x6d, 0x61, 0x6c, 0x29, 0x2c, 0x20, 0x69, 0x6e, 0x0a, 0x68, 0x75,
+ 0x6e, 0x64, 0x72, 0x65, 0x64, 0x74, 0x68, 0x73, 0x20, 0x6f, 0x66, 0x20,
+ 0x61, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, 0x65, 0x74,
+ 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x65, 0x70, 0x6f,
+ 0x63, 0x68, 0x73, 0x2e, 0x20, 0x20, 0x57, 0x68, 0x65, 0x6e, 0x20, 0x61,
+ 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x6e, 0x6f, 0x64, 0x65,
+ 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20,
+ 0x74, 0x68, 0x61, 0x74, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x6f, 0x66, 0x0a, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72,
+ 0x65, 0x6e, 0x63, 0x65, 0x20, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x73, 0x2e,
+ 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69,
+ 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x73,
+ 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e,
+ 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d,
+ 0x65, 0x54, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32,
+ 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37, 0x38, 0x3a, 0x20,
+ 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66,
+ 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x53, 0x4d,
+ 0x49, 0x76, 0x32, 0x29, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64,
+ 0x65, 0x66, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x3a,
+ 0x74, 0x69, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x3b, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54,
+ 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73,
+ 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73,
+ 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x0a, 0x74, 0x69, 0x6d, 0x65,
+ 0x74, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69,
+ 0x63, 0x68, 0x20, 0x61, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x0a, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x65, 0x64, 0x2e, 0x20, 0x20,
+ 0x54, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20,
+ 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69,
+ 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f,
+ 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
+ 0x64, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x20, 0x20, 0x57, 0x68, 0x65, 0x6e,
+ 0x0a, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x64, 0x20, 0x70, 0x72,
+ 0x69, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c,
+ 0x61, 0x73, 0x74, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65,
+ 0x0a, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20,
+ 0x74, 0x69, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x61, 0x74,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20,
+ 0x7a, 0x65, 0x72, 0x6f, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x73, 0x20, 0x7a, 0x65,
+ 0x72, 0x6f, 0x2e, 0x20, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68,
+ 0x61, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75,
+ 0x69, 0x72, 0x65, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x69, 0x6d,
+ 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x73, 0x0a, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x73, 0x65,
+ 0x74, 0x20, 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x77, 0x68,
+ 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x73, 0x73, 0x6f,
+ 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x74,
+ 0x69, 0x63, 0x6b, 0x73, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x65, 0x73, 0x20, 0x34,
+ 0x39, 0x37, 0x2b, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e,
+ 0x64, 0x20, 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x0a, 0x0a,
+ 0x54, 0x68, 0x65, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x73,
+ 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x70, 0x65,
+ 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x0a, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x75, 0x73, 0x69,
+ 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63,
+ 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65,
+ 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x69,
+ 0x6d, 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x4d,
+ 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37,
+ 0x39, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43,
+ 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x22, 0x3b, 0x7d, 0x74,
+ 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x70, 0x68, 0x79, 0x73, 0x2d,
+ 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61,
+ 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x5b, 0x30, 0x2d, 0x39,
+ 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x32, 0x7d, 0x28, 0x3a,
+ 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b,
+ 0x32, 0x7d, 0x29, 0x2a, 0x29, 0x3f, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x52, 0x65,
+ 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x65, 0x64,
+ 0x69, 0x61, 0x2d, 0x20, 0x6f, 0x72, 0x20, 0x70, 0x68, 0x79, 0x73, 0x69,
+ 0x63, 0x61, 0x6c, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x61, 0x64,
+ 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x72, 0x65, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x0a, 0x61, 0x73, 0x20, 0x61,
+ 0x20, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x63,
+ 0x74, 0x65, 0x74, 0x73, 0x2c, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x6f,
+ 0x63, 0x74, 0x65, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65,
+ 0x6e, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x77, 0x6f, 0x20,
+ 0x68, 0x65, 0x78, 0x61, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x0a,
+ 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x20, 0x4f, 0x63,
+ 0x74, 0x65, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x73, 0x65, 0x70,
+ 0x61, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f,
+ 0x6c, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63,
+ 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x0a, 0x72, 0x65, 0x70,
+ 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x75, 0x73, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61,
+ 0x73, 0x65, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72,
+ 0x73, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69,
+ 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c,
+ 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x50,
+ 0x68, 0x79, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x74,
+ 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65,
+ 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20,
+ 0x32, 0x35, 0x37, 0x39, 0x3a, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61,
+ 0x6c, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x22,
+ 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x6d, 0x61,
+ 0x63, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b,
+ 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5b, 0x30, 0x2d,
+ 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x32, 0x7d, 0x28,
+ 0x3a, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d,
+ 0x7b, 0x32, 0x7d, 0x29, 0x7b, 0x35, 0x7d, 0x27, 0x3b, 0x7d, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x54,
+ 0x68, 0x65, 0x20, 0x6d, 0x61, 0x63, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45,
+ 0x45, 0x45, 0x20, 0x38, 0x30, 0x32, 0x20, 0x4d, 0x41, 0x43, 0x20, 0x61,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x0a, 0x54, 0x68, 0x65, 0x20,
+ 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65,
+ 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63,
+ 0x61, 0x73, 0x65, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65,
+ 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74,
+ 0x69, 0x63, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61,
+ 0x6c, 0x65, 0x6e, 0x74, 0x0a, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x4d, 0x61, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x74,
+ 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65,
+ 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x49, 0x45, 0x45, 0x45,
+ 0x20, 0x38, 0x30, 0x32, 0x3a, 0x20, 0x49, 0x45, 0x45, 0x45, 0x20, 0x53,
+ 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4d, 0x65,
+ 0x74, 0x72, 0x6f, 0x70, 0x6f, 0x6c, 0x69, 0x74, 0x61, 0x6e, 0x20, 0x41,
+ 0x72, 0x65, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x3a,
+ 0x20, 0x4f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x0a, 0x52, 0x46, 0x43, 0x20, 0x32, 0x35, 0x37, 0x39, 0x3a,
+ 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x43, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x53, 0x4d, 0x49, 0x76, 0x32, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70,
+ 0x65, 0x64, 0x65, 0x66, 0x20, 0x78, 0x70, 0x61, 0x74, 0x68, 0x31, 0x2e,
+ 0x30, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x3b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x0a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73,
+ 0x20, 0x61, 0x6e, 0x20, 0x58, 0x50, 0x41, 0x54, 0x48, 0x20, 0x31, 0x2e,
+ 0x30, 0x20, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x2e, 0x0a, 0x0a, 0x57, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20,
+ 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x73, 0x70,
+ 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x50,
+ 0x61, 0x74, 0x68, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20,
+ 0x69, 0x6e, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x58, 0x50, 0x61, 0x74, 0x68, 0x20, 0x65, 0x78, 0x70, 0x72, 0x65,
+ 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x65, 0x76, 0x61,
+ 0x6c, 0x75, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x58, 0x50, 0x41, 0x54,
+ 0x48, 0x3a, 0x20, 0x58, 0x4d, 0x4c, 0x20, 0x50, 0x61, 0x74, 0x68, 0x20,
+ 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x20, 0x28, 0x58, 0x50,
+ 0x61, 0x74, 0x68, 0x29, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x31, 0x2e, 0x30, 0x22, 0x3b, 0x7d, 0x74, 0x79, 0x70, 0x65, 0x64,
+ 0x65, 0x66, 0x20, 0x68, 0x65, 0x78, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e,
+ 0x67, 0x20, 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20,
+ 0x27, 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46,
+ 0x5d, 0x7b, 0x32, 0x7d, 0x28, 0x3a, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d,
+ 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x32, 0x7d, 0x29, 0x2a, 0x29, 0x3f,
+ 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x0a, 0x22, 0x41, 0x20, 0x68, 0x65, 0x78, 0x61, 0x64, 0x65,
+ 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x73,
+ 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x64,
+ 0x20, 0x61, 0x73, 0x20, 0x68, 0x65, 0x78, 0x20, 0x64, 0x69, 0x67, 0x69,
+ 0x74, 0x73, 0x0a, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x6e, 0x73, 0x2e, 0x20,
+ 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63,
+ 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x73, 0x0a, 0x6c,
+ 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x63, 0x68, 0x61,
+ 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x22, 0x3b, 0x7d, 0x74,
+ 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x75, 0x69, 0x64, 0x20,
+ 0x7b, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x20, 0x7b, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x5b,
+ 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x38,
+ 0x7d, 0x2d, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46,
+ 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66,
+ 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b,
+ 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x31,
+ 0x32, 0x7d, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x41, 0x20, 0x55, 0x6e, 0x69, 0x76,
+ 0x65, 0x72, 0x73, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x55, 0x6e, 0x69, 0x71,
+ 0x75, 0x65, 0x20, 0x49, 0x44, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
+ 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x72,
+ 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x64, 0x65, 0x66, 0x69, 0x6e,
+ 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46, 0x43, 0x20, 0x34, 0x31,
+ 0x32, 0x32, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6e,
+ 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65,
+ 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73,
+ 0x65, 0x73, 0x0a, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65,
+ 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e,
+ 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77,
+ 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x55,
+ 0x55, 0x49, 0x44, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
+ 0x67, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x0a, 0x66, 0x38, 0x31, 0x64, 0x34, 0x66,
+ 0x61, 0x65, 0x2d, 0x37, 0x64, 0x65, 0x63, 0x2d, 0x31, 0x31, 0x64, 0x30,
+ 0x2d, 0x61, 0x37, 0x36, 0x35, 0x2d, 0x30, 0x30, 0x61, 0x30, 0x63, 0x39,
+ 0x31, 0x65, 0x36, 0x62, 0x66, 0x36, 0x0a, 0x22, 0x3b, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x22, 0x52, 0x46, 0x43, 0x20,
+ 0x34, 0x31, 0x32, 0x32, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x6e, 0x69, 0x76,
+ 0x65, 0x72, 0x73, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x55, 0x6e, 0x69, 0x71,
+ 0x75, 0x65, 0x20, 0x49, 0x44, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
+ 0x72, 0x20, 0x28, 0x55, 0x55, 0x49, 0x44, 0x29, 0x20, 0x55, 0x52, 0x4e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x3b, 0x7d,
+ 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x64, 0x6f, 0x74, 0x74,
+ 0x65, 0x64, 0x2d, 0x71, 0x75, 0x61, 0x64, 0x20, 0x7b, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x70, 0x61,
+ 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x27, 0x28, 0x28, 0x5b, 0x30, 0x2d,
+ 0x39, 0x5d, 0x7c, 0x5b, 0x31, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x7c, 0x31, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x7c, 0x32, 0x5b, 0x30, 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39,
+ 0x5d, 0x7c, 0x32, 0x35, 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x29, 0x5c, 0x2e,
+ 0x29, 0x7b, 0x33, 0x7d, 0x28, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b,
+ 0x31, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x31, 0x5b,
+ 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x32, 0x5b,
+ 0x30, 0x2d, 0x34, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x32, 0x35,
+ 0x5b, 0x30, 0x2d, 0x35, 0x5d, 0x29, 0x27, 0x3b, 0x7d, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x22, 0x41, 0x6e,
+ 0x20, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x33, 0x32,
+ 0x2d, 0x62, 0x69, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20,
+ 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x6f, 0x74, 0x74, 0x65, 0x64, 0x2d,
+ 0x71, 0x75, 0x61, 0x64, 0x0a, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2c, 0x20, 0x69, 0x2e, 0x65, 0x2e, 0x2c, 0x20, 0x66, 0x6f, 0x75,
+ 0x72, 0x20, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x20, 0x77, 0x72, 0x69,
+ 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x64, 0x65, 0x63, 0x69,
+ 0x6d, 0x61, 0x6c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x0a,
+ 0x61, 0x6e, 0x64, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65,
+ 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27,
+ 0x2e, 0x27, 0x20, 0x28, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x74, 0x6f,
+ 0x70, 0x29, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72,
+ 0x2e, 0x22, 0x3b, 0x7d, 0x7d, 0x00
+};
diff --git a/models/ietf-yang-types@2013-07-15.yang b/models/ietf-yang-types@2013-07-15.yang
new file mode 100644
index 0000000..ee58fa3
--- /dev/null
+++ b/models/ietf-yang-types@2013-07-15.yang
@@ -0,0 +1,474 @@
+module ietf-yang-types {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+ prefix "yang";
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: David Kessens
+ <mailto:david.kessens@nsn.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>";
+
+ description
+ "This module contains a collection of generally useful derived
+ YANG data types.
+
+ Copyright (c) 2013 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6991; see
+ the RFC itself for full legal notices.";
+
+ revision 2013-07-15 {
+ description
+ "This revision adds the following new data types:
+ - yang-identifier
+ - hex-string
+ - uuid
+ - dotted-quad";
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ revision 2010-09-24 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6021: Common YANG Data Types";
+ }
+
+ /*** collection of counter and gauge types ***/
+
+ typedef counter32 {
+ type uint32;
+ description
+ "The counter32 type represents a non-negative integer
+ that monotonically increases until it reaches a
+ maximum value of 2^32-1 (4294967295 decimal), when it
+ wraps around and starts increasing again from zero.
+
+ Counters have no defined 'initial' value, and thus, a
+ single value of a counter has (in general) no information
+ content. Discontinuities in the monotonically increasing
+ value normally occur at re-initialization of the
+ management system, and at other times as specified in the
+ description of a schema node using this type. If such
+ other times can occur, for example, the creation of
+ a schema node of type counter32 at times other than
+ re-initialization, then a corresponding schema node
+ should be defined, with an appropriate type, to indicate
+ the last discontinuity.
+
+ The counter32 type should not be used for configuration
+ schema nodes. A default statement SHOULD NOT be used in
+ combination with the type counter32.
+
+ In the value set and its semantics, this type is equivalent
+ to the Counter32 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef zero-based-counter32 {
+ type yang:counter32;
+ default "0";
+ description
+ "The zero-based-counter32 type represents a counter32
+ that has the defined 'initial' value zero.
+
+ A schema node of this type will be set to zero (0) on creation
+ and will thereafter increase monotonically until it reaches
+ a maximum value of 2^32-1 (4294967295 decimal), when it
+ wraps around and starts increasing again from zero.
+
+ Provided that an application discovers a new schema node
+ of this type within the minimum time to wrap, it can use the
+ 'initial' value as a delta. It is important for a management
+ station to be aware of this minimum time and the actual time
+ between polls, and to discard data if the actual time is too
+ long or there is no defined minimum time.
+
+ In the value set and its semantics, this type is equivalent
+ to the ZeroBasedCounter32 textual convention of the SMIv2.";
+ reference
+ "RFC 4502: Remote Network Monitoring Management Information
+ Base Version 2";
+ }
+
+ typedef counter64 {
+ type uint64;
+ description
+ "The counter64 type represents a non-negative integer
+ that monotonically increases until it reaches a
+ maximum value of 2^64-1 (18446744073709551615 decimal),
+ when it wraps around and starts increasing again from zero.
+
+ Counters have no defined 'initial' value, and thus, a
+ single value of a counter has (in general) no information
+ content. Discontinuities in the monotonically increasing
+ value normally occur at re-initialization of the
+ management system, and at other times as specified in the
+ description of a schema node using this type. If such
+ other times can occur, for example, the creation of
+ a schema node of type counter64 at times other than
+ re-initialization, then a corresponding schema node
+ should be defined, with an appropriate type, to indicate
+ the last discontinuity.
+
+ The counter64 type should not be used for configuration
+ schema nodes. A default statement SHOULD NOT be used in
+ combination with the type counter64.
+
+ In the value set and its semantics, this type is equivalent
+ to the Counter64 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef zero-based-counter64 {
+ type yang:counter64;
+ default "0";
+ description
+ "The zero-based-counter64 type represents a counter64 that
+ has the defined 'initial' value zero.
+
+ A schema node of this type will be set to zero (0) on creation
+ and will thereafter increase monotonically until it reaches
+ a maximum value of 2^64-1 (18446744073709551615 decimal),
+ when it wraps around and starts increasing again from zero.
+
+ Provided that an application discovers a new schema node
+ of this type within the minimum time to wrap, it can use the
+ 'initial' value as a delta. It is important for a management
+ station to be aware of this minimum time and the actual time
+ between polls, and to discard data if the actual time is too
+ long or there is no defined minimum time.
+
+ In the value set and its semantics, this type is equivalent
+ to the ZeroBasedCounter64 textual convention of the SMIv2.";
+ reference
+ "RFC 2856: Textual Conventions for Additional High Capacity
+ Data Types";
+ }
+
+ typedef gauge32 {
+ type uint32;
+ description
+ "The gauge32 type represents a non-negative integer, which
+ may increase or decrease, but shall never exceed a maximum
+ value, nor fall below a minimum value. The maximum value
+ cannot be greater than 2^32-1 (4294967295 decimal), and
+ the minimum value cannot be smaller than 0. The value of
+ a gauge32 has its maximum value whenever the information
+ being modeled is greater than or equal to its maximum
+ value, and has its minimum value whenever the information
+ being modeled is smaller than or equal to its minimum value.
+ If the information being modeled subsequently decreases
+ below (increases above) the maximum (minimum) value, the
+ gauge32 also decreases (increases).
+
+ In the value set and its semantics, this type is equivalent
+ to the Gauge32 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef gauge64 {
+ type uint64;
+ description
+ "The gauge64 type represents a non-negative integer, which
+ may increase or decrease, but shall never exceed a maximum
+ value, nor fall below a minimum value. The maximum value
+ cannot be greater than 2^64-1 (18446744073709551615), and
+ the minimum value cannot be smaller than 0. The value of
+ a gauge64 has its maximum value whenever the information
+ being modeled is greater than or equal to its maximum
+ value, and has its minimum value whenever the information
+ being modeled is smaller than or equal to its minimum value.
+ If the information being modeled subsequently decreases
+ below (increases above) the maximum (minimum) value, the
+ gauge64 also decreases (increases).
+
+ In the value set and its semantics, this type is equivalent
+ to the CounterBasedGauge64 SMIv2 textual convention defined
+ in RFC 2856";
+ reference
+ "RFC 2856: Textual Conventions for Additional High Capacity
+ Data Types";
+ }
+
+ /*** collection of identifier-related types ***/
+
+ typedef object-identifier {
+ type string {
+ pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+ + '(\.(0|([1-9]\d*)))*';
+ }
+ description
+ "The object-identifier type represents administratively
+ assigned names in a registration-hierarchical-name tree.
+
+ Values of this type are denoted as a sequence of numerical
+ non-negative sub-identifier values. Each sub-identifier
+ value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers
+ are separated by single dots and without any intermediate
+ whitespace.
+
+ The ASN.1 standard restricts the value space of the first
+ sub-identifier to 0, 1, or 2. Furthermore, the value space
+ of the second sub-identifier is restricted to the range
+ 0 to 39 if the first sub-identifier is 0 or 1. Finally,
+ the ASN.1 standard requires that an object identifier
+ has always at least two sub-identifiers. The pattern
+ captures these restrictions.
+
+ Although the number of sub-identifiers is not limited,
+ module designers should realize that there may be
+ implementations that stick with the SMIv2 limit of 128
+ sub-identifiers.
+
+ This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+ since it is not restricted to 128 sub-identifiers. Hence,
+ this type SHOULD NOT be used to represent the SMIv2 OBJECT
+ IDENTIFIER type; the object-identifier-128 type SHOULD be
+ used instead.";
+ reference
+ "ISO9834-1: Information technology -- Open Systems
+ Interconnection -- Procedures for the operation of OSI
+ Registration Authorities: General procedures and top
+ arcs of the ASN.1 Object Identifier tree";
+ }
+
+ typedef object-identifier-128 {
+ type object-identifier {
+ pattern '\d*(\.\d*){1,127}';
+ }
+ description
+ "This type represents object-identifiers restricted to 128
+ sub-identifiers.
+
+ In the value set and its semantics, this type is equivalent
+ to the OBJECT IDENTIFIER type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef yang-identifier {
+ type string {
+ length "1..max";
+ pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+ pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+ }
+ description
+ "A YANG identifier string as defined by the 'identifier'
+ rule in Section 12 of RFC 6020. An identifier must
+ start with an alphabetic character or an underscore
+ followed by an arbitrary sequence of alphabetic or
+ numeric characters, underscores, hyphens, or dots.
+
+ A YANG identifier MUST NOT start with any possible
+ combination of the lowercase or uppercase character
+ sequence 'xml'.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the Network
+ Configuration Protocol (NETCONF)";
+ }
+
+ /*** collection of types related to date and time***/
+
+ typedef date-and-time {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+ + '(Z|[\+\-]\d{2}:\d{2})';
+ }
+ description
+ "The date-and-time type is a profile of the ISO 8601
+ standard for representation of dates and times using the
+ Gregorian calendar. The profile is defined by the
+ date-time production in Section 5.6 of RFC 3339.
+
+ The date-and-time type is compatible with the dateTime XML
+ schema type with the following notable exceptions:
+
+ (a) The date-and-time type does not allow negative years.
+
+ (b) The date-and-time time-offset -00:00 indicates an unknown
+ time zone (see RFC 3339) while -00:00 and +00:00 and Z
+ all represent the same time zone in dateTime.
+
+ (c) The canonical format (see below) of data-and-time values
+ differs from the canonical format used by the dateTime XML
+ schema type, which requires all times to be in UTC using
+ the time-offset 'Z'.
+
+ This type is not equivalent to the DateAndTime textual
+ convention of the SMIv2 since RFC 3339 uses a different
+ separator between full-date and full-time and provides
+ higher resolution of time-secfrac.
+
+ The canonical format for date-and-time values with a known time
+ zone uses a numeric time zone offset that is calculated using
+ the device's configured known offset to UTC time. A change of
+ the device's offset to UTC time will cause date-and-time values
+ to change accordingly. Such changes might happen periodically
+ in case a server follows automatically daylight saving time
+ (DST) time zone offset changes. The canonical format for
+ date-and-time values with an unknown time zone (usually
+ referring to the notion of local time) uses the time-offset
+ -00:00.";
+ reference
+ "RFC 3339: Date and Time on the Internet: Timestamps
+ RFC 2579: Textual Conventions for SMIv2
+ XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+ }
+
+ typedef timeticks {
+ type uint32;
+ description
+ "The timeticks type represents a non-negative integer that
+ represents the time, modulo 2^32 (4294967296 decimal), in
+ hundredths of a second between two epochs. When a schema
+ node is defined that uses this type, the description of
+ the schema node identifies both of the reference epochs.
+
+ In the value set and its semantics, this type is equivalent
+ to the TimeTicks type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef timestamp {
+ type yang:timeticks;
+ description
+ "The timestamp type represents the value of an associated
+ timeticks schema node at which a specific occurrence
+ happened. The specific occurrence must be defined in the
+ description of any schema node defined using this type. When
+ the specific occurrence occurred prior to the last time the
+ associated timeticks attribute was zero, then the timestamp
+ value is zero. Note that this requires all timestamp values
+ to be reset to zero when the value of the associated timeticks
+ attribute reaches 497+ days and wraps around to zero.
+
+ The associated timeticks schema node must be specified
+ in the description of any schema node using this type.
+
+ In the value set and its semantics, this type is equivalent
+ to the TimeStamp textual convention of the SMIv2.";
+ reference
+ "RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ /*** collection of generic address types ***/
+
+ typedef phys-address {
+ type string {
+ pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+ }
+
+ description
+ "Represents media- or physical-level addresses represented
+ as a sequence octets, each octet represented by two hexadecimal
+ numbers. Octets are separated by colons. The canonical
+ representation uses lowercase characters.
+
+ In the value set and its semantics, this type is equivalent
+ to the PhysAddress textual convention of the SMIv2.";
+ reference
+ "RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ typedef mac-address {
+ type string {
+ pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+ }
+ description
+ "The mac-address type represents an IEEE 802 MAC address.
+ The canonical representation uses lowercase characters.
+
+ In the value set and its semantics, this type is equivalent
+ to the MacAddress textual convention of the SMIv2.";
+ reference
+ "IEEE 802: IEEE Standard for Local and Metropolitan Area
+ Networks: Overview and Architecture
+ RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ /*** collection of XML-specific types ***/
+
+ typedef xpath1.0 {
+ type string;
+ description
+ "This type represents an XPATH 1.0 expression.
+
+ When a schema node is defined that uses this type, the
+ description of the schema node MUST specify the XPath
+ context in which the XPath expression is evaluated.";
+ reference
+ "XPATH: XML Path Language (XPath) Version 1.0";
+ }
+
+ /*** collection of string types ***/
+
+ typedef hex-string {
+ type string {
+ pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+ }
+ description
+ "A hexadecimal string with octets represented as hex digits
+ separated by colons. The canonical representation uses
+ lowercase characters.";
+ }
+
+ typedef uuid {
+ type string {
+ pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+ + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+ }
+ description
+ "A Universally Unique IDentifier in the string representation
+ defined in RFC 4122. The canonical representation uses
+ lowercase characters.
+
+ The following is an example of a UUID in string representation:
+ f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+ ";
+ reference
+ "RFC 4122: A Universally Unique IDentifier (UUID) URN
+ Namespace";
+ }
+
+ typedef dotted-quad {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+ }
+ description
+ "An unsigned 32-bit number expressed in the dotted-quad
+ notation, i.e., four octets written as decimal numbers
+ and separated with the '.' (full stop) character.";
+ }
+}
diff --git a/models/yang@2022-06-16.h b/models/yang@2022-06-16.h
new file mode 100644
index 0000000..563398b
--- /dev/null
+++ b/models/yang@2022-06-16.h
@@ -0,0 +1,483 @@
+unsigned char yang_2022_06_16_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x20, 0x22, 0x75, 0x72, 0x6e, 0x3a, 0x69, 0x65, 0x74, 0x66, 0x3a,
+ 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x78, 0x6d, 0x6c, 0x3a, 0x6e,
+ 0x73, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x31, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e, 0x67,
+ 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20,
+ 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x6d, 0x64, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x2d, 0x64, 0x61, 0x74, 0x65, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30,
+ 0x38, 0x2d, 0x30, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20,
+ 0x20, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x69, 0x62, 0x79, 0x61,
+ 0x6e, 0x67, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x57, 0x65, 0x62,
+ 0x3a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x43, 0x45, 0x53, 0x4e, 0x45, 0x54, 0x2f, 0x6c, 0x69, 0x62, 0x79,
+ 0x61, 0x6e, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x52, 0x61, 0x64, 0x65, 0x6b,
+ 0x20, 0x4b, 0x72, 0x65, 0x6a, 0x63, 0x69, 0x20, 0x3c, 0x72, 0x6b, 0x72,
+ 0x65, 0x6a, 0x63, 0x69, 0x40, 0x63, 0x65, 0x73, 0x6e, 0x65, 0x74, 0x2e,
+ 0x63, 0x7a, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x4d, 0x69, 0x63, 0x68, 0x61, 0x6c, 0x20,
+ 0x56, 0x61, 0x73, 0x6b, 0x6f, 0x20, 0x3c, 0x6d, 0x76, 0x61, 0x73, 0x6b,
+ 0x6f, 0x40, 0x63, 0x65, 0x73, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x7a, 0x3e,
+ 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x54,
+ 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x64, 0x75, 0x6d,
+ 0x6d, 0x79, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x70,
+ 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65,
+ 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75,
+ 0x73, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46,
+ 0x43, 0x20, 0x36, 0x30, 0x32, 0x30, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52,
+ 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x2e, 0x20, 0x54, 0x68, 0x65,
+ 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6d,
+ 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x73, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x69, 0x62, 0x79, 0x61, 0x6e, 0x67, 0x20,
+ 0x64, 0x69, 0x66, 0x66, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f,
+ 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x72,
+ 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x32,
+ 0x2d, 0x30, 0x36, 0x2d, 0x31, 0x36, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65,
+ 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x34, 0x2d, 0x30,
+ 0x37, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x65,
+ 0x79, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x65,
+ 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x64, 0x69, 0x66, 0x66,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72,
+ 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x30,
+ 0x2d, 0x30, 0x36, 0x2d, 0x31, 0x37, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65,
+ 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66,
+ 0x6f, 0x72, 0x20, 0x64, 0x69, 0x66, 0x66, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x37, 0x2d, 0x30, 0x32, 0x2d, 0x32,
+ 0x30, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4e, 0x45,
+ 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x27, 0x73, 0x20, 0x65, 0x64, 0x69, 0x74,
+ 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x6d, 0x61, 0x6e, 0x69,
+ 0x70, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74,
+ 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x3a, 0x20,
+ 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x31, 0x2e, 0x31,
+ 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69,
+ 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30,
+ 0x32, 0x2d, 0x31, 0x31, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
+ 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b,
+ 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@2022-06-16.yang b/models/yang@2022-06-16.yang
new file mode 100644
index 0000000..0d41360
--- /dev/null
+++ b/models/yang@2022-06-16.yang
@@ -0,0 +1,194 @@
+module yang {
+ namespace "urn:ietf:params:xml:ns:yang:1";
+ prefix yang;
+
+ import ietf-yang-metadata {
+ prefix md;
+ revision-date 2016-08-05;
+ }
+
+ organization
+ "libyang";
+
+ contact
+ "Web: <https://github.com/CESNET/libyang/>
+ Author: Radek Krejci <rkrejci@cesnet.cz>
+ Author: Michal Vasko <mvasko@cesnet.cz>";
+
+ description
+ "This is a dummy module with no data formally supplying the definitions
+ of various metadata defined in RFC 6020 and RFC 7950. There are
+ additional metadata used in libyang diff data format.";
+
+ revision 2022-06-16 {
+ description
+ "Added typedef for key metadata type.";
+ }
+
+ revision 2021-04-07 {
+ description
+ "Added metadata for key-less list and state leaf-list diff.";
+ }
+
+ revision 2020-06-17 {
+ description
+ "Added metadata for diff.";
+ }
+
+ revision 2017-02-20 {
+ description
+ "Added metadata for NETCONF's edit-config manipulation with ordered
+ lists and leaf-lists.";
+ reference
+ "RFC 7950: The YANG 1.1 Data Modeling Language";
+ }
+
+ revision 2016-02-11 {
+ description
+ "Initial revision";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for
+ the Network Configuration Protocol (NETCONF)";
+ }
+
+ typedef instance-identifier-keys {
+ type string;
+ reference "RFC7950 section 7.8.6.";
+ description
+ "The key predicates of the full instance-identifier built-in type.";
+ }
+
+ md:annotation insert {
+ type enumeration {
+ enum first;
+ enum last;
+ enum before;
+ enum after;
+ }
+ reference "RFC7950 section 7.8.6. and section 7.7.9.";
+ description
+ "In user ordered leaf-list, this attribute can be used to control
+ where in the leaf-list the entry is inserted. It can be used during
+ the NETCONF <edit-config> \"create\" operations to insert a new list or
+ leaf-list entry, or during \"merge\" or \"replace\" operations to insert
+ a new list or leaf-list entry or move an existing one.
+
+ If the value is \"before\" or \"after\", the \"value\"/\"key\" attribute
+ MUST also be used to specify an existing entry in the list or leaf-list.
+
+ If no \"insert\" attribute is present in the \"create\" operation, it
+ defaults to \"last\".";
+ }
+
+ md:annotation value {
+ type string;
+ reference "RFC7950 section 7.7.9.";
+ description
+ "In user ordered leaf-list, this attribute must be used if the attribute
+ insert is used and specifies before/after which existing instance the
+ new instance should be inserted.";
+ }
+
+ md:annotation key {
+ type union {
+ type empty;
+ type instance-identifier-keys;
+ }
+ reference "RFC7950 section 7.8.6.";
+ description
+ "In user ordered list, this attribute must be used if the attribute
+ insert is used and specifies before/after which existing instance the
+ new instance should be inserted.";
+ }
+
+ md:annotation position {
+ type union {
+ type empty;
+ type uint32 {
+ range 1..max;
+ }
+ }
+ description
+ "In key-less list or state leaf-list, this attribute must be used if
+ the attribute insert is used and specifies the instance position
+ before/after which the new instance should be inserted.";
+ }
+
+ md:annotation operation {
+ type enumeration {
+ enum none {
+ description
+ "The node existed in both data trees but there is a nested node
+ with another operation. In case of a leaf, only its default
+ flag changed.";
+ }
+ enum create {
+ description
+ "The node did not exist in the first tree and was created in the
+ second tree.";
+ }
+ enum delete {
+ description
+ "The node existed in the first tree and was deleted in the second
+ tree.";
+ }
+ enum replace {
+ description
+ "The node value was changed or the node was moved for
+ leaves/anyxml/anydata and user-ordered lists/leaf-lists,
+ respectively.";
+ }
+ }
+ reference "RFC6241 section 7.2.";
+ description
+ "Operation of a node in a diff. If a node has no operation,
+ it is inherited from its nearest parent with an operation.
+ Top-level nodes must always have an operation.
+
+ User-ordered lists/leaf-lists with operation 'create' and
+ 'replace' MUST also have the 'key', 'value', or 'position'
+ metadata defined. It specifies the preceding instance.
+ In case the value of this metadata is empty, the node was
+ created/moved on the first position.
+
+ All the operations keep the meaning of edit-config operations
+ with similar names but some are further restricted, meaning
+ they are used for only a subset of use-cases.";
+ }
+
+ md:annotation orig-default {
+ type boolean;
+ description
+ "Information about the previous default state of the node.";
+ }
+
+ md:annotation orig-value {
+ type string;
+ description
+ "Previous value of a changed leaf. Alternatively, its meaning
+ is the same as the \"value\" attribute but identifies the original
+ leaf-list instance rather than the new one.";
+ }
+
+ md:annotation orig-key {
+ type union {
+ type empty;
+ type instance-identifier-keys;
+ }
+ description
+ "Its meaning is the same as the \"key\" attribute but identifies
+ the original list instance rather than the new one.";
+ }
+
+ md:annotation orig-position {
+ type union {
+ type empty;
+ type uint32 {
+ range 1..max;
+ }
+ }
+ description
+ "Its meaning is the same as the \"position\" attribute but identifies
+ the original list instance rather than the new one.";
+ }
+}
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..38f51ea
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,770 @@
+/**
+ * @file common.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief common internal definitions for libyang
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "common.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <sys/mman.h>
+#else
+#include <io.h>
+#endif
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+
+void *
+ly_realloc(void *ptr, size_t size)
+{
+ void *new_mem;
+
+ new_mem = realloc(ptr, size);
+ if (!new_mem) {
+ free(ptr);
+ }
+
+ return new_mem;
+}
+
+char *
+ly_strnchr(const char *s, int c, size_t len)
+{
+ for ( ; len && (*s != (char)c); ++s, --len) {}
+ return len ? (char *)s : NULL;
+}
+
+int
+ly_strncmp(const char *refstr, const char *str, size_t str_len)
+{
+ int rc = strncmp(refstr, str, str_len);
+
+ if (!rc && (refstr[str_len] == '\0')) {
+ return 0;
+ } else {
+ return rc ? rc : 1;
+ }
+}
+
+LY_ERR
+ly_strntou8(const char *nptr, size_t len, uint8_t *ret)
+{
+ uint8_t num = 0, dig, dec_pow;
+
+ if (len > 3) {
+ /* overflow for sure */
+ return LY_EDENIED;
+ }
+
+ dec_pow = 1;
+ for ( ; len && isdigit(nptr[len - 1]); --len) {
+ dig = nptr[len - 1] - 48;
+
+ if (LY_OVERFLOW_MUL(UINT8_MAX, dig, dec_pow)) {
+ return LY_EDENIED;
+ }
+ dig *= dec_pow;
+
+ if (LY_OVERFLOW_ADD(UINT8_MAX, num, dig)) {
+ return LY_EDENIED;
+ }
+ num += dig;
+
+ dec_pow *= 10;
+ }
+
+ if (len) {
+ return LY_EVALID;
+ }
+ *ret = num;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix, const char **str_next)
+{
+ const char *stop, *prefix;
+ size_t bytes_read;
+ uint32_t c;
+ ly_bool prefix_found;
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(len && is_prefix && str_next);
+
+#define IS_AT_END(PTR, STR_END) (STR_END ? PTR == STR_END : !(*PTR))
+
+ *str_next = NULL;
+ *is_prefix = 0;
+ *len = 0;
+
+ if (!str_begin || !(*str_begin) || (str_begin == str_end)) {
+ return ret;
+ }
+
+ stop = str_begin;
+ prefix = NULL;
+ prefix_found = 0;
+
+ do {
+ /* look for the beginning of the YANG value */
+ do {
+ LY_CHECK_RET(ly_getutf8(&stop, &c, &bytes_read));
+ } while (!is_xmlqnamestartchar(c) && !IS_AT_END(stop, str_end));
+
+ if (IS_AT_END(stop, str_end)) {
+ break;
+ }
+
+ /* maybe the prefix was found */
+ prefix = stop - bytes_read;
+
+ /* look for the the end of the prefix */
+ do {
+ LY_CHECK_RET(ly_getutf8(&stop, &c, &bytes_read));
+ } while (is_xmlqnamechar(c) && !IS_AT_END(stop, str_end));
+
+ prefix_found = c == ':' ? 1 : 0;
+
+ /* if it wasn't the prefix, keep looking */
+ } while (!IS_AT_END(stop, str_end) && !prefix_found);
+
+ if ((str_begin == prefix) && prefix_found) {
+ /* prefix found at the beginning of the input string */
+ *is_prefix = 1;
+ *str_next = IS_AT_END(stop, str_end) ? NULL : stop;
+ *len = (stop - bytes_read) - str_begin;
+ } else if ((str_begin != prefix) && (prefix_found)) {
+ /* there is a some string before prefix */
+ *str_next = prefix;
+ *len = prefix - str_begin;
+ } else {
+ /* no prefix found */
+ *len = stop - str_begin;
+ }
+
+#undef IS_AT_END
+
+ return ret;
+}
+
+LY_ERR
+ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
+{
+ uint32_t c, aux;
+ size_t len;
+
+ if (bytes_read) {
+ (*bytes_read) = 0;
+ }
+
+ c = (*input)[0];
+ LY_CHECK_RET(!c, LY_EINVAL);
+
+ if (!(c & 0x80)) {
+ /* one byte character */
+ len = 1;
+
+ if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
+ return LY_EINVAL;
+ }
+ } else if ((c & 0xe0) == 0xc0) {
+ /* two bytes character */
+ len = 2;
+
+ aux = (*input)[1];
+ if ((aux & 0xc0) != 0x80) {
+ return LY_EINVAL;
+ }
+ c = ((c & 0x1f) << 6) | (aux & 0x3f);
+
+ if (c < 0x80) {
+ return LY_EINVAL;
+ }
+ } else if ((c & 0xf0) == 0xe0) {
+ /* three bytes character */
+ len = 3;
+
+ c &= 0x0f;
+ for (uint64_t i = 1; i <= 2; i++) {
+ aux = (*input)[i];
+ if ((aux & 0xc0) != 0x80) {
+ return LY_EINVAL;
+ }
+
+ c = (c << 6) | (aux & 0x3f);
+ }
+
+ if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
+ return LY_EINVAL;
+ }
+ } else if ((c & 0xf8) == 0xf0) {
+ /* four bytes character */
+ len = 4;
+
+ c &= 0x07;
+ for (uint64_t i = 1; i <= 3; i++) {
+ aux = (*input)[i];
+ if ((aux & 0xc0) != 0x80) {
+ return LY_EINVAL;
+ }
+
+ c = (c << 6) | (aux & 0x3f);
+ }
+
+ if ((c < 0x1000) || (c > 0x10ffff)) {
+ return LY_EINVAL;
+ }
+ } else {
+ return LY_EINVAL;
+ }
+
+ (*utf8_char) = c;
+ (*input) += len;
+ if (bytes_read) {
+ (*bytes_read) = len;
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
+{
+ if (value < 0x80) {
+ /* one byte character */
+ if ((value < 0x20) &&
+ (value != 0x09) &&
+ (value != 0x0a) &&
+ (value != 0x0d)) {
+ return LY_EINVAL;
+ }
+
+ dst[0] = value;
+ (*bytes_written) = 1;
+ } else if (value < 0x800) {
+ /* two bytes character */
+ dst[0] = 0xc0 | (value >> 6);
+ dst[1] = 0x80 | (value & 0x3f);
+ (*bytes_written) = 2;
+ } else if (value < 0xfffe) {
+ /* three bytes character */
+ if (((value & 0xf800) == 0xd800) ||
+ ((value >= 0xfdd0) && (value <= 0xfdef))) {
+ /* exclude surrogate blocks %xD800-DFFF */
+ /* exclude noncharacters %xFDD0-FDEF */
+ return LY_EINVAL;
+ }
+
+ dst[0] = 0xe0 | (value >> 12);
+ dst[1] = 0x80 | ((value >> 6) & 0x3f);
+ dst[2] = 0x80 | (value & 0x3f);
+
+ (*bytes_written) = 3;
+ } else if (value < 0x10fffe) {
+ if ((value & 0xffe) == 0xffe) {
+ /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
+ * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
+ * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
+ return LY_EINVAL;
+ }
+ /* four bytes character */
+ dst[0] = 0xf0 | (value >> 18);
+ dst[1] = 0x80 | ((value >> 12) & 0x3f);
+ dst[2] = 0x80 | ((value >> 6) & 0x3f);
+ dst[3] = 0x80 | (value & 0x3f);
+
+ (*bytes_written) = 4;
+ } else {
+ return LY_EINVAL;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Static table of the UTF8 characters lengths according to their first byte.
+ */
+static const unsigned char utf8_char_length_table[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
+};
+
+size_t
+ly_utf8len(const char *str, size_t bytes)
+{
+ size_t len = 0;
+ const char *ptr = str;
+
+ while (((size_t)(ptr - str) < bytes) && *ptr) {
+ ++len;
+ ptr += utf8_char_length_table[((unsigned char)(*ptr))];
+ }
+ return len;
+}
+
+size_t
+LY_VCODE_INSTREXP_len(const char *str)
+{
+ size_t len = 0;
+
+ if (!str) {
+ return len;
+ } else if (!str[0]) {
+ return 1;
+ }
+ for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
+ return len;
+}
+
+#ifdef HAVE_MMAP
+LY_ERR
+ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
+{
+ struct stat sb;
+ long pagesize;
+ size_t m;
+
+ assert(length);
+ assert(addr);
+ assert(fd >= 0);
+
+ if (fstat(fd, &sb) == -1) {
+ LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
+ return LY_ESYS;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
+ return LY_ESYS;
+ }
+ if (!sb.st_size) {
+ *addr = NULL;
+ return LY_SUCCESS;
+ }
+ pagesize = sysconf(_SC_PAGESIZE);
+
+ m = sb.st_size % pagesize;
+ if (m && (pagesize - m >= 1)) {
+ /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
+ *length = sb.st_size + 1;
+ *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
+ } else {
+ /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
+ * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
+ * Therefore we have to do the following hack with double mapping. First, the required number of bytes
+ * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
+ * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
+ * where the anonymous mapping starts. */
+ *length = sb.st_size + pagesize;
+ *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
+ }
+ if (*addr == MAP_FAILED) {
+ LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_munmap(void *addr, size_t length)
+{
+ if (munmap(addr, length)) {
+ return LY_ESYS;
+ }
+ return LY_SUCCESS;
+}
+
+#else
+
+LY_ERR
+ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
+{
+ struct stat sb;
+ size_t m;
+
+ assert(length);
+ assert(addr);
+ assert(fd >= 0);
+
+#if _WIN32
+ if (_setmode(fd, _O_BINARY) == -1) {
+ LOGERR(ctx, LY_ESYS, "Failed to switch the file descriptor to binary mode.", strerror(errno));
+ return LY_ESYS;
+ }
+#endif
+
+ if (fstat(fd, &sb) == -1) {
+ LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
+ return LY_ESYS;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
+ return LY_ESYS;
+ }
+ if (!sb.st_size) {
+ *addr = NULL;
+ return LY_SUCCESS;
+ }
+ /* On Windows, the mman-win32 mmap() emulation uses CreateFileMapping and MapViewOfFile, and these functions
+ * do not allow mapping more than "length of file" bytes for PROT_READ. Remapping existing mappings is not allowed, either.
+ * At that point the path of least resistance is just reading the file in as-is. */
+ m = sb.st_size + 1;
+ char *buf = calloc(m, 1);
+
+ if (!buf) {
+ LOGERR(ctx, LY_ESYS, "ly_mmap: malloc() failed (%s).", strerror(errno));
+ }
+ *addr = buf;
+ *length = m;
+
+ lseek(fd, 0, SEEK_SET);
+ ssize_t to_read = m - 1;
+
+ while (to_read > 0) {
+ ssize_t n = read(fd, buf, to_read);
+
+ if (n == 0) {
+ return LY_SUCCESS;
+ } else if (n < 0) {
+ if (errno == EINTR) {
+ continue; // can I get this on Windows?
+ }
+ LOGERR(ctx, LY_ESYS, "ly_mmap: read() failed (%s).", strerror(errno));
+ }
+ to_read -= n;
+ buf += n;
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_munmap(void *addr, size_t length)
+{
+ (void)length;
+ free(addr);
+ return LY_SUCCESS;
+}
+
+#endif
+
+LY_ERR
+ly_strcat(char **dest, const char *format, ...)
+{
+ va_list fp;
+ char *addition = NULL;
+ size_t len;
+
+ va_start(fp, format);
+ len = vasprintf(&addition, format, fp);
+ len += (*dest ? strlen(*dest) : 0) + 1;
+
+ if (*dest) {
+ *dest = ly_realloc(*dest, len);
+ if (!*dest) {
+ va_end(fp);
+ return LY_EMEM;
+ }
+ *dest = strcat(*dest, addition);
+ free(addition);
+ } else {
+ *dest = addition;
+ }
+
+ va_end(fp);
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t max, int base, int64_t *ret)
+{
+ LY_ERR rc = LY_SUCCESS;
+ char *ptr, *str;
+ int64_t i;
+
+ LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
+
+ /* duplicate the value */
+ str = strndup(val_str, val_len);
+ LY_CHECK_RET(!str, LY_EMEM);
+
+ /* parse the value to avoid accessing following bytes */
+ errno = 0;
+ i = strtoll(str, &ptr, base);
+ if (errno || (ptr == str)) {
+ /* invalid string */
+ rc = LY_EVALID;
+ } else if ((i < min) || (i > max)) {
+ /* invalid number */
+ rc = LY_EDENIED;
+ } else if (*ptr) {
+ while (isspace(*ptr)) {
+ ++ptr;
+ }
+ if (*ptr) {
+ /* invalid characters after some number */
+ rc = LY_EVALID;
+ }
+ }
+
+ /* cleanup */
+ free(str);
+ if (!rc) {
+ *ret = i;
+ }
+ return rc;
+}
+
+LY_ERR
+ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base, uint64_t *ret)
+{
+ LY_ERR rc = LY_SUCCESS;
+ char *ptr, *str;
+ uint64_t u;
+
+ LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
+
+ /* duplicate the value to avoid accessing following bytes */
+ str = strndup(val_str, val_len);
+ LY_CHECK_RET(!str, LY_EMEM);
+
+ /* parse the value */
+ errno = 0;
+ u = strtoull(str, &ptr, base);
+ if (errno || (ptr == str)) {
+ /* invalid string */
+ rc = LY_EVALID;
+ } else if ((u > max) || (u && (str[0] == '-'))) {
+ /* invalid number */
+ rc = LY_EDENIED;
+ } else if (*ptr) {
+ while (isspace(*ptr)) {
+ ++ptr;
+ }
+ if (*ptr) {
+ /* invalid characters after some number */
+ rc = LY_EVALID;
+ }
+ }
+
+ /* cleanup */
+ free(str);
+ if (!rc) {
+ *ret = u;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse an identifier.
+ *
+ * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
+ * identifier = (ALPHA / "_")
+ * *(ALPHA / DIGIT / "_" / "-" / ".")
+ *
+ * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
+ */
+static LY_ERR
+lys_parse_id(const char **id)
+{
+ assert(id && *id);
+
+ if (!is_yangidentstartchar(**id)) {
+ return LY_EINVAL;
+ }
+ ++(*id);
+
+ while (is_yangidentchar(**id)) {
+ ++(*id);
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+ assert(id && *id);
+ assert(prefix && prefix_len);
+ assert(name && name_len);
+
+ *prefix = *id;
+ *prefix_len = 0;
+ *name = NULL;
+ *name_len = 0;
+
+ LY_CHECK_RET(lys_parse_id(id));
+ if (**id == ':') {
+ /* there is prefix */
+ *prefix_len = *id - *prefix;
+ ++(*id);
+ *name = *id;
+
+ LY_CHECK_RET(lys_parse_id(id));
+ *name_len = *id - *name;
+ } else {
+ /* there is no prefix, so what we have as prefix now is actually the name */
+ *name = *prefix;
+ *name_len = *id - *name;
+ *prefix = NULL;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
+ const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
+ const char **errmsg)
+{
+ LY_ERR ret = LY_EVALID;
+ const char *in = *pred;
+ size_t offset = 1;
+ uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
+ char quot;
+
+ assert(in[0] == '[');
+
+ *prefix = *id = *value = NULL;
+ *prefix_len = *id_len = *value_len = 0;
+
+ /* leading *WSP */
+ for ( ; isspace(in[offset]); offset++) {}
+
+ if (isdigit(in[offset])) {
+ /* pos: "[" *WSP positive-integer-value *WSP "]" */
+ if (in[offset] == '0') {
+ /* zero */
+ *errmsg = "The position predicate cannot be zero.";
+ goto error;
+ }
+
+ /* positive-integer-value */
+ *value = &in[offset++];
+ for ( ; isdigit(in[offset]); offset++) {}
+ *value_len = &in[offset] - *value;
+
+ } else if (in[offset] == '.') {
+ /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
+ *id = &in[offset];
+ *id_len = 1;
+ offset++;
+ expr = 1;
+ } else if (in[offset] == '-') {
+ /* typically negative value */
+ *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
+ goto error;
+ } else {
+ /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
+ in = &in[offset];
+ if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
+ *errmsg = "Invalid node-identifier.";
+ goto error;
+ }
+ if ((format == LYD_XML) && !(*prefix)) {
+ /* all node names MUST be qualified with explicit namespace prefix */
+ *errmsg = "Missing prefix of a node name.";
+ goto error;
+ }
+ offset = in - *pred;
+ in = *pred;
+ expr = 2;
+ }
+
+ if (expr) {
+ /* *WSP "=" *WSP quoted-string *WSP "]" */
+ for ( ; isspace(in[offset]); offset++) {}
+
+ if (in[offset] != '=') {
+ if (expr == 1) {
+ *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
+ } else { /* 2 */
+ *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
+ }
+ goto error;
+ }
+ offset++;
+ for ( ; isspace(in[offset]); offset++) {}
+
+ /* quoted-string */
+ quot = in[offset++];
+ if ((quot != '\'') && (quot != '\"')) {
+ *errmsg = "String value is not quoted.";
+ goto error;
+ }
+ *value = &in[offset];
+ for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
+ if (in[offset] == quot) {
+ *value_len = &in[offset] - *value;
+ offset++;
+ } else {
+ *errmsg = "Value is not terminated quoted-string.";
+ goto error;
+ }
+ }
+
+ /* *WSP "]" */
+ for ( ; isspace(in[offset]); offset++) {}
+ if (in[offset] != ']') {
+ if (expr == 0) {
+ *errmsg = "Predicate (pos) is not terminated by \']\' character.";
+ } else if (expr == 1) {
+ *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
+ } else { /* 2 */
+ *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
+ }
+ goto error;
+ }
+ offset++;
+
+ if (offset <= limit) {
+ *pred = &in[offset];
+ return LY_SUCCESS;
+ }
+
+ /* we read after the limit */
+ *errmsg = "Predicate is incomplete.";
+ *prefix = *id = *value = NULL;
+ *prefix_len = *id_len = *value_len = 0;
+ offset = limit;
+ ret = LY_EINVAL;
+
+error:
+ *pred = &in[offset];
+ return ret;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..264fe81
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,626 @@
+/**
+ * @file common.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief common internal definitions for libyang
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_COMMON_H_
+#define LY_COMMON_H_
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "compat.h"
+#include "config.h"
+#include "context.h"
+#include "hash_table.h"
+#include "log.h"
+#include "schema_compile.h"
+#include "set.h"
+#include "tree_data.h"
+
+struct ly_ctx;
+struct ly_in;
+struct lysc_node;
+
+#if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
+# define THREAD_LOCAL _Thread_local
+#elif defined __GNUC__ || \
+ defined __SUNPRO_C || \
+ defined __xlC__
+# define THREAD_LOCAL __thread
+#elif defined _MSC_VER
+# define THREAD_LOCAL __declspec(thread)
+#else
+# error "Cannot define THREAD_LOCAL"
+#endif
+
+#define GETMACRO1(_1, NAME, ...) NAME
+#define GETMACRO2(_1, _2, NAME, ...) NAME
+#define GETMACRO3(_1, _2, _3, NAME, ...) NAME
+#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
+#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
+#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
+
+/******************************************************************************
+ * Logger
+ *****************************************************************************/
+
+extern ATOMIC_T ly_ll;
+extern ATOMIC_T ly_log_opts;
+
+struct ly_log_location_s {
+ uint64_t line; /**< One-time line value being reset after use - replaces whatever is in inputs */
+ struct ly_set inputs; /**< Set of const struct ly_in *in pointers providing the input handler with the line information (LIFO) */
+ struct ly_set scnodes; /**< Set of const struct lysc_node *scnode pointers providing the compiled schema node to generate path (LIFO) */
+ struct ly_set dnodes; /**< Set of const struct lyd_node *dnode pointers providing the data node to generate path (LIFO) */
+ struct ly_set paths; /**< Set of path strings (LIFO) */
+};
+
+/**
+ * @brief Print a log message and store it into the context (if provided).
+ *
+ * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] no Error type code.
+ * @param[in] format Format string to print.
+ */
+void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...);
+
+/**
+ * @brief Print Validation error and store it into the context (if provided).
+ *
+ * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
+ * @param[in] apptag Optional specific error-app-tag.
+ * @param[in] code Validation error code.
+ * @param[in] format Format string to print.
+ */
+void ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...);
+
+/**
+ * @brief Logger's location data setter.
+ *
+ * @param[in] scnode Compiled schema node.
+ * @param[in] dnode Data node.
+ * @param[in] path Direct path string to print.
+ * @param[in] in Input handler (providing line number)
+ * @param[in] line One-time line value to be reset when used.
+ */
+void ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode,
+ const char *path, const struct ly_in *in, uint64_t line);
+
+/**
+ * @brief Revert the specific logger's location data by number of changes made by ::ly_log_location().
+ *
+ * @param[in] scnode_steps Number of items in ::ly_log_location_s.scnodes to forget.
+ * @param[in] dnode_steps Number of items in ::ly_log_location_s.dnodes to forget.
+ * @param[in] path_steps Number of path strings in ::ly_log_location_s.paths to forget.
+ * @param[in] in_steps Number of input handlers ::ly_log_location_s.inputs to forget.
+ */
+void ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps);
+
+/**
+ * @brief Update location data for logger, not provided arguments (NULLs) are kept (does not override).
+ *
+ * @param[in] SCNODE Compiled schema node.
+ * @param[in] DNODE Data node.
+ * @param[in] PATH Direct path string to print.
+ * @param[in] IN Input handler (providing line number)
+ */
+#define LOG_LOCSET(SCNODE, DNODE, PATH, IN) \
+ ly_log_location(SCNODE, DNODE, PATH, IN, 0)
+
+/**
+ * @brief Update location data for logger, not provided arguments (NULLs) are kept (does not override).
+ *
+ * @param[in] SCNODE_STEPS Number of the compiled schema nodes to remove from the stack.
+ * @param[in] DNODE_STEPS Number of the data nodes to remove from the stack.
+ * @param[in] PATH_STEPS Number of the direct path strings to remove from the stack.
+ * @param[in] IN_STEPS Number of the input handlers (providing line number) to remove from the stack.
+ */
+#define LOG_LOCBACK(SCNODE_STEPS, DNODE_STEPS, PATH_STEPS, IN_STEPS) \
+ ly_log_location_revert(SCNODE_STEPS, DNODE_STEPS, PATH_STEPS, IN_STEPS)
+
+#define LOGERR(ctx, errno, ...) ly_log(ctx, LY_LLERR, errno, __VA_ARGS__)
+#define LOGWRN(ctx, ...) ly_log(ctx, LY_LLWRN, 0, __VA_ARGS__)
+#define LOGVRB(...) ly_log(NULL, LY_LLVRB, 0, __VA_ARGS__)
+
+#ifdef NDEBUG
+# define LOGDBG(dbg_group, ...)
+#else
+void ly_log_dbg(uint32_t group, const char *format, ...);
+# define LOGDBG(dbg_group, ...) ly_log_dbg(dbg_group, __VA_ARGS__);
+#endif
+
+/**
+ * Simple EMEM message, it can be safely stored in ::ly_err_item structures without problems when freeing.
+ */
+#define LY_EMEM_MSG "Memory allocation failed."
+
+#ifdef LOGMEM
+/* overwrite shadow definition from tree_edit.h */
+#undef LOGMEM
+#endif
+#define LOGMEM(CTX) LOGERR(CTX, LY_EMEM, "Memory allocation failed (%s()).", __func__)
+
+#define LOGINT(CTX) LOGERR(CTX, LY_EINT, "Internal error (%s:%d).", __FILE__, __LINE__)
+#define LOGARG(CTX, ARG) LOGERR(CTX, LY_EINVAL, "Invalid argument %s (%s()).", #ARG, __func__)
+#define LOGVAL(CTX, ...) ly_vlog(CTX, NULL, __VA_ARGS__)
+#define LOGVAL_APPTAG(CTX, APPTAG, ...) ly_vlog(CTX, APPTAG, __VA_ARGS__)
+#define LOGVAL_LINE(CTX, LINE, ...) \
+ ly_log_location(NULL, NULL, NULL, NULL, LINE); \
+ ly_vlog(CTX, NULL, __VA_ARGS__)
+
+/**
+ * @brief Print Validation error from struct ly_err_item.
+ *
+ * String ::ly_err_item.msg cannot be used directly because it may contain the % character,
+ * which is incorrectly interpreted in this situation as a conversion specification.
+ *
+ * @param[in] CTX libyang context to store the error record. If not provided, the error is just printed.
+ * @param[in] ERRITEM pointer to ly_err_item that contains an error message.
+ */
+#define LOGVAL_ERRITEM(CTX, ERRITEM) ly_vlog(CTX, ERRITEM->apptag, ERRITEM->vecode, "%s", ERRITEM->msg)
+
+#define LOGMEM_RET(CTX) LOGMEM(CTX); return LY_EMEM
+#define LOGINT_RET(CTX) LOGINT(CTX); return LY_EINT
+#define LOGARG_RET(CTX) LOGARG(CTX); return LY_EINVAL
+
+/*
+ * Common code to check return value and perform appropriate action.
+ */
+#define LY_CHECK_GOTO(COND, GOTO) if ((COND)) {goto GOTO;}
+#define LY_CHECK_ERR_GOTO(COND, ERR, GOTO) if ((COND)) {ERR; goto GOTO;}
+#define LY_CHECK_RET1(RETVAL) {LY_ERR ret__ = RETVAL;if (ret__ != LY_SUCCESS) {return ret__;}}
+#define LY_CHECK_RET2(COND, RETVAL) if ((COND)) {return RETVAL;}
+#define LY_CHECK_RET(...) GETMACRO2(__VA_ARGS__, LY_CHECK_RET2, LY_CHECK_RET1, DUMMY)(__VA_ARGS__)
+#define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if ((COND)) {ERR; return RETVAL;}
+
+#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!(ARG)) {LOGARG(CTX, ARG);goto GOTO;}
+#define LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO) LY_CHECK_ARG_GOTO1(CTX, ARG1, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG2, GOTO)
+#define LY_CHECK_ARG_GOTO3(CTX, ARG1, ARG2, ARG3, GOTO) LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG3, GOTO)
+#define LY_CHECK_ARG_GOTO4(CTX, ARG1, ARG2, ARG3, ARG4, GOTO) LY_CHECK_ARG_GOTO3(CTX, ARG1, ARG2, ARG3, GOTO);\
+ LY_CHECK_ARG_GOTO1(CTX, ARG4, GOTO)
+#define LY_CHECK_ARG_GOTO(CTX, ...) GETMACRO5(__VA_ARGS__, LY_CHECK_ARG_GOTO4, LY_CHECK_ARG_GOTO3, LY_CHECK_ARG_GOTO2, \
+ LY_CHECK_ARG_GOTO1)(CTX, __VA_ARGS__)
+
+#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!(ARG)) {LOGARG(CTX, ARG);return RETVAL;}
+#define LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL) LY_CHECK_ARG_RET1(CTX, ARG1, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG2, RETVAL)
+#define LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL) LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG3, RETVAL)
+#define LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL) LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL);\
+ LY_CHECK_ARG_RET1(CTX, ARG4, RETVAL)
+#define LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL) LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL);\
+ LY_CHECK_ARG_RET1(CTX, ARG5, RETVAL)
+#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO6(__VA_ARGS__, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, \
+ LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
+
+#define LY_CHECK_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__)
+
+/* count sequence size for LY_VCODE_INCHILDSTMT validation error code */
+size_t LY_VCODE_INSTREXP_len(const char *str);
+/* default maximum characters to print in LY_VCODE_INCHILDSTMT */
+#define LY_VCODE_INSTREXP_MAXLEN 20
+
+#define LY_VCODE_INCHAR LYVE_SYNTAX, "Invalid character 0x%x."
+#define LY_VCODE_INSTREXP LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected %s."
+#define LY_VCODE_EOF LYVE_SYNTAX, "Unexpected end-of-input."
+#define LY_VCODE_NTERM LYVE_SYNTAX, "%s not terminated."
+#define LY_VCODE_NSUPP LYVE_SYNTAX, "%s not supported."
+#define LY_VCODE_MOD_SUBOMD LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\"."
+#define LY_VCODE_TRAILING_MOD LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after module, expected end-of-input."
+#define LY_VCODE_TRAILING_SUBMOD LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after submodule, expected end-of-input."
+
+#define LY_VCODE_INVAL_MINMAX LYVE_SEMANTICS, "Invalid combination of min-elements and max-elements: min value %u is bigger than the max value %u."
+#define LY_VCODE_NAME_COL LYVE_SEMANTICS, "Name collision between %s of name \"%s\"."
+#define LY_VCODE_NAME2_COL LYVE_SEMANTICS, "Name collision between %s and %s of name \"%s\"."
+
+#define LY_VCODE_INSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\"."
+#define LY_VCODE_INCHILDSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\"."
+#define LY_VCODE_INCHILDSTMT2 LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\" - the statement is allowed only in YANG 1.1 modules."
+#define LY_VCODE_INCHILDSTMSCOMB LYVE_SYNTAX_YANG, "Invalid combination of keywords \"%s\" and \"%s\" as substatements of \"%s\"."
+#define LY_VCODE_DUPSTMT LYVE_SYNTAX_YANG, "Duplicate keyword \"%s\"."
+#define LY_VCODE_DUPIDENT LYVE_SYNTAX_YANG, "Duplicate identifier \"%s\" of %s statement."
+#define LY_VCODE_DUPIDENT2 LYVE_SYNTAX_YANG, "Duplicate identifier \"%s\" of %s statement - %s."
+#define LY_VCODE_INVAL LYVE_SYNTAX_YANG, "Invalid value \"%.*s\" of \"%s\"."
+#define LY_VCODE_MISSTMT LYVE_SYNTAX_YANG, "Missing mandatory keyword \"%s\" as a child of \"%s\"."
+#define LY_VCODE_MISSCHILDSTMT LYVE_SYNTAX_YANG, "Missing %s substatement for %s%s."
+#define LY_VCODE_INORD LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", it cannot appear after \"%s\"."
+#define LY_VCODE_OOB LYVE_SYNTAX_YANG, "Value \"%.*s\" is out of \"%s\" bounds."
+#define LY_VCODE_INDEV LYVE_SYNTAX_YANG, "Deviate \"%s\" does not support keyword \"%s\"."
+#define LY_VCODE_INREGEXP LYVE_SYNTAX_YANG, "Regular expression \"%s\" is not valid (\"%s\": %s)."
+
+#define LY_VCODE_INSUBELEM2 LYVE_SYNTAX_YIN, "Invalid sub-elemnt \"%s\" of \"%s\" element - this sub-element is allowed only in modules with version 1.1 or newer."
+#define LY_VCODE_INVAL_YIN LYVE_SYNTAX_YIN, "Invalid value \"%s\" of \"%s\" attribute in \"%s\" element."
+#define LY_VCODE_UNEXP_SUBELEM LYVE_SYNTAX_YIN, "Unexpected sub-element \"%.*s\" of \"%s\" element."
+#define LY_VCODE_INDEV_YIN LYVE_SYNTAX_YIN, "Deviate of this type doesn't allow \"%s\" as it's sub-element."
+#define LY_VCODE_INORDER_YIN LYVE_SYNTAX_YIN, "Invalid order of %s\'s sub-elements \"%s\" can't appear after \"%s\"."
+#define LY_VCODE_OOB_YIN LYVE_SYNTAX_YIN, "Value \"%s\" of \"%s\" attribute in \"%s\" element is out of bounds."
+#define LY_VCODE_INCHILDSTMSCOMB_YIN LYVE_SYNTAX_YIN, "Invalid combination of sub-elemnts \"%s\" and \"%s\" in \"%s\" element."
+#define LY_VCODE_DUP_ATTR LYVE_SYNTAX_YIN, "Duplicit definition of \"%s\" attribute in \"%s\" element."
+#define LY_VCODE_UNEXP_ATTR LYVE_SYNTAX_YIN, "Unexpected attribute \"%.*s\" of \"%s\" element."
+#define LY_VCODE_MAND_SUBELEM LYVE_SYNTAX_YIN, "Missing mandatory sub-element \"%s\" of \"%s\" element."
+#define LY_VCODE_FIRT_SUBELEM LYVE_SYNTAX_YIN, "Sub-element \"%s\" of \"%s\" element must be defined as it's first sub-element."
+#define LY_VCODE_SUBELEM_REDEF LYVE_SYNTAX_YIN, "Redefinition of \"%s\" sub-element in \"%s\" element."
+
+#define LY_VCODE_XP_EOE LYVE_XPATH, "Unterminated string delimited with %c (%.15s)."
+#define LY_VCODE_XP_INEXPR LYVE_XPATH, "Invalid character '%c'[%u] of expression \'%s\'."
+#define LY_VCODE_XP_EOF LYVE_XPATH, "Unexpected XPath expression end."
+#define LY_VCODE_XP_INTOK LYVE_XPATH, "Unexpected XPath token \"%s\" (\"%.15s\")."
+#define LY_VCODE_XP_INTOK2 LYVE_XPATH, "Unexpected XPath token \"%s\" (\"%.15s\"), expected \"%s\"."
+#define LY_VCODE_XP_INFUNC LYVE_XPATH, "Unknown XPath function \"%.*s\"."
+#define LY_VCODE_XP_INARGCOUNT LYVE_XPATH, "Invalid number of arguments (%d) for the XPath function %.*s."
+#define LY_VCODE_XP_INARGTYPE LYVE_XPATH, "Wrong type of argument #%d (%s) for the XPath function %s."
+#define LY_VCODE_XP_INCTX LYVE_XPATH, "Invalid context type %s in %s."
+#define LY_VCODE_XP_INOP_1 LYVE_XPATH, "Cannot apply XPath operation %s on %s."
+#define LY_VCODE_XP_INOP_2 LYVE_XPATH, "Cannot apply XPath operation %s on %s and %s."
+#define LY_VCODE_XP_INMOD LYVE_XPATH, "Unknown/non-implemented module \"%.*s\"."
+#define LY_VCODE_XP_DEPTH LYVE_XPATH, "The maximum nesting of expressions has been exceeded."
+
+#define LY_VCODE_DEV_NOT_PRESENT LYVE_REFERENCE, "Invalid deviation %s \"%s\" property \"%s\" which is not present."
+
+#define LY_VCODE_NOWHEN LYVE_DATA, "When condition \"%s\" not satisfied."
+#define LY_VCODE_NOMAND LYVE_DATA, "Mandatory node \"%s\" instance does not exist."
+#define LY_VCODE_DUP LYVE_DATA, "Duplicate instance of \"%s\"."
+#define LY_VCODE_DUPCASE LYVE_DATA, "Data for both cases \"%s\" and \"%s\" exist."
+#define LY_VCODE_UNEXPNODE LYVE_DATA, "Unexpected data %s node \"%s\" found."
+#define LY_VCODE_NOKEY LYVE_DATA, "List instance is missing its key \"%s\"."
+
+#define LY_ERRMSG_NOPATTERN /* LYVE_DATA */ "Unsatisfied pattern - \"%.*s\" does not conform to %s\"%s\"."
+#define LY_ERRMSG_NOLENGTH /* LYVE_DATA */ "Unsatisfied length - string \"%.*s\" length is not allowed."
+#define LY_ERRMSG_NORANGE /* LYVE_DATA */ "Unsatisfied range - value \"%.*s\" is out of the allowed range."
+
+/* RFC 7950 section 15 errors */
+#define LY_VCODE_NOUNIQ LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
+#define LY_VCODE_NOMAX LYVE_DATA, "Too many \"%s\" instances."
+#define LY_VCODE_NOMIN LYVE_DATA, "Too few \"%s\" instances."
+#define LY_VCODE_NOMUST LYVE_DATA, "Must condition \"%s\" not satisfied."
+#define LY_VCODE_NOMAND_CHOIC LYVE_DATA, "Mandatory choice \"%s\" data do not exist."
+
+/* RFC 7950 section 15 error messages used in type plugin validation callbacks */
+#define LY_ERRMSG_NOLREF_VAL /* LYVE_DATA */ "Invalid leafref value \"%s\" - no target instance \"%s\" with the same value."
+#define LY_ERRMSG_NOINST /* LYVE_DATA */ "Invalid instance-identifier \"%s\" value - required instance not found."
+
+/******************************************************************************
+ * Context
+ *****************************************************************************/
+
+/**
+ * @brief Context of the YANG schemas
+ */
+struct ly_ctx {
+ struct dict_table dict; /**< dictionary to effectively store strings used in the context related structures */
+ struct ly_set search_paths; /**< set of directories where to search for schema's imports/includes */
+ struct ly_set list; /**< set of loaded YANG schemas */
+ ly_module_imp_clb imp_clb; /**< optional callback for retrieving missing included or imported models */
+ void *imp_clb_data; /**< optional private data for ::ly_ctx.imp_clb */
+ struct lys_glob_unres unres; /**< global unres, should be empty unless there are modules prepared for
+ compilation if ::LY_CTX_EXPLICIT_COMPILE flag is set */
+ uint16_t change_count; /**< count of changes of the context, on some changes it could be incremented
+ more times */
+ uint16_t flags; /**< context settings, see @ref contextoptions */
+
+ ly_ext_data_clb ext_clb; /**< optional callback for providing extension-specific run-time data for extensions */
+ void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */
+ pthread_key_t errlist_key; /**< key for the thread-specific list of errors related to the context */
+ pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */
+};
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its name.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @param[in] name_len Optional length of the @p name. If zero, NULL-terminated name is expected.
+ * @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
+ * implemented module of the given name.
+ */
+struct lys_module *ly_ctx_get_module_implemented2(const struct ly_ctx *ctx, const char *name, size_t name_len);
+
+/******************************************************************************
+ * Generic useful functions.
+ *****************************************************************************/
+
+/**
+ * @brief Insert string into dictionary.
+ *
+ * @param[in] CTX libyang context.
+ * @param[in] STRING string to store.
+ * @param[in] LEN length of the string in WORD to store.
+ * @param[in,out] DYNAMIC Set to 1 if @p STRING is dynamically allocated, 0 otherwise.
+ * If set to 1, zerocopy version of lydict_insert is used.
+ * @param[out] TARGET pointer is set to @p STRING value stored in the dictionary.
+ */
+#define INSERT_STRING_RET(CTX, STRING, LEN, DYNAMIC, TARGET) \
+ if (DYNAMIC) { \
+ LY_CHECK_RET(lydict_insert_zc(CTX, (char *)(STRING), &(TARGET))); \
+ } else { \
+ LY_CHECK_RET(lydict_insert(CTX, LEN ? (STRING) : "", LEN, &(TARGET))); \
+ } \
+ DYNAMIC = 0
+
+/**
+ * @brief Wrapper for realloc() call. The only difference is that if it fails to
+ * allocate the requested memory, the original memory is freed as well.
+ *
+ * @param[in] ptr Memory to reallocate.
+ * @param[in] size New size of the memory block.
+ *
+ * @return Pointer to the new memory, NULL on error.
+ */
+void *ly_realloc(void *ptr, size_t size);
+
+/**
+ * @brief Just like strchr() function except limit the number of examined characters.
+ *
+ * @param[in] s String to search in.
+ * @param[in] c Character to search for.
+ * @param[in] len Limit the search to this number of characters in @p s.
+ * @return Pointer to first @p c occurrence in @p s, NULL if not found in first @p len characters.
+ */
+char *ly_strnchr(const char *s, int c, size_t len);
+
+/**
+ * @brief Compare NULL-terminated @p refstr with @p str_len bytes from @p str.
+ *
+ * @param[in] refstr NULL-terminated string which must match @p str_len bytes from @p str followed by NULL-byte.
+ * @param[in] str String to compare.
+ * @param[in] str_len Number of bytes to take into comparison from @p str.
+ * @return An integer less than, equal to, or greater than zero if @p refstr matches,
+ * respectively, to be less than, to match, or be greater than @p str.
+ */
+int ly_strncmp(const char *refstr, const char *str, size_t str_len);
+
+/**
+ * @brief Similar functionality to strtoul() except number length in the string
+ * must be specified and the whole number must be parsed for success.
+ *
+ * @param[in] nptr Number string.
+ * @param[in] len Number string length starting at @p nptr.
+ * @param[out] ret Parsed number.
+ * @return LY_EDENIED on overflow.
+ * @return LY_EVALID on encountering a non-digit character.
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR ly_strntou8(const char *nptr, size_t len, uint8_t *ret);
+
+/**
+ * @brief Get all possible value prefixes from an YANG value by iteratively returning specific substrings.
+ *
+ * The function looks for possible prefix ending in a colon at the beginning of @p str_begin.
+ * If @p str_begin does not contain the prefix at the beginning, then either:
+ * 1. Returns the entire input string if the input string does not contain the prefix at all.
+ * 2. Returns a substring before the prefix. The substring is terminated by any character
+ * that is not allowed to be present in prefix (except colon).
+ *
+ * Examples of inputs and outputs are shown in the table below.
+ * Output string @p str_next is used in the next iteration as input parameter @p str_begin.
+ @verbatim
+ | INPUT | OUTPUT |
+ | | iteration 1 | iteration 2 | iteration 3 |
+ |------------------------------ |------------------|------------------|-----------------|
+ | /namespace_prefix:some_string | / | namespace_prefix | some_string |
+ | namespace_prefix:some_string | namespace_prefix | some_string | NULL |
+ | /some_string | /some_string | NULL | NULL |
+ @endverbatim
+ *
+ *
+ * @param[in] str_begin Begin of the input string.
+ * @param[in] str_end Length of the @p str_begin. If set to NULL then the @p str_begin must be NULL-terminated string.
+ * @param[out] len Number of bytes (length) of the found prefix/substring starting at @p str_begin.
+ * @param[out] is_prefix Type of substring found. Set to True for prefix, otherwise False.
+ * @param[out] str_next Remaining string starting after prefix/substring and ending with @p str_end.
+ * If the @p is_prefix is set to True then the colon character is skipped.
+ * If no string remains, it is set to NULL.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix, const char **str_next);
+
+/**
+ * @brief Wrapper around strlen() to handle NULL strings.
+ */
+#define ly_strlen(STR) (STR ? strlen(STR) : 0)
+
+/**
+ * @brief Compile-time strlen() for string constants.
+ *
+ * Use to avoid magic numbers usage
+ */
+#define ly_strlen_const(STR) (sizeof STR - 1)
+
+/**
+ * @brief Macro to simply put couple of string length and the string as
+ * printf's arguments for %.*s. Use only with constant strings.
+ */
+#define LY_PRI_LENSTR(STR) (int)ly_strlen_const(STR), STR
+
+#define ly_sizeofarray(ARRAY) (sizeof ARRAY / sizeof *ARRAY)
+
+/**
+ * @brief Check for overflow during the addition of two unsigned integers.
+ */
+#define LY_OVERFLOW_ADD(MAX, X, Y) (X > MAX - Y)
+
+/**
+ * @brief Check for overflow during the multiplication of two unsigned integers.
+ */
+#define LY_OVERFLOW_MUL(MAX, X, Y) (X > MAX / Y)
+
+/*
+ * Numerical bases for use in functions like strtoll() instead of magic numbers
+ */
+#define LY_BASE_DEC 10 /**< Decimal numeral base */
+#define LY_BASE_OCT 8 /**< Octal numeral base */
+#define LY_BASE_HEX 16 /**< Hexadecimal numeral base */
+
+/**
+ * Maximal length of (needed storage for) a number encoded as a string.
+ *
+ * Applies not only for standard numbers, but also for YANG's decimal64.
+ */
+#define LY_NUMBER_MAXLEN 22
+
+/**
+ * @brief Get UTF8 code point of the next character in the input string.
+ *
+ * @param[in,out] input Input string to process, updated according to the processed/read data.
+ * @param[out] utf8_char UTF8 code point of the next character.
+ * @param[out] bytes_read Number of bytes used to encode the read utf8_char.
+ * @return LY_ERR value
+ */
+LY_ERR ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read);
+
+/**
+ * Store UTF-8 character specified as 4byte integer into the dst buffer.
+ *
+ * UTF-8 mapping:
+ * 00000000 -- 0000007F: 0xxxxxxx
+ * 00000080 -- 000007FF: 110xxxxx 10xxxxxx
+ * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
+ * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *
+ * Includes checking for valid characters (following RFC 7950, sec 9.4)
+ *
+ * @param[in, out] dst Destination buffer to store the UTF-8 character, must provide enough space (up to 4 bytes) for storing the UTF-8 character.
+ * @param[in] value 32b value of the UTF-8 character to store.
+ * @param[out] bytes_written Number of bytes written into @p dst (size of the written UTF-8 character).
+ * @return LY_SUCCESS on success
+ * @return LY_EINVAL in case of invalid UTF-8 @p value to store.
+ */
+LY_ERR ly_pututf8(char *dst, uint32_t value, size_t *bytes_written);
+
+/**
+ * @brief Get number of characters in the @p str, taking multibyte characters into account.
+ * @param[in] str String to examine.
+ * @param[in] bytes Number of valid bytes that are supposed to be taken into account in @p str.
+ * This parameter is useful mainly for non NULL-terminated strings. In case of NULL-terminated
+ * string, strlen() can be used.
+ * @return Number of characters in (possibly) multibyte characters string.
+ */
+size_t ly_utf8len(const char *str, size_t bytes);
+
+/**
+ * @brief Parse signed integer with possible limitation.
+ * @param[in] val_str String value containing signed integer, note that
+ * nothing else than whitespaces are expected after the value itself.
+ * @param[in] val_len Length of the @p val_str string.
+ * @param[in] min Limitation for the value which must not be lower than min.
+ * @param[in] max Limitation for the value which must not be higher than max.
+ * @param[in] base Numeric base for parsing:
+ * 0 - to accept decimal, octal, hexadecimal (e.g. in default value)
+ * 10 - to accept only decimal (e.g. data instance value)
+ * @param[out] ret Resulting value.
+ * @return LY_ERR value:
+ * LY_EDENIED - the value breaks the limits,
+ * LY_EVALID - string contains invalid value,
+ * LY_SUCCESS - successful parsing.
+ */
+LY_ERR ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t max, int base, int64_t *ret);
+
+/**
+ * @brief Parse unsigned integer with possible limitation.
+ * @param[in] val_str String value containing unsigned integer, note that
+ * nothing else than whitespaces are expected after the value itself.
+ * @param[in] val_len Length of the @p val_str string.
+ * @param[in] max Limitation for the value which must not be higher than max.
+ * @param[in] base Numeric base for parsing:
+ * 0 - to accept decimal, octal, hexadecimal (e.g. in default value)
+ * 10 - to accept only decimal (e.g. data instance value)
+ * @param[out] ret Resulting value.
+ * @return LY_ERR value:
+ * LY_EDENIED - the value breaks the limits,
+ * LY_EVALID - string contains invalid value,
+ * LY_SUCCESS - successful parsing.
+ */
+LY_ERR ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base, uint64_t *ret);
+
+/**
+ * @brief Parse a node-identifier.
+ *
+ * node-identifier = [prefix ":"] identifier
+ *
+ * @param[in, out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @param[out] prefix Node's prefix, NULL if there is not any.
+ * @param[out] prefix_len Length of the node's prefix, 0 if there is not any.
+ * @param[out] name Node's name.
+ * @param[out] name_len Length of the node's name.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the id.
+ */
+LY_ERR ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
+
+/**
+ * @brief parse instance-identifier's predicate, supports key-predicate, leaf-list-predicate and pos rules from YANG ABNF Grammar.
+ *
+ * @param[in, out] pred Predicate string (including the leading '[') to parse. The string is updated according to what was parsed
+ * (even for error case, so it can be used to determine which substring caused failure).
+ * @param[in] limit Limiting length of the @p pred. Function expects NULL terminated string which is not overread.
+ * The limit value is not checked with each character, so it can be overread and the failure is detected later.
+ * @param[in] format Input format of the data containing the @p pred.
+ * @param[out] prefix Start of the node-identifier's prefix if any, NULL in case of pos or leaf-list-predicate rules.
+ * @param[out] prefix_len Length of the parsed @p prefix.
+ * @param[out] id Start of the node-identifier's identifier string, NULL in case of pos rule, "." in case of leaf-list-predicate rule.
+ * @param[out] id_len Length of the parsed @p id.
+ * @param[out] value Start of the quoted-string (without quotation marks), not NULL in case of success.
+ * @param[out] value_len Length of the parsed @p value.
+ * @param[out] errmsg Error message string in case of error.
+ * @return LY_SUCCESS in case a complete predicate was parsed.
+ * @return LY_EVALID in case of invalid predicate form.
+ * @return LY_EINVAL in case of reaching @p limit when parsing @p pred.
+ */
+LY_ERR ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
+ const char **prefix, size_t *prefix_len, const char **id, size_t *id_len,
+ const char **value, size_t *value_len, const char **errmsg);
+
+/**
+ * @brief mmap(2) wrapper to map input files into memory to unify parsing.
+ *
+ * The address space is allocate only for reading.
+ *
+ * @param[in] ctx libyang context for logging
+ * @param[in] fd Open file descriptor of a file to map.
+ * @param[out] length Allocated size.
+ * @param[out] addr Address where the file is mapped.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr);
+
+/**
+ * @brief munmap(2) wrapper to free the memory mapped by ::ly_mmap()
+ *
+ * @param[in] addr Address where the input file is mapped.
+ * @param[in] length Allocated size of the address space.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_munmap(void *addr, size_t length);
+
+/**
+ * @brief Concatenate formating string to the @p dest.
+ *
+ * @param[in, out] dest String to be concatenated by @p format.
+ * Note that the input string can be reallocated during concatenation.
+ * @param[in] format Formating string (as for printf) which is supposed to be added after @p dest.
+ * @return LY_SUCCESS or LY_EMEM.
+ */
+LY_ERR ly_strcat(char **dest, const char *format, ...);
+
+#ifndef _WIN32
+# define PATH_SEPARATOR ":"
+#else
+# define PATH_SEPARATOR ";"
+#endif
+
+#endif /* LY_COMMON_H_ */
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..af3d1f0
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,74 @@
+/**
+ * @file config.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Various variables provided by cmake and compile time options.
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_CONFIG_H_
+#define LY_CONFIG_H_
+
+#ifdef _WIN32
+/* headers are broken on Windows, which means that some of them simply *have* to come first */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#endif
+
+/** size of fixed_mem in lyd_value, minimum is 8 (B) */
+#define LYD_VALUE_FIXED_MEM_SIZE @LYD_VALUE_SIZE@
+
+/** plugins */
+#define LYPLG_SUFFIX "@CMAKE_SHARED_MODULE_SUFFIX@"
+#define LYPLG_SUFFIX_LEN (sizeof LYPLG_SUFFIX - 1)
+#define LYPLG_TYPE_DIR "@PLUGINS_DIR_TYPES@"
+#define LYPLG_EXT_DIR "@PLUGINS_DIR_EXTENSIONS@"
+
+/** atomic compiler operations, to be able to use uint32_t */
+#ifndef _WIN32
+# define LY_ATOMIC_INC_BARRIER(var) __sync_fetch_and_add(&(var), 1)
+# define LY_ATOMIC_DEC_BARRIER(var) __sync_fetch_and_sub(&(var), 1)
+#else
+# include <windows.h>
+# define LY_ATOMIC_INC_BARRIER(var) InterlockedExchangeAdd(&(var), 1)
+# define LY_ATOMIC_DEC_BARRIER(var) InterlockedExchangeAdd(&(var), -1)
+#endif
+
+/** printf compiler attribute */
+#ifdef __GNUC__
+# define _FORMAT_PRINTF(FORM, ARGS) __attribute__((format (printf, FORM, ARGS)))
+#else
+# define _FORMAT_PRINTF(FORM, ARGS)
+#endif
+
+/** Exporting symbols to a shared library and importing back afterwards
+ *
+ * - use LIBYANG_API_DECL to mark a declaration in the public header
+ * - use LIBYANG_API_DEF to mark a definition (in the source code for the actual implementaiton)
+ * */
+#ifdef _MSC_VER
+# ifndef STATIC
+# define LIBYANG_API_DEF __declspec(dllexport)
+# ifdef LIBYANG_BUILD
+# define LIBYANG_API_DECL __declspec(dllexport)
+# else
+# define LIBYANG_API_DECL __declspec(dllimport)
+# endif
+# endif
+#else
+/*
+ * If the compiler supports attribute to mark objects as hidden, mark all
+ * objects as hidden and export only objects explicitly marked to be part of
+ * the public API.
+ */
+# define LIBYANG_API_DEF __attribute__((visibility("default")))
+# define LIBYANG_API_DECL
+#endif
+
+#endif /* LY_CONFIG_H_ */
diff --git a/src/context.c b/src/context.c
new file mode 100644
index 0000000..47e63d4
--- /dev/null
+++ b/src/context.c
@@ -0,0 +1,1277 @@
+/**
+ * @file context.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Context implementations
+ *
+ * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* asprintf, strdup */
+#if defined (__NetBSD__) || defined (__OpenBSD__)
+/* realpath */
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+#endif
+
+#include "context.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "compat.h"
+#include "hash_table.h"
+#include "in.h"
+#include "parser_data.h"
+#include "plugins_internal.h"
+#include "plugins_types.h"
+#include "schema_compile.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_free.h"
+#include "tree_schema_internal.h"
+
+#include "../models/ietf-datastores@2018-02-14.h"
+#include "../models/ietf-inet-types@2013-07-15.h"
+#include "../models/ietf-yang-library@2019-01-04.h"
+#include "../models/ietf-yang-metadata@2016-08-05.h"
+#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"
+#define IETF_YANG_LIB_REV "2019-01-04"
+
+static struct internal_modules_s {
+ const char *name;
+ const char *revision;
+ const char *data;
+ ly_bool implemented;
+ 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},
+ {"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},
+ {"ietf-yang-structure-ext", "2020-06-17", (const char *)ietf_yang_structure_ext_2020_06_17_yang, 0, LYS_IN_YANG},
+ /* ietf-datastores and ietf-yang-library must be right here at the end of the list! */
+ {"ietf-datastores", "2018-02-14", (const char *)ietf_datastores_2018_02_14_yang, 1, LYS_IN_YANG},
+ {"ietf-yang-library", IETF_YANG_LIB_REV, (const char *)ietf_yang_library_2019_01_04_yang, 1, LYS_IN_YANG}
+};
+
+#define LY_INTERNAL_MODS_COUNT sizeof(internal_modules) / sizeof(struct internal_modules_s)
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir)
+{
+ struct stat st;
+ char *new_dir = NULL;
+
+ LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
+
+ if (search_dir) {
+ new_dir = realpath(search_dir, NULL);
+ LY_CHECK_ERR_RET(!new_dir,
+ LOGERR(ctx, LY_ESYS, "Unable to use search directory \"%s\" (%s).", search_dir, strerror(errno)),
+ LY_EINVAL);
+ if (strcmp(search_dir, new_dir)) {
+ LOGVRB("Search directory string \"%s\" canonized to \"%s\".", search_dir, new_dir);
+ }
+ LY_CHECK_ERR_RET(access(new_dir, R_OK | X_OK),
+ LOGERR(ctx, LY_ESYS, "Unable to fully access search directory \"%s\" (%s).", new_dir, strerror(errno)); free(new_dir),
+ LY_EINVAL);
+ LY_CHECK_ERR_RET(stat(new_dir, &st),
+ LOGERR(ctx, LY_ESYS, "stat() failed for \"%s\" (%s).", new_dir, strerror(errno)); free(new_dir),
+ LY_ESYS);
+ LY_CHECK_ERR_RET(!S_ISDIR(st.st_mode),
+ LOGERR(ctx, LY_ESYS, "Given search directory \"%s\" is not a directory.", new_dir); free(new_dir),
+ LY_EINVAL);
+ /* avoid path duplication */
+ for (uint32_t u = 0; u < ctx->search_paths.count; ++u) {
+ if (!strcmp(new_dir, ctx->search_paths.objs[u])) {
+ free(new_dir);
+ return LY_EEXIST;
+ }
+ }
+ if (ly_set_add(&ctx->search_paths, new_dir, 1, NULL)) {
+ free(new_dir);
+ return LY_EMEM;
+ }
+
+ /* new searchdir - possibly more latest revision available */
+ ly_ctx_reset_latests(ctx);
+
+ return LY_SUCCESS;
+ } else {
+ /* consider that no change is not actually an error */
+ return LY_SUCCESS;
+ }
+}
+
+LIBYANG_API_DEF const char * const *
+ly_ctx_get_searchdirs(const struct ly_ctx *ctx)
+{
+#define LY_CTX_SEARCHDIRS_SIZE_STEP 8
+ void **new;
+
+ LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+ if (ctx->search_paths.count == ctx->search_paths.size) {
+ /* not enough space for terminating NULL byte */
+ new = realloc(((struct ly_ctx *)ctx)->search_paths.objs,
+ (ctx->search_paths.size + LY_CTX_SEARCHDIRS_SIZE_STEP) * sizeof *ctx->search_paths.objs);
+ LY_CHECK_ERR_RET(!new, LOGMEM(NULL), NULL);
+ ((struct ly_ctx *)ctx)->search_paths.size += LY_CTX_SEARCHDIRS_SIZE_STEP;
+ ((struct ly_ctx *)ctx)->search_paths.objs = new;
+ }
+ /* set terminating NULL byte to the strings list */
+ ctx->search_paths.objs[ctx->search_paths.count] = NULL;
+
+ return (const char * const *)ctx->search_paths.objs;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_unset_searchdir(struct ly_ctx *ctx, const char *value)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
+
+ if (!ctx->search_paths.count) {
+ return LY_SUCCESS;
+ }
+
+ if (value) {
+ /* remove specific search directory */
+ uint32_t index;
+
+ for (index = 0; index < ctx->search_paths.count; ++index) {
+ if (!strcmp(value, ctx->search_paths.objs[index])) {
+ break;
+ }
+ }
+ if (index == ctx->search_paths.count) {
+ LOGARG(ctx, value);
+ return LY_EINVAL;
+ } else {
+ return ly_set_rm_index(&ctx->search_paths, index, free);
+ }
+ } else {
+ /* remove them all */
+ ly_set_erase(&ctx->search_paths, free);
+ memset(&ctx->search_paths, 0, sizeof ctx->search_paths);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_unset_searchdir_last(struct ly_ctx *ctx, uint32_t count)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
+
+ for ( ; count > 0 && ctx->search_paths.count; --count) {
+ LY_CHECK_RET(ly_set_rm_index(&ctx->search_paths, ctx->search_paths.count - 1, free))
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision, const char **features)
+{
+ struct lys_module *mod = NULL;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+
+ /* load and parse */
+ ret = lys_parse_load(ctx, name, revision, &ctx->unres.creating, &mod);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* implement */
+ ret = _lys_set_implemented(mod, features, &ctx->unres);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!(ctx->flags & LY_CTX_EXPLICIT_COMPILE)) {
+ /* create dep set for the module and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(ctx, &ctx->unres.dep_sets, mod), cleanup);
+
+ /* (re)compile the whole dep set (other dep sets will have no modules marked for compilation) */
+ LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup);
+
+ /* unres resolved */
+ lys_unres_glob_erase(&ctx->unres);
+ }
+
+cleanup:
+ if (ret) {
+ lys_unres_glob_revert(ctx, &ctx->unres);
+ lys_unres_glob_erase(&ctx->unres);
+ mod = NULL;
+ }
+ return mod;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
+{
+ struct ly_ctx *ctx = NULL;
+ struct lys_module *module;
+ char *search_dir_list, *sep, *dir;
+ const char **imp_f, *all_f[] = {"*", NULL};
+ uint32_t i;
+ struct ly_in *in = NULL;
+ LY_ERR rc = LY_SUCCESS;
+ struct lys_glob_unres unres = {0};
+
+ LY_CHECK_ARG_RET(NULL, new_ctx, LY_EINVAL);
+
+ ctx = calloc(1, sizeof *ctx);
+ LY_CHECK_ERR_GOTO(!ctx, LOGMEM(NULL); rc = LY_EMEM, cleanup);
+
+ /* dictionary */
+ lydict_init(&ctx->dict);
+
+ /* plugins */
+ LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup);
+
+ /* initialize thread-specific keys */
+ while ((pthread_key_create(&ctx->errlist_key, ly_err_free)) == EAGAIN) {}
+
+ /* init LYB hash lock */
+ pthread_mutex_init(&ctx->lyb_hash_lock, NULL);
+
+ /* models list */
+ ctx->flags = options;
+ if (search_dir) {
+ search_dir_list = strdup(search_dir);
+ LY_CHECK_ERR_GOTO(!search_dir_list, LOGMEM(NULL); rc = LY_EMEM, cleanup);
+
+ for (dir = search_dir_list; (sep = strchr(dir, PATH_SEPARATOR[0])) != NULL && rc == LY_SUCCESS; dir = sep + 1) {
+ *sep = 0;
+ rc = ly_ctx_set_searchdir(ctx, dir);
+ if (rc == LY_EEXIST) {
+ /* ignore duplication */
+ rc = LY_SUCCESS;
+ }
+ }
+ if (*dir && (rc == LY_SUCCESS)) {
+ rc = ly_ctx_set_searchdir(ctx, dir);
+ if (rc == LY_EEXIST) {
+ /* ignore duplication */
+ rc = LY_SUCCESS;
+ }
+ }
+ free(search_dir_list);
+
+ /* If ly_ctx_set_searchdir() failed, the error is already logged. Just exit */
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ ctx->change_count = 1;
+
+ if (!(options & LY_CTX_EXPLICIT_COMPILE)) {
+ /* use it for creating the initial context */
+ ctx->flags |= LY_CTX_EXPLICIT_COMPILE;
+ }
+
+ /* create dummy in */
+ rc = ly_in_new_memory(internal_modules[0].data, &in);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* 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);
+ 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);
+ }
+ }
+
+ if (!(options & LY_CTX_EXPLICIT_COMPILE)) {
+ /* compile now */
+ LY_CHECK_GOTO(rc = ly_ctx_compile(ctx), cleanup);
+ ctx->flags &= ~LY_CTX_EXPLICIT_COMPILE;
+ }
+
+cleanup:
+ ly_in_free(in, 0);
+ lys_unres_glob_erase(&unres);
+ if (rc) {
+ ly_ctx_destroy(ctx);
+ } else {
+ *new_ctx = ctx;
+ }
+ return rc;
+}
+
+static LY_ERR
+ly_ctx_new_yl_legacy(struct ly_ctx *ctx, const struct lyd_node *yltree)
+{
+ struct lyd_node *module, *node;
+ struct ly_set *set;
+ const char **feature_arr = NULL;
+ const char *name = NULL, *revision = NULL;
+ struct ly_set features = {0};
+ ly_bool imported = 0;
+ const struct lys_module *mod;
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t i, j;
+
+ LY_CHECK_RET(ret = lyd_find_xpath(yltree, "/ietf-yang-library:yang-library/modules-state/module", &set));
+
+ /* process the data tree */
+ for (i = 0; i < set->count; ++i) {
+ module = set->dnodes[i];
+
+ /* initiate */
+ revision = NULL;
+ name = NULL;
+ imported = 0;
+
+ LY_LIST_FOR(lyd_child(module), node) {
+ if (!strcmp(node->schema->name, "name")) {
+ name = lyd_get_value(node);
+ } else if (!strcmp(node->schema->name, "revision")) {
+ revision = lyd_get_value(node);
+ } else if (!strcmp(node->schema->name, "feature")) {
+ LY_CHECK_GOTO(ret = ly_set_add(&features, node, 0, NULL), cleanup);
+ } else if (!strcmp(node->schema->name, "conformance-type") &&
+ !strcmp(lyd_get_value(node), "import")) {
+ /* imported module - skip it, it will be loaded as a side effect
+ * of loading another module */
+ imported = 1;
+ break;
+ }
+ }
+
+ if (imported) {
+ continue;
+ }
+
+ feature_arr = malloc((features.count + 1) * sizeof *feature_arr);
+ LY_CHECK_ERR_GOTO(!feature_arr, ret = LY_EMEM, cleanup);
+
+ /* Parse features into an array of strings */
+ for (j = 0; j < features.count; ++j) {
+ feature_arr[j] = lyd_get_value(features.dnodes[j]);
+ }
+ feature_arr[features.count] = NULL;
+ ly_set_clean(&features, free);
+
+ /* use the gathered data to load the module */
+ mod = ly_ctx_load_module(ctx, name, revision, feature_arr);
+ free(feature_arr);
+ if (!mod) {
+ LOGERR(ctx, LY_EINVAL, "Unable to load module specified by yang library data.");
+ ly_set_free(set, free);
+ return LY_EINVAL;
+ }
+ }
+
+cleanup:
+ ly_set_clean(&features, free);
+ ly_set_free(set, free);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_new_ylpath(const char *search_dir, const char *path, LYD_FORMAT format, int options, struct ly_ctx **ctx)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx_yl = NULL;
+ struct lyd_node *data_yl = NULL;
+
+ LY_CHECK_ARG_RET(NULL, path, ctx, LY_EINVAL);
+
+ /* create a seperate context for the data */
+ LY_CHECK_GOTO(ret = ly_ctx_new(search_dir, 0, &ctx_yl), cleanup);
+
+ /* parse yang library data tree */
+ LY_CHECK_GOTO(ret = lyd_parse_data_path(ctx_yl, path, format, 0, LYD_VALIDATE_PRESENT, &data_yl), cleanup);
+
+ /* create the new context */
+ ret = ly_ctx_new_yldata(search_dir, data_yl, options, ctx);
+
+cleanup:
+ lyd_free_all(data_yl);
+ ly_ctx_destroy(ctx_yl);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_new_ylmem(const char *search_dir, const char *data, LYD_FORMAT format, int options, struct ly_ctx **ctx)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx_yl = NULL;
+ struct lyd_node *data_yl = NULL;
+
+ LY_CHECK_ARG_RET(NULL, data, ctx, LY_EINVAL);
+
+ /* create a seperate context for the data */
+ LY_CHECK_GOTO(ret = ly_ctx_new(search_dir, 0, &ctx_yl), cleanup);
+
+ /* parse yang library data tree */
+ LY_CHECK_GOTO(ret = lyd_parse_data_mem(ctx_yl, data, format, 0, LYD_VALIDATE_PRESENT, &data_yl), cleanup);
+
+ /* create the new context */
+ ret = ly_ctx_new_yldata(search_dir, data_yl, options, ctx);
+
+cleanup:
+ lyd_free_all(data_yl);
+ ly_ctx_destroy(ctx_yl);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_new_yldata(const char *search_dir, const struct lyd_node *tree, int options, struct ly_ctx **ctx)
+{
+ const char *name = NULL, *revision = NULL;
+ struct lyd_node *module, *node;
+ struct ly_set *set = NULL;
+ const char **feature_arr = NULL;
+ struct ly_set features = {0};
+ LY_ERR ret = LY_SUCCESS;
+ const struct lys_module *mod;
+ struct ly_ctx *ctx_new = NULL;
+ ly_bool no_expl_compile = 0;
+ uint32_t i, j;
+
+ LY_CHECK_ARG_RET(NULL, tree, ctx, LY_EINVAL);
+
+ /* create a new context */
+ if (*ctx == NULL) {
+ LY_CHECK_GOTO(ret = ly_ctx_new(search_dir, options, &ctx_new), cleanup);
+ } else {
+ ctx_new = *ctx;
+ }
+
+ /* redundant to compile modules one-by-one */
+ if (!(options & LY_CTX_EXPLICIT_COMPILE)) {
+ ctx_new->flags |= LY_CTX_EXPLICIT_COMPILE;
+ no_expl_compile = 1;
+ }
+
+ LY_CHECK_GOTO(ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set[1]/module", &set), cleanup);
+ if (set->count == 0) {
+ /* perhaps a legacy data tree? */
+ LY_CHECK_GOTO(ret = ly_ctx_new_yl_legacy(ctx_new, tree), cleanup);
+ } else {
+ /* process the data tree */
+ for (i = 0; i < set->count; ++i) {
+ module = set->dnodes[i];
+
+ /* initiate */
+ name = NULL;
+ revision = NULL;
+
+ /* iterate over data */
+ LY_LIST_FOR(lyd_child(module), node) {
+ if (!strcmp(node->schema->name, "name")) {
+ name = lyd_get_value(node);
+ } else if (!strcmp(node->schema->name, "revision")) {
+ revision = lyd_get_value(node);
+ } else if (!strcmp(node->schema->name, "feature")) {
+ LY_CHECK_GOTO(ret = ly_set_add(&features, node, 0, NULL), cleanup);
+ }
+ }
+
+ feature_arr = malloc((features.count + 1) * sizeof *feature_arr);
+ LY_CHECK_ERR_GOTO(!feature_arr, ret = LY_EMEM, cleanup);
+
+ /* parse features into an array of strings */
+ for (j = 0; j < features.count; ++j) {
+ feature_arr[j] = lyd_get_value(features.dnodes[j]);
+ }
+ feature_arr[features.count] = NULL;
+ ly_set_clean(&features, NULL);
+
+ /* use the gathered data to load the module */
+ mod = ly_ctx_load_module(ctx_new, name, revision, feature_arr);
+ free(feature_arr);
+ if (!mod) {
+ LOGERR(*ctx ? *ctx : LYD_CTX(tree), LY_EINVAL, "Unable to load module %s@%s specified by yang library data.",
+ name, revision ? revision : "<none>");
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* compile */
+ LY_CHECK_GOTO(ret = ly_ctx_compile(ctx_new), cleanup);
+
+ if (no_expl_compile) {
+ /* unset flag */
+ ctx_new->flags &= ~LY_CTX_EXPLICIT_COMPILE;
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ ly_set_erase(&features, NULL);
+ if (*ctx == NULL) {
+ *ctx = ctx_new;
+ if (ret) {
+ ly_ctx_destroy(*ctx);
+ *ctx = NULL;
+ }
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_compile(struct ly_ctx *ctx)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(NULL, ctx, LY_EINVAL);
+
+ /* create dep sets and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(ctx, &ctx->unres.dep_sets, NULL), cleanup);
+
+ /* (re)compile all the dep sets */
+ LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup);
+
+cleanup:
+ if (ret) {
+ /* revert changes of modules */
+ lys_unres_glob_revert(ctx, &ctx->unres);
+ }
+ lys_unres_glob_erase(&ctx->unres);
+ return ret;
+}
+
+LIBYANG_API_DEF uint16_t
+ly_ctx_get_options(const struct ly_ctx *ctx)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, 0);
+
+ return ctx->flags;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option)
+{
+ LY_ERR lyrc = LY_SUCCESS;
+ struct lys_module *mod;
+ uint32_t i;
+
+ LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
+ LY_CHECK_ERR_RET((option & LY_CTX_NO_YANGLIBRARY) && !(ctx->flags & LY_CTX_NO_YANGLIBRARY),
+ LOGARG(ctx, option), LY_EINVAL);
+
+ if (!(ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) {
+ ctx->flags |= LY_CTX_SET_PRIV_PARSED;
+ /* recompile the whole context to set the priv pointers */
+ for (i = 0; i < ctx->list.count; ++i) {
+ mod = ctx->list.objs[i];
+ if (mod->implemented) {
+ mod->to_compile = 1;
+ }
+ }
+ lyrc = ly_ctx_compile(ctx);
+ if (lyrc) {
+ ly_ctx_unset_options(ctx, LY_CTX_SET_PRIV_PARSED);
+ }
+ }
+
+ /* set the option(s) */
+ if (!lyrc) {
+ ctx->flags |= option;
+ }
+
+ return lyrc;
+}
+
+static LY_ERR
+lysc_node_clear_priv_dfs_cb(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue))
+{
+ node->priv = NULL;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ const struct lysc_ext_instance *ext;
+ struct lysc_node *root;
+
+ LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
+ LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);
+
+ if ((ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) {
+ struct lys_module *mod;
+ uint32_t index;
+
+ index = 0;
+ while ((mod = ly_ctx_get_module_iter(ctx, &index))) {
+ if (!mod->compiled) {
+ continue;
+ }
+
+ /* set NULL for all ::lysc_node.priv pointers in module */
+ lysc_module_dfs_full(mod, lysc_node_clear_priv_dfs_cb, NULL);
+
+ /* set NULL for all ::lysc_node.priv pointers in compiled extension instances */
+ LY_ARRAY_FOR(mod->compiled->exts, u) {
+ ext = &mod->compiled->exts[u];
+ LY_ARRAY_FOR(ext->substmts, v) {
+ if (ext->substmts[v].stmt & LY_STMT_DATA_NODE_MASK) {
+ LY_LIST_FOR(*(struct lysc_node **)ext->substmts[v].storage, root) {
+ lysc_tree_dfs_full(root, lysc_node_clear_priv_dfs_cb, NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* unset the option(s) */
+ ctx->flags &= ~option;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF uint16_t
+ly_ctx_get_change_count(const struct ly_ctx *ctx)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, 0);
+
+ return ctx->change_count;
+}
+
+LIBYANG_API_DEF ly_module_imp_clb
+ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+ if (user_data) {
+ *user_data = ctx->imp_clb_data;
+ }
+ return ctx->imp_clb;
+}
+
+LIBYANG_API_DEF void
+ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, );
+
+ ctx->imp_clb = clb;
+ ctx->imp_clb_data = user_data;
+}
+
+LIBYANG_API_DEF ly_ext_data_clb
+ly_ctx_set_ext_data_clb(struct ly_ctx *ctx, ly_ext_data_clb clb, void *user_data)
+{
+ ly_ext_data_clb prev;
+
+ LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+ prev = ctx->ext_clb;
+ ctx->ext_clb = clb;
+ ctx->ext_clb_data = user_data;
+
+ return prev;
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module_iter(const struct ly_ctx *ctx, uint32_t *index)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, index, NULL);
+
+ if (*index < ctx->list.count) {
+ return ctx->list.objs[(*index)++];
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of
+ * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures).
+ *
+ * @param[in] ctx Context where to iterate.
+ * @param[in] key Key value to search for.
+ * @param[in] key_size Optional length of the @p key. If zero, NULL-terminated key is expected.
+ * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key.
+ * @param[in,out] Iterator to pass between the function calls. On the first call, the variable is supposed to be
+ * initiated to 0. After each call returning a module, the value is greater by 1 than the index of the returned
+ * module in the context.
+ * @return Module matching the given key, NULL if no such module found.
+ */
+static struct lys_module *
+ly_ctx_get_module_by_iter(const struct ly_ctx *ctx, const char *key, size_t key_size, size_t key_offset, uint32_t *index)
+{
+ struct lys_module *mod;
+ const char *value;
+
+ for ( ; *index < ctx->list.count; ++(*index)) {
+ mod = ctx->list.objs[*index];
+ value = *(const char **)(((int8_t *)(mod)) + key_offset);
+ if ((!key_size && !strcmp(key, value)) || (key_size && !strncmp(key, value, key_size) && (value[key_size] == '\0'))) {
+ /* increment index for the next run */
+ ++(*index);
+ return mod;
+ }
+ }
+ /* done */
+ return NULL;
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module() and ly_ctx_get_module_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key.
+ * @param[in] revision Revision date to match. If NULL, the matching module must have no revision. To search for the latest
+ * revision module, use ly_ctx_get_module_latest_by().
+ * @return Matching module if any.
+ */
+static struct lys_module *
+ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision)
+{
+ struct lys_module *mod;
+ uint32_t index = 0;
+
+ while ((mod = ly_ctx_get_module_by_iter(ctx, key, 0, key_offset, &index))) {
+ if (!revision) {
+ if (!mod->revision) {
+ /* found requested module without revision */
+ return mod;
+ }
+ } else {
+ if (mod->revision && !strcmp(mod->revision, revision)) {
+ /* found requested module of the specific revision */
+ return mod;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+ return ly_ctx_get_module_by(ctx, ns, offsetof(struct lys_module, ns), revision);
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_by(ctx, name, offsetof(struct lys_module, name), revision);
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_latest() and ly_ctx_get_module_latest_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static struct lys_module *
+ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+ struct lys_module *mod;
+ uint32_t index = 0;
+
+ while ((mod = ly_ctx_get_module_by_iter(ctx, key, 0, key_offset, &index))) {
+ if (mod->latest_revision & LYS_MOD_LATEST_REV) {
+ return mod;
+ }
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lys_module, name));
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+ return ly_ctx_get_module_latest_by(ctx, ns, offsetof(struct lys_module, ns));
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_implemented() and ly_ctx_get_module_implemented_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_size Optional length of the @p key. If zero, NULL-terminated key is expected.
+ * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static struct lys_module *
+ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_size, size_t key_offset)
+{
+ struct lys_module *mod;
+ uint32_t index = 0;
+
+ while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_size, key_offset, &index))) {
+ if (mod->implemented) {
+ return mod;
+ }
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_implemented_by(ctx, name, 0, offsetof(struct lys_module, name));
+}
+
+struct lys_module *
+ly_ctx_get_module_implemented2(const struct ly_ctx *ctx, const char *name, size_t name_len)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_implemented_by(ctx, name, name_len, offsetof(struct lys_module, name));
+}
+
+LIBYANG_API_DEF struct lys_module *
+ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+ return ly_ctx_get_module_implemented_by(ctx, ns, 0, offsetof(struct lys_module, ns));
+}
+
+/**
+ * @brief Try to find a submodule in a module.
+ *
+ * @param[in] module Module where to search in.
+ * @param[in] submodule Name of the submodule to find.
+ * @param[in] revision Revision of the submodule to find. NULL for submodule with no revision.
+ * @param[in] latest Ignore @p revision and look for the latest revision.
+ * @return Pointer to the specified submodule if it is present in the context.
+ */
+static const struct lysp_submodule *
+_ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule, const char *revision, ly_bool latest)
+{
+ struct lysp_include *inc;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_CHECK_ARG_RET(NULL, module, module->parsed, submodule, NULL);
+
+ LY_ARRAY_FOR(module->parsed->includes, u) {
+ if (module->parsed->includes[u].submodule && !strcmp(submodule, module->parsed->includes[u].submodule->name)) {
+ inc = &module->parsed->includes[u];
+
+ if (latest && inc->submodule->latest_revision) {
+ /* latest revision */
+ return inc->submodule;
+ } else if (!revision && !inc->submodule->revs) {
+ /* no revision */
+ return inc->submodule;
+ } else if (revision && inc->submodule->revs && !strcmp(revision, inc->submodule->revs[0].date)) {
+ /* specific revision */
+ return inc->submodule;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Try to find a submodule in the context.
+ *
+ * @param[in] ctx Context where to search in.
+ * @param[in] submodule Name of the submodule to find.
+ * @param[in] revision Revision of the submodule to find. NULL for submodule with no revision.
+ * @param[in] latest Ignore @p revision and look for the latest revision.
+ * @return Pointer to the specified submodule if it is present in the context.
+ */
+static const struct lysp_submodule *
+_ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule, const char *revision, ly_bool latest)
+{
+ const struct lys_module *mod;
+ const struct lysp_submodule *submod = NULL;
+ uint32_t v;
+
+ LY_CHECK_ARG_RET(ctx, ctx, submodule, NULL);
+
+ for (v = 0; v < ctx->list.count; ++v) {
+ mod = ctx->list.objs[v];
+ if (!mod->parsed) {
+ continue;
+ }
+
+ submod = _ly_ctx_get_submodule2(mod, submodule, revision, latest);
+ if (submod) {
+ break;
+ }
+ }
+
+ return submod;
+}
+
+LIBYANG_API_DEF const struct lysp_submodule *
+ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule, const char *revision)
+{
+ return _ly_ctx_get_submodule(ctx, submodule, revision, 0);
+}
+
+LIBYANG_API_DEF const struct lysp_submodule *
+ly_ctx_get_submodule_latest(const struct ly_ctx *ctx, const char *submodule)
+{
+ return _ly_ctx_get_submodule(ctx, submodule, NULL, 1);
+}
+
+LIBYANG_API_DEF const struct lysp_submodule *
+ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule, const char *revision)
+{
+ return _ly_ctx_get_submodule2(module, submodule, revision, 0);
+}
+
+LIBYANG_API_DEF const struct lysp_submodule *
+ly_ctx_get_submodule2_latest(const struct lys_module *module, const char *submodule)
+{
+ return _ly_ctx_get_submodule2(module, submodule, NULL, 1);
+}
+
+LIBYANG_API_DEF void
+ly_ctx_reset_latests(struct ly_ctx *ctx)
+{
+ struct lys_module *mod;
+
+ for (uint32_t v = 0; v < ctx->list.count; ++v) {
+ mod = ctx->list.objs[v];
+ mod->latest_revision &= ~(LYS_MOD_LATEST_SEARCHDIRS | LYS_MOD_LATEST_IMPCLB);
+ if (mod->parsed && mod->parsed->includes) {
+ for (LY_ARRAY_COUNT_TYPE u = 0; u < LY_ARRAY_COUNT(mod->parsed->includes); ++u) {
+ mod->parsed->includes[u].submodule->latest_revision &= ~(LYS_MOD_LATEST_SEARCHDIRS | LYS_MOD_LATEST_IMPCLB);
+ }
+ }
+ }
+}
+
+LIBYANG_API_DEF uint32_t
+ly_ctx_internal_modules_count(const struct ly_ctx *ctx)
+{
+ if (!ctx) {
+ return 0;
+ }
+
+ if (ctx->flags & LY_CTX_NO_YANGLIBRARY) {
+ return LY_INTERNAL_MODS_COUNT - 2;
+ } else {
+ return LY_INTERNAL_MODS_COUNT;
+ }
+}
+
+static LY_ERR
+ylib_feature(struct lyd_node *parent, const struct lysp_module *pmod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_feature *f;
+
+ if (!pmod->mod->implemented) {
+ /* no features can be enabled */
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_FOR(pmod->features, struct lysp_feature, f) {
+ if (!(f->flags & LYS_FENABLED)) {
+ continue;
+ }
+
+ LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, 0, NULL));
+ }
+
+ LY_ARRAY_FOR(pmod->includes, u) {
+ LY_ARRAY_FOR(pmod->includes[u].submodule->features, struct lysp_feature, f) {
+ if (!(f->flags & LYS_FENABLED)) {
+ continue;
+ }
+
+ LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, 0, NULL));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+ylib_deviation(struct lyd_node *parent, const struct lys_module *cur_mod, ly_bool bis)
+{
+ LY_ARRAY_COUNT_TYPE i;
+ struct lys_module *mod;
+
+ if (!cur_mod->implemented) {
+ /* no deviations of the module for certain */
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_FOR(cur_mod->deviated_by, i) {
+ mod = cur_mod->deviated_by[i];
+
+ if (bis) {
+ LY_CHECK_RET(lyd_new_term(parent, NULL, "deviation", mod->name, 0, NULL));
+ } else {
+ LY_CHECK_RET(lyd_new_list(parent, NULL, "deviation", 0, NULL, mod->name,
+ (mod->parsed->revs ? mod->parsed->revs[0].date : "")));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+ylib_submodules(struct lyd_node *parent, const struct lysp_module *pmod, ly_bool bis)
+{
+ LY_ERR ret;
+ LY_ARRAY_COUNT_TYPE i;
+ struct lyd_node *cont;
+ struct lysp_submodule *submod;
+ int r;
+ char *str;
+
+ LY_ARRAY_FOR(pmod->includes, i) {
+ submod = pmod->includes[i].submodule;
+
+ if (bis) {
+ LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", 0, &cont, submod->name));
+
+ if (submod->revs) {
+ LY_CHECK_RET(lyd_new_term(cont, NULL, "revision", submod->revs[0].date, 0, NULL));
+ }
+ } else {
+ LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", 0, &cont, submod->name,
+ (submod->revs ? submod->revs[0].date : "")));
+ }
+
+ if (submod->filepath) {
+ r = asprintf(&str, "file://%s", submod->filepath);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(pmod->mod->ctx), LY_EMEM);
+
+ ret = lyd_new_term(cont, NULL, bis ? "location" : "schema", str, 0, NULL);
+ free(str);
+ LY_CHECK_RET(ret);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_ctx_get_yanglib_data(const struct ly_ctx *ctx, struct lyd_node **root_p, const char *content_id_format, ...)
+{
+ LY_ERR ret;
+ uint32_t i;
+ ly_bool bis = 0;
+ int r;
+ char *str;
+ const struct lys_module *mod;
+ struct lyd_node *root = NULL, *root_bis = NULL, *cont, *set_bis = NULL;
+ va_list ap;
+
+ LY_CHECK_ARG_RET(ctx, ctx, root_p, LY_EINVAL);
+
+ mod = ly_ctx_get_module_implemented(ctx, "ietf-yang-library");
+ LY_CHECK_ERR_RET(!mod, LOGERR(ctx, LY_EINVAL, "Module \"ietf-yang-library\" is not implemented."), LY_EINVAL);
+
+ if (mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, "2016-06-21")) {
+ bis = 0;
+ } else if (mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, IETF_YANG_LIB_REV)) {
+ bis = 1;
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Incompatible ietf-yang-library version in context.");
+ return LY_EINVAL;
+ }
+
+ LY_CHECK_GOTO(ret = lyd_new_inner(NULL, mod, "modules-state", 0, &root), error);
+
+ if (bis) {
+ LY_CHECK_GOTO(ret = lyd_new_inner(NULL, mod, "yang-library", 0, &root_bis), error);
+ LY_CHECK_GOTO(ret = lyd_new_list(root_bis, NULL, "module-set", 0, &set_bis, "complete"), error);
+ }
+
+ for (i = 0; i < ctx->list.count; ++i) {
+ mod = ctx->list.objs[i];
+ if (!mod->parsed) {
+ LOGERR(ctx, LY_ENOTFOUND, "Parsed module \"%s\" missing in the context.", mod->name);
+ goto error;
+ }
+
+ /*
+ * deprecated legacy
+ */
+ LY_CHECK_GOTO(ret = lyd_new_list(root, NULL, "module", 0, &cont, mod->name,
+ (mod->parsed->revs ? mod->parsed->revs[0].date : "")), error);
+
+ /* schema */
+ if (mod->filepath) {
+ r = asprintf(&str, "file://%s", mod->filepath);
+ LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error);
+
+ ret = lyd_new_term(cont, NULL, "schema", str, 0, NULL);
+ free(str);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ /* namespace */
+ LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, 0, NULL), error);
+
+ /* feature leaf-list */
+ LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error);
+
+ /* deviation list */
+ LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 0), error);
+
+ /* conformance-type */
+ LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "conformance-type", mod->implemented ? "implement" : "import", 0,
+ NULL), error);
+
+ /* submodule list */
+ LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 0), error);
+
+ /*
+ * current revision
+ */
+ if (bis) {
+ /* name and revision */
+ if (mod->implemented) {
+ LY_CHECK_GOTO(ret = lyd_new_list(set_bis, NULL, "module", 0, &cont, mod->name), error);
+
+ if (mod->parsed->revs) {
+ LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "revision", mod->parsed->revs[0].date, 0, NULL), error);
+ }
+ } else {
+ LY_CHECK_GOTO(ret = lyd_new_list(set_bis, NULL, "import-only-module", 0, &cont, mod->name,
+ (mod->parsed->revs ? mod->parsed->revs[0].date : "")), error);
+ }
+
+ /* namespace */
+ LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, 0, NULL), error);
+
+ /* location */
+ if (mod->filepath) {
+ r = asprintf(&str, "file://%s", mod->filepath);
+ LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error);
+
+ ret = lyd_new_term(cont, NULL, "location", str, 0, NULL);
+ free(str);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ /* submodule list */
+ LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 1), error);
+
+ /* feature list */
+ LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error);
+
+ /* deviation */
+ LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 1), error);
+ }
+ }
+
+ /* IDs */
+ va_start(ap, content_id_format);
+ r = vasprintf(&str, content_id_format, ap);
+ va_end(ap);
+ LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error);
+ ret = lyd_new_term(root, NULL, "module-set-id", str, 0, NULL);
+ LY_CHECK_ERR_GOTO(ret, free(str), error);
+
+ if (bis) {
+ /* create one complete schema */
+ LY_CHECK_ERR_GOTO(ret = lyd_new_list(root_bis, NULL, "schema", 0, &cont, "complete"), free(str), error);
+
+ LY_CHECK_ERR_GOTO(ret = lyd_new_term(cont, NULL, "module-set", "complete", 0, NULL), free(str), error);
+
+ /* content-id */
+ LY_CHECK_ERR_GOTO(ret = lyd_new_term(root_bis, NULL, "content-id", str, 0, NULL), free(str), error);
+ }
+ free(str);
+
+ if (root_bis) {
+ if (lyd_insert_sibling(root, root_bis, &root)) {
+ goto error;
+ }
+ root_bis = NULL;
+ }
+
+ LY_CHECK_GOTO(ret = lyd_validate_all(&root, NULL, LYD_VALIDATE_PRESENT, NULL), error);
+
+ *root_p = root;
+ return LY_SUCCESS;
+
+error:
+ lyd_free_all(root);
+ lyd_free_all(root_bis);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+ly_ctx_destroy(struct ly_ctx *ctx)
+{
+ struct lysf_ctx fctx = {.ctx = ctx};
+
+ if (!ctx) {
+ return;
+ }
+
+ /* models list */
+ for ( ; ctx->list.count; ctx->list.count--) {
+ fctx.mod = ctx->list.objs[ctx->list.count - 1];
+
+ /* remove the module */
+ if (fctx.mod->implemented) {
+ fctx.mod->implemented = 0;
+ lysc_module_free(&fctx, fctx.mod->compiled);
+ fctx.mod->compiled = NULL;
+ }
+ lys_module_free(&fctx, fctx.mod, 0);
+ }
+ free(ctx->list.objs);
+
+ /* free extensions */
+ lysf_ctx_erase(&fctx);
+
+ /* search paths list */
+ ly_set_erase(&ctx->search_paths, free);
+
+ /* leftover unres */
+ lys_unres_glob_erase(&ctx->unres);
+
+ /* clean the error list */
+ ly_err_clean(ctx, 0);
+ pthread_key_delete(ctx->errlist_key);
+
+ /* dictionary */
+ lydict_clean(&ctx->dict);
+
+ /* LYB hash lock */
+ pthread_mutex_destroy(&ctx->lyb_hash_lock);
+
+ /* plugins - will be removed only if this is the last context */
+ lyplg_clean();
+
+ free(ctx);
+}
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..331a89f
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,668 @@
+/**
+ * @file context.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief internal context structures and functions
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_CONTEXT_H_
+#define LY_CONTEXT_H_
+
+#include <stdint.h>
+
+#include "log.h"
+#include "parser_schema.h"
+#include "tree_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lys_module;
+
+/**
+ * @page howtoContext Context
+ *
+ * The context concept allows callers to work in environments with different sets of YANG modules.
+ *
+ * The first step with libyang is to create a new context using ::ly_ctx_new(). It returns a handler used in the following work.
+ * Note that the context is supposed to provide a stable environment for work with the data. Therefore the caller should prepare
+ * a complete context and after starting working with the data, the context and its content should not change. If it does,
+ * in most cases it leads to the context being recompiled and any parsed data invalid. Despite the API not enforcing this
+ * approach, it may change in future versions in the form of a locking mechanism which would allow further
+ * optimization of data manipulation. Also note that modules cannot be removed from their context. If you need to change the set
+ * of the schema modules in the context, the recommended way is to create a new context. To remove the context, there is ::ly_ctx_destroy() function.
+ *
+ * The context has [several options](@ref contextoptions) changing behavior when processing YANG modules being inserted. The
+ * specific behavior is mentioned below. All the options can be set as a parameter when the context is being created or later
+ * with ::ly_ctx_set_options().
+ *
+ * When creating a new context, another optional parameter is search_dir It provide directory where libyang
+ * will automatically search for YANG modules being imported or included. There is actually a set of search paths which can be later
+ * modified using ::ly_ctx_set_searchdir(), ::ly_ctx_unset_searchdir() and ::ly_ctx_unset_searchdir_last() functions. Before the values
+ * in the set are used, also the current working directory is (non-recursively) searched. For the case of the explicitly set
+ * search directories, they are searched recursively - all their subdirectories (and symlinks) are taken into account. Searching
+ * in the current working directory can be avoided with the context's ::LY_CTX_DISABLE_SEARCHDIR_CWD option.
+ * Searching in all the context's search dirs (without removing them) can be avoided with the context's
+ * ::LY_CTX_DISABLE_SEARCHDIRS option (or via ::ly_ctx_set_options()). This automatic searching can be preceded
+ * by a custom module searching callback (::ly_module_imp_clb) set via ::ly_ctx_set_module_imp_clb(). The algorithm of
+ * searching in search dirs is also available via API as ::lys_search_localfile() function.
+ *
+ * YANG modules are added into the context using [parser functions](@ref howtoSchemaParsers) - \b lys_parse*().
+ * Alternatively, also ::ly_ctx_load_module() can be used - in that case the ::ly_module_imp_clb or automatic
+ * search in search directories and in the current working directory is used, as described above. YANG submodules cannot be loaded
+ * or even validated directly, they are loaded always only as includes of YANG modules. Explicitly parsed/loaded modules are
+ * handled as implemented - libyang is able to instantiate data representing such a module. The modules loaded implicitly, are
+ * not implemented and serve only as a source of grouping or typedef definitions. Context can hold multiple revisions of the same
+ * YANG module, but only one of them can be implemented. Details about the difference between implemented and imported modules
+ * can be found on @ref howtoSchema page. This behavior can be changed with the context's ::LY_CTX_ALL_IMPLEMENTED option, which
+ * causes that all the parsed modules, whether loaded explicitly or implicitly, are set to be implemented. Note, that as
+ * a consequence of this option, only a single revision of any module can be present in the context in this case. Also, a less
+ * crude option ::LY_CTX_REF_IMPLEMENTED can be used to implement only referenced modules that should also be implemented.
+ *
+ * When loading/importing a module without revision, the latest revision of the required module is supposed to load.
+ * For a context, the first time the latest revision of a module is requested, it is properly searched for and loaded.
+ * However, when this module is requested (without revision) the second time, the one found previously is returned.
+ * This has the advantage of not searching for the module repeatedly but there is a drawback in case the content of search
+ * directories is updated and a later revision become available.
+ *
+ * Context holds all the schema modules internally. To get a specific module, use ::ly_ctx_get_module() (or some of its
+ * variants). If you need to do something with all the modules in the context, it is advised to iterate over them using
+ * ::ly_ctx_get_module_iter(). Alternatively, the ::ly_ctx_get_yanglib_data() function can be used to get complex information about the schemas in the context
+ * in the form of data tree defined by <a href="https://tools.ietf.org/html/rfc7895">ietf-yang-library</a> module.
+ *
+ * YANG data can be parsed by \b lyd_parse_*() functions. Note, that functions for schema have \b lys_
+ * prefix (or \b lysp_ for the parsed and \b lysc_ for the compiled schema - for details see @ref howtoSchema page) while
+ * functions for instance data have \b lyd_ prefix. Details about data formats or handling data without the appropriate
+ * YANG module in context can be found on @ref howtoData page.
+ *
+ * Besides the YANG modules, context holds also [error information](@ref howtoErrors) and
+ * [database of strings](@ref howtoContextDict), both connected with the processed YANG modules and data.
+ *
+ * - @subpage howtoErrors
+ * - @subpage howtoContextDict
+ *
+ * \note API for this group of functions is available in the [context module](@ref context).
+ *
+ * Functions List
+ * --------------
+ *
+ * - ::ly_ctx_new()
+ * - ::ly_ctx_destroy()
+ *
+ * - ::ly_ctx_set_searchdir()
+ * - ::ly_ctx_get_searchdirs()
+ * - ::ly_ctx_unset_searchdir()
+ * - ::ly_ctx_unset_searchdir_last()
+ *
+ * - ::ly_ctx_set_options()
+ * - ::ly_ctx_get_options()
+ * - ::ly_ctx_unset_options()
+ *
+ * - ::ly_ctx_set_module_imp_clb()
+ * - ::ly_ctx_get_module_imp_clb()
+ *
+ * - ::ly_ctx_load_module()
+ * - ::ly_ctx_get_module_iter()
+ * - ::ly_ctx_get_module()
+ * - ::ly_ctx_get_module_ns()
+ * - ::ly_ctx_get_module_implemented()
+ * - ::ly_ctx_get_module_implemented_ns()
+ * - ::ly_ctx_get_module_latest()
+ * - ::ly_ctx_get_module_latest_ns()
+ * - ::ly_ctx_get_submodule()
+ * - ::ly_ctx_get_submodule_latest()
+ * - ::ly_ctx_get_submodule2()
+ * - ::ly_ctx_get_submodule2_latest()
+ * - ::ly_ctx_reset_latests()
+ *
+ * - ::ly_ctx_get_yanglib_data()
+ *
+ * - ::ly_ctx_get_change_count()
+ * - ::ly_ctx_internal_modules_count()
+ *
+ * - ::lys_search_localfile()
+ * - ::lys_set_implemented()
+ *
+ */
+
+/**
+ * @defgroup context Context
+ * @{
+ *
+ * Structures and functions to manipulate with the libyang context containers.
+ *
+ * The \em context concept allows callers to work in environments with different sets of YANG schemas.
+ * More detailed information can be found at @ref howtoContext page.
+ */
+
+/**
+ * @struct ly_ctx
+ * @brief libyang context handler.
+ */
+struct ly_ctx;
+
+/**
+ * @ingroup context
+ * @defgroup contextoptions Context options
+ *
+ * Options to change context behavior.
+ *
+ * @{
+ */
+
+#define LY_CTX_ALL_IMPLEMENTED 0x01 /**< All the imported modules of the schema being parsed are implemented. */
+#define LY_CTX_REF_IMPLEMENTED 0x02 /**< Implement all imported modules "referenced" from an implemented module.
+ Normally, leafrefs, augment and deviation targets are implemented as
+ specified by YANG 1.1. In addition to this, implement any modules of
+ nodes referenced by when and must conditions and by any default values.
+ Generally, only if all these modules are implemented, the explicitly
+ implemented modules can be properly used and instantiated in data. */
+#define LY_CTX_NO_YANGLIBRARY 0x04 /**< Do not internally implement ietf-yang-library module. The option
+ causes that function ::ly_ctx_get_yanglib_data() does not work (returns ::LY_EINVAL) until
+ the ietf-yang-library module is loaded manually. While any revision
+ of this schema can be loaded with this option, note that the only
+ revisions implemented by ::ly_ctx_get_yanglib_data() are 2016-06-21 and 2019-01-04.
+ This option cannot be changed on existing context. */
+#define LY_CTX_DISABLE_SEARCHDIRS 0x08 /**< Do not search for schemas in context's searchdirs neither in current
+ working directory. It is entirely skipped and the only way to get
+ schema data for imports or for ::ly_ctx_load_module() is to use the
+ callbacks provided by caller via ::ly_ctx_set_module_imp_clb() */
+#define LY_CTX_DISABLE_SEARCHDIR_CWD 0x10 /**< Do not automatically search for schemas in current working
+ directory, which is by default searched automatically (despite not
+ recursively). */
+#define LY_CTX_PREFER_SEARCHDIRS 0x20 /**< When searching for schema, prefer searchdirs instead of user callback. */
+#define LY_CTX_SET_PRIV_PARSED 0x40 /**< For all compiled nodes, their private objects (::lysc_node.priv) are used
+ by libyang as a reference to the corresponding parsed node (::lysp_node).
+ The exception are \"case\" statements, which are omitted (shorthand),
+ in that case the private objects are set to NULL.
+ So if this option is set, the user must not change private objects.
+ Setting this option by ::ly_ctx_set_options() may result in context recompilation.
+ Resetting this option by ::ly_ctx_unset_options() cause that private
+ objects will be set to NULL. */
+#define LY_CTX_EXPLICIT_COMPILE 0x80 /**< If this flag is set, the compiled modules and their schema nodes are
+ not automatically updated (compiled) on any context changes. In other words, they do
+ not immediately take effect. To do that, call ::ly_ctx_compile(). Changes
+ requiring compilation include adding new modules, changing their features,
+ and implementing parsed-only modules. This option allows efficient compiled
+ context creation without redundant recompilations. */
+#define LY_CTX_ENABLE_IMP_FEATURES 0x0100 /**< By default, all features of newly implemented imported modules of
+ a module that is being loaded are disabled. With this flag they all become
+ enabled. */
+
+/** @} contextoptions */
+
+/**
+ * @brief Create libyang context.
+ *
+ * Context is used to hold all information about schemas. Usually, the application is supposed
+ * to work with a single context in which libyang is holding all schemas (and other internal
+ * information) according to which the data trees will be processed and validated. So, the schema
+ * trees are tightly connected with the specific context and they are held by the context internally
+ * - caller does not need to keep pointers to the schemas returned by ::lys_parse(), context knows
+ * about them. The data trees created with \b lyd_parse_*() are still connected with the specific context,
+ * but they are not internally held by the context. The data tree just points and lean on some data
+ * held by the context (schema tree, string dictionary, etc.). Therefore, in case of data trees, caller
+ * is supposed to keep pointers returned by the \b lyd_parse_*() functions and manage the data tree on its own. This
+ * also affects the number of instances of both tree types. While you can have only one instance of
+ * specific schema connected with a single context, number of data tree instances is not connected.
+ *
+ * @param[in] search_dir Directory (or directories) where libyang will search for the imported or included modules
+ * and submodules. If no such directory is available, NULL is accepted. Several directories can be specified,
+ * delimited by colon ":" (on Windows, use semicolon ";" instead).
+ * @param[in] options Context options, see @ref contextoptions.
+ * @param[out] new_ctx Pointer to the created libyang context if LY_SUCCESS returned.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx);
+
+/**
+ * @brief Create libyang context according to the provided yang-library data in a file.
+ *
+ * This function loads the yang-library data from the given path. If you need to pass the data as
+ * string, use ::::ly_ctx_new_ylmem(). Both functions extend functionality of ::ly_ctx_new() by loading
+ * modules specified in the ietf-yang-library form into the context being created.
+ * The preferred tree model revision is 2019-01-04. However, only the first module-set is processed and loaded
+ * into the context. If there are no matching nodes from this tree, the legacy tree (originally from model revision 2016-04-09)
+ * is processed. Note, that the modules are loaded the same way as in case of ::ly_ctx_load_module(), so the schema paths in the
+ * yang-library data are ignored and the modules are loaded from the context's search locations. On the other hand, YANG features
+ * of the modules are set as specified in the yang-library data.
+ * To get yang library data from a libyang context, use ::ly_ctx_get_yanglib_data().
+ *
+ * @param[in] search_dir Directory where libyang will search for the imported or included modules and submodules.
+ * If no such directory is available, NULL is accepted.
+ * @param[in] path Path to the file containing yang-library-data in the specified format
+ * @param[in] format Format of the data in the provided file.
+ * @param[in] options Context options, see @ref contextoptions.
+ * @param[in,out] ctx If *ctx is not NULL, the existing libyang context is modified. Otherwise, a pointer to a
+ * newly created context is returned here if LY_SUCCESS.
+ * @return LY_ERR return value
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_new_ylpath(const char *search_dir, const char *path, LYD_FORMAT format, int options,
+ struct ly_ctx **ctx);
+
+/**
+ * @brief Create libyang context according to the provided yang-library data in a string.
+ *
+ * Details in ::ly_ctx_new_ylpath().
+ *
+ * @param[in] search_dir Directory where libyang will search for the imported or included modules and submodules.
+ * If no such directory is available, NULL is accepted.
+ * @param[in] data String containing yang-library data in the specified format.
+ * @param[in] format Format of the data in the provided file.
+ * @param[in] options Context options, see @ref contextoptions.
+ * @param[in,out] ctx If *ctx is not NULL, the existing libyang context is modified. Otherwise, a pointer to a
+ * newly created context is returned here if LY_SUCCESS.
+ * @return LY_ERR return value
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_new_ylmem(const char *search_dir, const char *data, LYD_FORMAT format, int options,
+ struct ly_ctx **ctx);
+
+/**
+ * @brief Create libyang context according to the provided yang-library data in a data tree.
+ *
+ * Details in ::ly_ctx_new_ylpath().
+ *
+ * @param[in] search_dir Directory where libyang will search for the imported or included modules and submodules.
+ * If no such directory is available, NULL is accepted.
+ * @param[in] tree Data tree containing yang-library data.
+ * @param[in] options Context options, see @ref contextoptions.
+ * @param[in,out] ctx If *ctx is not NULL, the existing libyang context is modified. Otherwise, a pointer to a
+ * newly created context is returned here if LY_SUCCESS.
+ * @return LY_ERR return value
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_new_yldata(const char *search_dir, const struct lyd_node *tree, int options,
+ struct ly_ctx **ctx);
+
+/**
+ * @brief Compile (recompile) the context applying all the performed changes after the last context compilation.
+ * Should be used only if ::LY_CTX_EXPLICIT_COMPILE option is set, has no effect otherwise.
+ *
+ * @param[in] ctx Context to compile.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_compile(struct ly_ctx *ctx);
+
+/**
+ * @brief Add the search path into libyang context
+ *
+ * To reset search paths set in the context, use ::ly_ctx_unset_searchdir() and then
+ * set search paths again.
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] search_dir New search path to add to the current paths previously set in ctx.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir);
+
+/**
+ * @brief Clean the search path(s) from the libyang context
+ *
+ * To remove the recently added search path(s), use ::ly_ctx_unset_searchdir_last().
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] value Searchdir to be removed, use NULL to remove them all.
+ * @return LY_ERR return value
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_unset_searchdir(struct ly_ctx *ctx, const char *value);
+
+/**
+ * @brief Remove the least recently added search path(s) from the libyang context.
+ *
+ * To remove a specific search path by its value, use ::ly_ctx_unset_searchdir().
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] count Number of the searchdirs to be removed (starting by the least recently added).
+ * If the value is higher then the actual number of search paths, all paths are removed and no error is returned.
+ * Value 0 does not change the search path set.
+ * @return LY_ERR return value
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_unset_searchdir_last(struct ly_ctx *ctx, uint32_t count);
+
+/**
+ * @brief Get the NULL-terminated list of the search paths in libyang context. Do not modify the result!
+ *
+ * @param[in] ctx Context to query.
+ * @return NULL-terminated list (array) of the search paths, NULL if no searchpath was set.
+ * Do not modify the provided data in any way!
+ */
+LIBYANG_API_DECL const char * const *ly_ctx_get_searchdirs(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the currently set context's options.
+ *
+ * @param[in] ctx Context to query.
+ * @return Combination of all the currently set context's options, see @ref contextoptions.
+ */
+LIBYANG_API_DECL uint16_t ly_ctx_get_options(const struct ly_ctx *ctx);
+
+/**
+ * @brief Set some of the context's options, see @ref contextoptions.
+ * @param[in] ctx Context to be modified.
+ * @param[in] option Combination of the context's options to be set, see @ref contextoptions.
+ * If there is to be a change to ::LY_CTX_SET_PRIV_PARSED, the context will be recompiled
+ * and all ::lysc_node.priv in the modules will be overwritten, see ::LY_CTX_SET_PRIV_PARSED.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option);
+
+/**
+ * @brief Unset some of the context's options, see @ref contextoptions.
+ * @param[in] ctx Context to be modified.
+ * @param[in] option Combination of the context's options to be unset, see @ref contextoptions.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option);
+
+/**
+ * @brief Get the change count of the context (module set) during its life-time.
+ *
+ * @param[in] ctx Context to be examined.
+ * @return Context change count.
+ */
+LIBYANG_API_DECL uint16_t ly_ctx_get_change_count(const struct ly_ctx *ctx);
+
+/**
+ * @brief Callback for freeing returned module data in #ly_module_imp_clb.
+ *
+ * @param[in] module_data Data to free.
+ * @param[in] user_data User-supplied callback data, same as for #ly_module_imp_clb.
+ */
+typedef void (*ly_module_imp_data_free_clb)(void *module_data, void *user_data);
+
+/**
+ * @brief Callback for retrieving missing included or imported models in a custom way.
+ *
+ * When @p submod_name is provided, the submodule is requested instead of the module (in this case only
+ * the module name without its revision is provided).
+ *
+ * If an @arg free_module_data callback is provided, it will be used later to free the allegedly const data
+ * which were returned by this callback.
+ *
+ * @param[in] mod_name Missing module name.
+ * @param[in] mod_rev Optional missing module revision. If NULL and submod_name is not provided, the latest revision is
+ * requested, the parsed module is then marked by the latest_revision flag.
+ * @param[in] submod_name Optional missing submodule name.
+ * @param[in] submod_rev Optional missing submodule revision. If NULL and submod_name is provided, the latest revision is
+ * requested, the parsed submodule is then marked by the latest_revision flag.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] format Format of the returned module data.
+ * @param[out] module_data Requested module data.
+ * @param[out] free_module_data Callback for freeing the returned module data. If not set, the data will be left untouched.
+ * @return LY_ERR value. If the returned value differs from LY_SUCCESS, libyang continue in trying to get the module data
+ * according to the settings of its mechanism to search for the imported/included schemas.
+ */
+typedef LY_ERR (*ly_module_imp_clb)(const char *mod_name, const char *mod_rev, const char *submod_name, const char *submod_rev,
+ void *user_data, LYS_INFORMAT *format, const char **module_data, ly_module_imp_data_free_clb *free_module_data);
+
+/**
+ * @brief Get the custom callback for missing import/include module retrieval.
+ *
+ * @param[in] ctx Context to read from.
+ * @param[in] user_data Optional pointer for getting the user-supplied callback data.
+ * @return Callback or NULL if not set.
+ */
+LIBYANG_API_DECL ly_module_imp_clb ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data);
+
+/**
+ * @brief Set missing include or import module callback. It is meant to be used when the models
+ * are not locally available (such as when downloading modules from a NETCONF server), it should
+ * not be required in other cases.
+ *
+ * @param[in] ctx Context that will use this callback.
+ * @param[in] clb Callback responsible for returning the missing model.
+ * @param[in] user_data Arbitrary data that will always be passed to the callback @p clb.
+ */
+LIBYANG_API_DECL void ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data);
+
+/**
+ * @brief Callback for getting arbitrary run-time data required by an extension instance.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] ext_data Provided extension instance data.
+ * @param[out] ext_data_free Whether the extension instance should free @p ext_data or not.
+ * @return LY_ERR value.
+ */
+typedef LY_ERR (*ly_ext_data_clb)(const struct lysc_ext_instance *ext, void *user_data, void **ext_data,
+ ly_bool *ext_data_free);
+
+/**
+ * @brief Set callback providing run-time extension instance data. The expected data depend on the extension.
+ * Data expected by internal extensions:
+ *
+ * - *ietf-yang-schema-mount:mount-point* (struct lyd_node \*\*ext_data)\n
+ * Operational data tree with at least `ietf-yang-library` data describing the mounted schema and
+ * `ietf-yang-schema-mount` **validated** data describing the specific mount point
+ * ([ref](https://datatracker.ietf.org/doc/html/rfc8528#section-3.3)).
+ *
+ * @param[in] ctx Context that will use this callback.
+ * @param[in] clb Callback responsible for returning the extension instance data.
+ * @param[in] user_data Arbitrary data that will always be passed to the callback @p clb.
+ */
+LIBYANG_API_DECL ly_ext_data_clb ly_ctx_set_ext_data_clb(struct ly_ctx *ctx, ly_ext_data_clb clb, void *user_data);
+
+/**
+ * @brief Get YANG module of the given name and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] name Name of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the name and revision requirements.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its name.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given name is present in the context.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its name.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
+ * implemented module of the given name.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Iterate over all modules in the given context.
+ *
+ * @param[in] ctx Context with the modules.
+ * @param[in,out] index Index of the next module to get. Value of 0 starts from the beginning.
+ * The value is updated with each call, so to iterate over all modules the same variable is supposed
+ * to be used in all calls starting with value 0.
+ * @return Next context module, NULL if the last was already returned.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module_iter(const struct ly_ctx *ctx, uint32_t *index);
+
+/**
+ * @brief Get YANG module of the given namespace and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the namespace and revision requirements.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its namespace.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given namespace is present in the context.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its namespace.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The only implemented YANG module revision of the given namespace in the given context. NULL if there is no
+ * implemented module of the given namespace.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Get a specific submodule from context. If its belongs-to module is known, use ::ly_ctx_get_submodule2().
+ *
+ * @param[in] ctx libyang context to search in.
+ * @param[in] submodule Submodule name to find.
+ * @param[in] revision Revision of the submodule to find, NULL for a submodule without a revision.
+ * @return Found submodule, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysp_submodule *ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule,
+ const char *revision);
+
+/**
+ * @brief Get the latests revision of a submodule from context. If its belongs-to module is known,
+ * use ::ly_ctx_get_submodule2_latest().
+ *
+ * @param[in] ctx libyang context to search in.
+ * @param[in] submodule Submodule name to find.
+ * @return Found submodule, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysp_submodule *ly_ctx_get_submodule_latest(const struct ly_ctx *ctx, const char *submodule);
+
+/**
+ * @brief Get a specific submodule from a module. If the belongs-to module is not known, use ::ly_ctx_get_submodule().
+ *
+ * @param[in] module Belongs-to module to search in.
+ * @param[in] submodule Submodule name to find.
+ * @param[in] revision Revision of the submodule to find, NULL for a submodule without a revision.
+ * @return Found submodule, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysp_submodule *ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule,
+ const char *revision);
+
+/**
+ * @brief Get the latest revision of a submodule from a module. If the belongs-to module is not known,
+ * use ::ly_ctx_get_submodule_latest().
+ *
+ * @param[in] module Belongs-to module to search in.
+ * @param[in] submodule Submodule name to find.
+ * @return Found submodule, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysp_submodule *ly_ctx_get_submodule2_latest(const struct lys_module *module,
+ const char *submodule);
+
+/**
+ * @brief Reset cached latest revision information of the schemas in the context.
+ *
+ * This function is deprecated and should not be used.
+ *
+ * When a (sub)module is imported/included without revision, the latest revision is
+ * searched. libyang searches for the latest revision in searchdirs and/or via provided
+ * import callback ::ly_module_imp_clb() just once. Then it is expected that the content
+ * of searchdirs or data returned by the callback does not change. So when it changes,
+ * it is necessary to force searching for the latest revision in case of loading another
+ * module, which what this function does.
+ *
+ * The latest revision information is also reset when the searchdirs set changes via
+ * ::ly_ctx_set_searchdir().
+ *
+ * @param[in] ctx libyang context where the latest revision information is going to be reset.
+ */
+LIBYANG_API_DECL void ly_ctx_reset_latests(struct ly_ctx *ctx);
+
+/**
+ * @brief Learn the number of internal modules of a context. Internal modules
+ * is considered one that was loaded during the context creation.
+ *
+ * @param[in] ctx libyang context to examine.
+ * @return Number of internal modules.
+ */
+LIBYANG_API_DECL uint32_t ly_ctx_internal_modules_count(const struct ly_ctx *ctx);
+
+/**
+ * @brief Try to find the model in the searchpaths of \p ctx and load it into it. If custom missing
+ * module callback is set, it is used instead.
+ *
+ * The context itself is searched for the requested module first. If \p revision is not specified
+ * (the module of the latest revision is requested) and there is implemented revision of the requested
+ * module in the context, this implemented revision is returned despite there might be a newer revision.
+ * This behavior is cause by the fact that it is not possible to have multiple implemented revisions of
+ * the same module in the context.
+ *
+ * @param[in] ctx Context to add to.
+ * @param[in] name Name of the module to load.
+ * @param[in] revision Optional revision date of the module. If not specified, the latest revision is loaded.
+ * @param[in] features Optional array of features ended with NULL to be enabled if the module is being implemented.
+ * The feature string '*' enables all and array of length 1 with only the terminating NULL explicitly disables all
+ * the features. In case the parameter is NULL, the features are untouched - left disabled in newly loaded module or
+ * with the current features settings in case the module is already present in the context.
+ * @return Pointer to the data model structure, NULL if not found or some error occurred.
+ */
+LIBYANG_API_DECL struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision,
+ const char **features);
+
+/**
+ * @brief Get data of the internal ietf-yang-library module with information about all the loaded modules.
+ * ietf-yang-library module must be loaded.
+ *
+ * Note that "/ietf-yang-library:yang-library/datastore" list instances are not created and should be
+ * appended by the caller. There is a single "/ietf-yang-library:yang-library/schema" instance created
+ * with the key value "complete".
+ *
+ * If the data identifier can be limited to the existence and changes of this context, the following
+ * last 2 parameters can be used:
+ *
+ * "%u" as @p content_id_format and ::ly_ctx_get_change_count() as its parameter.
+ *
+ * @param[in] ctx Context with the modules.
+ * @param[out] root Generated yang-library data.
+ * @param[in] content_id_format Format string (printf-like) for the yang-library data identifier, which is
+ * the "content_id" node in the 2019-01-04 revision of ietf-yang-library.
+ * @param[in] ... Parameters for @p content_id_format.
+ * @return LY_ERR value
+ */
+LIBYANG_API_DECL LY_ERR ly_ctx_get_yanglib_data(const struct ly_ctx *ctx, struct lyd_node **root,
+ const char *content_id_format, ...);
+
+/**
+ * @brief Free all internal structures of the specified context.
+ *
+ * The function should be used before terminating the application to destroy
+ * and free all structures internally used by libyang. If the caller uses
+ * multiple contexts, the function should be called for each used context.
+ *
+ * All instance data are supposed to be freed before destroying the context using ::lyd_free_all(), for example.
+ * Data models (schemas) are destroyed automatically as part of ::ly_ctx_destroy() call.
+ *
+ * Note that the data stored by user into the ::lysc_node.priv pointer are kept
+ * untouched and the caller is responsible for freeing this private data.
+ *
+ * @param[in] ctx libyang context to destroy
+ */
+LIBYANG_API_DECL void ly_ctx_destroy(struct ly_ctx *ctx);
+
+/** @} context */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_CONTEXT_H_ */
diff --git a/src/dict.h b/src/dict.h
new file mode 100644
index 0000000..cf897e7
--- /dev/null
+++ b/src/dict.h
@@ -0,0 +1,122 @@
+/**
+ * @file dict.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang dictionary
+ *
+ * Copyright (c) 2015-2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_DICT_H_
+#define LY_DICT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* dummy context structure */
+struct ly_ctx;
+
+/**
+ * @page howtoContextDict Context Dictionary
+ *
+ * Context includes dictionary to store strings more effectively. The most of strings repeats quite often in schema
+ * as well as data trees. Therefore, instead of allocating those strings each time they appear, libyang stores them
+ * as records in the dictionary. The basic API to the context dictionary is public, so even a caller application can
+ * use the dictionary.
+ *
+ * To insert a string into the dictionary, caller can use ::lydict_insert() (adding a constant string) or
+ * ::lydict_insert_zc() (for dynamically allocated strings that won't be used by the caller after its insertion into
+ * the dictionary). Both functions provide the pointer to the inserted string in the dictionary record.
+ *
+ * To remove (reference of the) string from the context dictionary, ::lydict_remove() is supposed to be used.
+ *
+ * \note Incorrect usage of the dictionary can break libyang functionality.
+ *
+ * \note API for this group of functions is described in the [Dictionary module](@ref dict).
+ *
+ * Functions List
+ * --------------
+ * - ::lydict_insert()
+ * - ::lydict_insert_zc()
+ * - ::lydict_remove()
+ */
+
+/**
+ * @defgroup dict Dictionary
+ * @{
+ *
+ * Publicly visible functions and values of the libyang dictionary. They provide
+ * access to the strings stored in the libyang context. More detailed information can be found at
+ * @ref howtoContextDict page.
+ */
+
+/**
+ * @brief Insert string into dictionary. If the string is already present,
+ * only a reference counter is incremented and no memory allocation is
+ * performed.
+ *
+ * @param[in] ctx libyang context handler
+ * @param[in] value String to be stored in the dictionary. If NULL, function does nothing.
+ * @param[in] len Number of bytes to store. The value is not required to be
+ * NULL terminated string, the len parameter says number of bytes stored in
+ * dictionary. The specified number of bytes is duplicated and terminating NULL
+ * byte is added automatically. If \p len is 0, it is count automatically using strlen().
+ * @param[out] str_p Optional parameter to get pointer to the string corresponding to the @p value and stored in dictionary.
+ * @return LY_SUCCESS in case of successful insertion into dictionary, note that the function does not return LY_EEXIST.
+ * @return LY_EINVAL in case of invalid input parameters.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p);
+
+/**
+ * @brief Insert string into dictionary - zerocopy version. If the string is
+ * already present, only a reference counter is incremented and no memory
+ * allocation is performed. This insert function variant avoids duplication of
+ * specified value - it is inserted into the dictionary directly.
+ *
+ * @param[in] ctx libyang context handler
+ * @param[in] value NULL-terminated string to be stored in the dictionary. If
+ * the string is not present in dictionary, the pointer is directly used by the
+ * dictionary. Otherwise, the reference counter is incremented and the value is
+ * freed. So, after calling the function, caller is supposed to not use the
+ * value address anymore. If NULL, function does nothing.
+ * @param[out] str_p Optional parameter to get pointer to the string corresponding to the @p value and stored in dictionary.
+ * @return LY_SUCCESS in case of successful insertion into dictionary, note that the function does not return LY_EEXIST.
+ * @return LY_EINVAL in case of invalid input parameters.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p);
+
+/**
+ * @brief Remove specified string from the dictionary. It decrement reference
+ * counter for the string and if it is zero, the string itself is freed.
+ *
+ * @param[in] ctx libyang context handler
+ * @param[in] value String to be freed. Note, that not only the string itself
+ * must match the stored value, but also the address is being compared and the
+ * counter is decremented only if it matches. If NULL, function does nothing.
+ * @return LY_SUCCESS if the value was found and removed (or refcount decreased).
+ * @return LY_ENOTFOUND if the value was not found.
+ * @return LY_ERR on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lydict_remove(const struct ly_ctx *ctx, const char *value);
+
+/** @} dict */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_DICT_H_ */
diff --git a/src/diff.c b/src/diff.c
new file mode 100644
index 0000000..edfcb34
--- /dev/null
+++ b/src/diff.c
@@ -0,0 +1,2151 @@
+/**
+ * @file diff.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief diff functions
+ *
+ * Copyright (c) 2020 - 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "diff.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "log.h"
+#include "plugins_exts.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_types.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+#define LOGERR_META(ctx, meta_name, node) \
+ { \
+ char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
+ LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
+ free(__path); \
+ }
+
+#define LOGERR_NOINST(ctx, node) \
+ { \
+ char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
+ LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
+ free(__path); \
+ }
+
+#define LOGERR_UNEXPVAL(ctx, node, data_source) \
+ { \
+ char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
+ LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
+ free(__path); \
+ }
+
+#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
+ { \
+ char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
+ LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
+ lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
+ free(__path); \
+ }
+
+static const char *
+lyd_diff_op2str(enum lyd_diff_op op)
+{
+ switch (op) {
+ case LYD_DIFF_OP_CREATE:
+ return "create";
+ case LYD_DIFF_OP_DELETE:
+ return "delete";
+ case LYD_DIFF_OP_REPLACE:
+ return "replace";
+ case LYD_DIFF_OP_NONE:
+ return "none";
+ }
+
+ LOGINT(NULL);
+ return NULL;
+}
+
+static enum lyd_diff_op
+lyd_diff_str2op(const char *str)
+{
+ switch (str[0]) {
+ case 'c':
+ assert(!strcmp(str, "create"));
+ return LYD_DIFF_OP_CREATE;
+ case 'd':
+ assert(!strcmp(str, "delete"));
+ return LYD_DIFF_OP_DELETE;
+ case 'r':
+ assert(!strcmp(str, "replace"));
+ return LYD_DIFF_OP_REPLACE;
+ case 'n':
+ assert(!strcmp(str, "none"));
+ return LYD_DIFF_OP_NONE;
+ }
+
+ LOGINT(NULL);
+ return 0;
+}
+
+/**
+ * @brief Create diff metadata for a nested user-ordered node with the effective operation "create".
+ *
+ * @param[in] node User-rodered node to update.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_add_create_nested_userord(struct lyd_node *node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const char *meta_name, *meta_val;
+ size_t buflen = 0, bufused = 0;
+ uint32_t pos;
+ char *dyn = NULL;
+
+ assert(lysc_is_userordered(node->schema));
+
+ /* get correct metadata name and value */
+ if (lysc_is_dup_inst_list(node->schema)) {
+ meta_name = "yang:position";
+
+ pos = lyd_list_pos(node);
+ if (asprintf(&dyn, "%" PRIu32, pos) == -1) {
+ LOGMEM(LYD_CTX(node));
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ meta_val = dyn;
+ } else if (node->schema->nodetype == LYS_LIST) {
+ meta_name = "yang:key";
+
+ if (node->prev->next && (node->prev->schema == node->schema)) {
+ LY_CHECK_GOTO(rc = lyd_path_list_predicate(node->prev, &dyn, &buflen, &bufused, 0), cleanup);
+ meta_val = dyn;
+ } else {
+ meta_val = "";
+ }
+ } else {
+ meta_name = "yang:value";
+
+ if (node->prev->next && (node->prev->schema == node->schema)) {
+ meta_val = lyd_get_value(node->prev);
+ } else {
+ meta_val = "";
+ }
+ }
+
+ /* create the metadata */
+ LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, 0, NULL), cleanup);
+
+cleanup:
+ free(dyn);
+ return rc;
+}
+
+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 *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
+ const struct lyd_node *parent = NULL;
+
+ assert(diff);
+
+ /* replace leaf always needs orig-default and orig-value */
+ assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
+
+ /* create on userord needs key/value */
+ assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
+ (lysc_is_dup_inst_list(node->schema) && position) || key);
+ assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
+ (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
+
+ /* move on userord needs both key and orig-key/value and orig-value */
+ assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
+ (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
+ assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
+ (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
+ (value && orig_value));
+
+ /* find the first existing parent */
+ siblings = *diff;
+ while (1) {
+ /* find next node parent */
+ parent = node;
+ while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
+ parent = lyd_parent(parent);
+ }
+ if (parent == node) {
+ /* no more parents to find */
+ break;
+ }
+
+ /* check whether it exists in the diff */
+ if (lyd_find_sibling_first(siblings, parent, &match)) {
+ break;
+ }
+
+ /* another parent found */
+ diff_parent = match;
+
+ /* move down in the diff */
+ siblings = lyd_child_no_keys(match);
+ }
+
+ /* duplicate the subtree (and connect to the diff if possible) */
+ LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
+ LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
+
+ /* find the first duplicated parent */
+ if (!diff_parent) {
+ diff_parent = lyd_parent(dup);
+ while (diff_parent && diff_parent->parent) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ } else {
+ diff_parent = dup;
+ while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ }
+
+ /* no parent existed, must be manually connected */
+ if (!diff_parent) {
+ /* there actually was no parent to duplicate */
+ lyd_insert_sibling(*diff, dup, diff);
+ } else if (!diff_parent->parent) {
+ lyd_insert_sibling(*diff, diff_parent, diff);
+ }
+
+ /* add parent operation, if any */
+ if (diff_parent && (diff_parent != dup)) {
+ LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ }
+
+ /* add subtree operation */
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
+
+ if (op == LYD_DIFF_OP_CREATE) {
+ /* all nested user-ordered (leaf-)lists need special metadata for create op */
+ LYD_TREE_DFS_BEGIN(dup, elem) {
+ if ((elem != dup) && lysc_is_userordered(elem->schema)) {
+ LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
+ }
+ LYD_TREE_DFS_END(dup, elem);
+ }
+ }
+
+ /* orig-default */
+ if (orig_default) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
+ }
+
+ /* orig-value */
+ if (orig_value) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
+ }
+
+ /* key */
+ if (key) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
+ }
+
+ /* value */
+ if (value) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
+ }
+
+ /* position */
+ if (position) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
+ }
+
+ /* orig-key */
+ if (orig_key) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
+ }
+
+ /* orig-position */
+ if (orig_position) {
+ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
+ *
+ * @param[in] first Node from the first tree, can be NULL (on create).
+ * @param[in] schema Schema node of the list/leaf-list.
+ * @param[in,out] userord Sized array of userord items.
+ * @return Userord item for all the user-ordered list/leaf-list instances.
+ */
+static struct lyd_diff_userord *
+lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
+{
+ struct lyd_diff_userord *item;
+ struct lyd_node *iter;
+ const struct lyd_node **node;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(*userord, u) {
+ if ((*userord)[u].schema == schema) {
+ return &(*userord)[u];
+ }
+ }
+
+ /* it was not added yet, add it now */
+ LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
+
+ item->schema = schema;
+ item->pos = 0;
+ item->inst = NULL;
+
+ /* store all the instance pointers in the current order */
+ if (first) {
+ LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
+ LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
+ *node = iter;
+ }
+ }
+
+ return item;
+}
+
+/**
+ * @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.
+ *
+ * @param[in] first Node from the first tree, can be NULL (on create).
+ * @param[in] second Node from the second tree, can be NULL (on delete).
+ * @param[in] options Diff options.
+ * @param[in] userord_item Userord item of @p first and/or @p second node.
+ * @param[out] op Operation.
+ * @param[out] orig_default Original default metadata.
+ * @param[out] value Value metadata.
+ * @param[out] orig_value Original value metadata
+ * @param[out] key Key metadata.
+ * @param[out] orig_key Original key metadata.
+ * @param[out] position Position metadata.
+ * @param[out] orig_position Original position metadata.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if there is no change to be added into diff,
+ * @return LY_ERR value on other errors.
+ */
+static LY_ERR
+lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
+ struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
+ char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const struct lysc_node *schema;
+ size_t buflen, bufused;
+ uint32_t first_pos, second_pos;
+
+ assert(first || second);
+
+ *orig_default = NULL;
+ *value = NULL;
+ *orig_value = NULL;
+ *key = NULL;
+ *orig_key = NULL;
+ *position = NULL;
+ *orig_position = NULL;
+
+ schema = first ? first->schema : second->schema;
+ assert(lysc_is_userordered(schema));
+
+ /* find user-ordered first position */
+ if (first) {
+ for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
+ if (userord_item->inst[first_pos] == first) {
+ break;
+ }
+ }
+ assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
+ } else {
+ first_pos = 0;
+ }
+
+ /* prepare position of the next instance */
+ second_pos = userord_item->pos++;
+
+ /* learn operation first */
+ if (!second) {
+ *op = LYD_DIFF_OP_DELETE;
+ } else if (!first) {
+ *op = LYD_DIFF_OP_CREATE;
+ } else {
+ if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
+ /* in first, there is a different instance on the second position, we are going to move 'first' node */
+ *op = LYD_DIFF_OP_REPLACE;
+ } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
+ /* default flag change */
+ *op = LYD_DIFF_OP_NONE;
+ } else {
+ /* no changes */
+ return LY_ENOT;
+ }
+ }
+
+ /*
+ * set each attribute correctly based on the operation and node type
+ */
+
+ /* orig-default */
+ if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
+ if (first->flags & LYD_DEFAULT) {
+ *orig_default = "true";
+ } else {
+ *orig_default = "false";
+ }
+ }
+
+ /* value */
+ if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
+ ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
+ if (second_pos) {
+ *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
+ LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ } else {
+ *value = strdup("");
+ LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* orig-value */
+ if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
+ ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
+ if (first_pos) {
+ *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
+ LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ } else {
+ *orig_value = strdup("");
+ LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* key */
+ if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
+ ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
+ if (second_pos) {
+ buflen = bufused = 0;
+ LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0), cleanup);
+ } else {
+ *key = strdup("");
+ LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* orig-key */
+ if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
+ ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
+ if (first_pos) {
+ buflen = bufused = 0;
+ LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0), cleanup);
+ } else {
+ *orig_key = strdup("");
+ LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* position */
+ if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
+ if (second_pos) {
+ if (asprintf(position, "%" PRIu32, second_pos) == -1) {
+ LOGMEM(schema->module->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ } else {
+ *position = strdup("");
+ LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* orig-position */
+ if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
+ if (first_pos) {
+ if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
+ LOGMEM(schema->module->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ } else {
+ *orig_position = strdup("");
+ LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /*
+ * update our instances - apply the change
+ */
+ if (*op == LYD_DIFF_OP_CREATE) {
+ /* insert the instance */
+ LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
+ if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
+ memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
+ (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
+ }
+ LY_ARRAY_INCREMENT(userord_item->inst);
+ userord_item->inst[second_pos] = second;
+
+ } else if (*op == LYD_DIFF_OP_DELETE) {
+ /* remove the instance */
+ if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
+ memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
+ (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
+ }
+ LY_ARRAY_DECREMENT(userord_item->inst);
+
+ } else if (*op == LYD_DIFF_OP_REPLACE) {
+ /* move the instances */
+ memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
+ (first_pos - second_pos) * sizeof *userord_item->inst);
+ userord_item->inst[second_pos] = first;
+ }
+
+cleanup:
+ if (rc) {
+ free(*value);
+ *value = NULL;
+ free(*orig_value);
+ *orig_value = NULL;
+ free(*key);
+ *key = NULL;
+ free(*orig_key);
+ *orig_key = NULL;
+ free(*position);
+ *position = NULL;
+ free(*orig_position);
+ *orig_position = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
+ * lists/leaf-lists.
+ *
+ * @param[in] first Node from the first tree, can be NULL (on create).
+ * @param[in] second Node from the second tree, can be NULL (on delete).
+ * @param[in] options Diff options.
+ * @param[out] op Operation.
+ * @param[out] orig_default Original default metadata.
+ * @param[out] orig_value Original value metadata.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if there is no change to be added into diff,
+ * @return LY_ERR value on other errors.
+ */
+static LY_ERR
+lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, enum lyd_diff_op *op,
+ const char **orig_default, char **orig_value)
+{
+ const struct lysc_node *schema;
+ const char *str_val;
+
+ assert(first || second);
+
+ *orig_default = NULL;
+ *orig_value = NULL;
+
+ schema = first ? first->schema : second->schema;
+ assert(!lysc_is_userordered(schema));
+
+ /* learn operation first */
+ if (!second) {
+ *op = LYD_DIFF_OP_DELETE;
+ } else if (!first) {
+ *op = LYD_DIFF_OP_CREATE;
+ } else {
+ switch (schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ /* no changes */
+ return LY_ENOT;
+ 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 {
+ /* no changes */
+ return LY_ENOT;
+ }
+ break;
+ case LYS_LEAF:
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ if (lyd_compare_single(first, second, 0)) {
+ /* different values */
+ *op = LYD_DIFF_OP_REPLACE;
+ } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
+ /* default flag change */
+ *op = LYD_DIFF_OP_NONE;
+ } else {
+ /* no changes */
+ return LY_ENOT;
+ }
+ break;
+ default:
+ LOGINT_RET(schema->module->ctx);
+ }
+ }
+
+ /*
+ * set each attribute correctly based on the operation and node type
+ */
+
+ /* orig-default */
+ if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
+ if (first->flags & LYD_DEFAULT) {
+ *orig_default = "true";
+ } else {
+ *orig_default = "false";
+ }
+ }
+
+ /* orig-value */
+ if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
+ if (schema->nodetype == LYS_LEAF) {
+ str_val = lyd_get_value(first);
+ *orig_value = strdup(str_val ? str_val : "");
+ LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
+ } else {
+ LY_CHECK_RET(lyd_any_value_str(first, orig_value));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Find a matching instance of a node in a data tree.
+ *
+ * @param[in] siblings Siblings to search in.
+ * @param[in] target Target node to search for.
+ * @param[in] defaults Whether to consider (or ignore) default values.
+ * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[out] match Found match, NULL if no matching node found.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
+ struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
+{
+ LY_ERR r;
+
+ if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ /* try to find the exact instance */
+ r = lyd_find_sibling_first(siblings, target, match);
+ } else {
+ /* try to simply find the node, there cannot be more instances */
+ r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
+ }
+ if (r && (r != LY_ENOTFOUND)) {
+ return r;
+ }
+
+ /* update match as needed */
+ LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
+
+ if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
+ /* ignore default nodes */
+ *match = NULL;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Perform diff for all siblings at certain depth, recursively.
+ *
+ * For user-ordered lists/leaf-lists a specific structure is used for storing
+ * the current order. The idea is to apply all the generated diff changes
+ * virtually on the first tree so that we can continue to generate correct
+ * changes after some were already generated.
+ *
+ * The algorithm then uses second tree position-based changes with a before
+ * (preceding) item anchor.
+ *
+ * Example:
+ *
+ * Virtual first tree leaf-list order:
+ * 1 2 [3] 4 5
+ *
+ * Second tree leaf-list order:
+ * 1 2 [5] 3 4
+ *
+ * We are at the 3rd node now. We look at whether the nodes on the 3rd position
+ * match - they do not - move nodes so that the 3rd position node is final ->
+ * -> move node 5 to the 3rd position -> move node 5 after node 2.
+ *
+ * Required properties:
+ * Stored operations (move) should not be affected by later operations -
+ * - would cause a redundantly long list of operations, possibly inifinite.
+ *
+ * Implemenation justification:
+ * First, all delete operations and only then move/create operations are stored.
+ * Also, preceding anchor is used and after each iteration another node is
+ * at its final position. That results in the invariant that all preceding
+ * nodes are final and will not be changed by the later operations, meaning
+ * they can safely be used as anchors for the later operations.
+ *
+ * @param[in] first First tree first sibling.
+ * @param[in] second Second tree first sibling.
+ * @param[in] options Diff options.
+ * @param[in] nosiblings Whether to skip following siblings.
+ * @param[in,out] diff Diff to append to.
+ * @return LY_ERR value.
+ */
+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;
+ const struct lyd_node *iter_first, *iter_second;
+ struct lyd_node *match_second, *match_first;
+ struct lyd_diff_userord *userord = NULL, *userord_item;
+ struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+ enum lyd_diff_op op;
+ const char *orig_default;
+ char *orig_value, *key, *value, *position, *orig_key, *orig_position;
+
+ /* compare first tree to the second tree - delete, replace, none */
+ LY_LIST_FOR(first, iter_first) {
+ if (!iter_first->schema) {
+ continue;
+ }
+
+ assert(!(iter_first->schema->flags & LYS_KEY));
+ if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
+ /* skip default nodes */
+ continue;
+ }
+
+ /* 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,
+ &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);
+
+ /* 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,
+ &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);
+
+ free(orig_value);
+ free(key);
+ free(value);
+ free(position);
+ free(orig_key);
+ free(orig_position);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* get all the attributes */
+ ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
+
+ /* add into diff if there are any changes */
+ if (!ret) {
+ if (op == LYD_DIFF_OP_DELETE) {
+ ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
+ } else {
+ assert(match_second);
+ ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
+ }
+
+ free(orig_value);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else if (ret == LY_ENOT) {
+ ret = LY_SUCCESS;
+ } else {
+ goto 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),
+ options, 0, diff), cleanup);
+ }
+
+ if (nosiblings) {
+ break;
+ }
+ }
+
+ /* reset all cached positions */
+ LY_ARRAY_FOR(userord, u) {
+ userord[u].pos = 0;
+ }
+
+ /* compare second tree to the first tree - create, user-ordered move */
+ LY_LIST_FOR(second, iter_second) {
+ if (!iter_second->schema) {
+ continue;
+ }
+
+ assert(!(iter_second->schema->flags & LYS_KEY));
+ if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
+ /* skip default nodes */
+ continue;
+ }
+
+ /* 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,
+ &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);
+
+ /* get all the attributes */
+ ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
+ &value, &orig_value, &key, &orig_key, &position, &orig_position);
+
+ /* 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);
+
+ 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;
+ }
+ } 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);
+
+ /* 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);
+
+ free(orig_value);
+ LY_CHECK_GOTO(ret, cleanup);
+ } /* else was handled */
+
+ if (nosiblings) {
+ break;
+ }
+ }
+
+cleanup:
+ lyd_dup_inst_free(dup_inst_first);
+ lyd_dup_inst_free(dup_inst_second);
+ LY_ARRAY_FOR(userord, u) {
+ LY_ARRAY_FREE(userord[u].inst);
+ }
+ LY_ARRAY_FREE(userord);
+ return ret;
+}
+
+static LY_ERR
+lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
+ struct lyd_node **diff)
+{
+ const struct ly_ctx *ctx;
+
+ LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
+
+ if (first) {
+ ctx = LYD_CTX(first);
+ } else if (second) {
+ ctx = LYD_CTX(second);
+ } else {
+ ctx = NULL;
+ }
+
+ if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
+ LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
+ return LY_EINVAL;
+ }
+
+ *diff = NULL;
+
+ return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff)
+{
+ return lyd_diff(first, second, options, 1, diff);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff)
+{
+ return lyd_diff(first, second, options, 0, diff);
+}
+
+/**
+ * @brief Learn operation of a diff node.
+ *
+ * @param[in] diff_node Diff node.
+ * @param[out] op Operation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
+{
+ struct lyd_meta *meta = NULL;
+ const struct lyd_node *diff_parent;
+ const char *str;
+ char *path;
+
+ for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
+ LY_LIST_FOR(diff_parent->meta, meta) {
+ if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
+ str = lyd_get_meta_value(meta);
+ if ((str[0] == 'r') && (diff_parent != diff_node)) {
+ /* we do not care about this operation if it's in our parent */
+ continue;
+ }
+ *op = lyd_diff_str2op(str);
+ break;
+ }
+ }
+ if (meta) {
+ break;
+ }
+ }
+
+ if (!meta) {
+ path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
+ LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
+ free(path);
+ return LY_EINT;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Insert a diff node into a data tree.
+ *
+ * @param[in,out] first_node First sibling of the data tree.
+ * @param[in] parent_node Data tree sibling parent node.
+ * @param[in] new_node Node to insert.
+ * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
+ * the user-ordered instance will be inserted at the first position.
+ * @return err_info, NULL on success.
+ */
+static LY_ERR
+lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
+ const char *userord_anchor)
+{
+ LY_ERR ret;
+ struct lyd_node *anchor;
+ uint32_t pos, anchor_pos;
+ int found;
+
+ assert(new_node);
+
+ if (!*first_node) {
+ if (!parent_node) {
+ /* no parent or siblings */
+ *first_node = new_node;
+ return LY_SUCCESS;
+ }
+
+ /* simply insert into parent, no other children */
+ if (userord_anchor) {
+ LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
+ new_node->schema->name);
+ return LY_EINVAL;
+ }
+ return lyd_insert_child(parent_node, new_node);
+ }
+
+ assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
+
+ if (!lysc_is_userordered(new_node->schema)) {
+ /* simple insert */
+ return lyd_insert_sibling(*first_node, new_node, first_node);
+ }
+
+ if (userord_anchor) {
+ /* find the anchor sibling */
+ if (lysc_is_dup_inst_list(new_node->schema)) {
+ anchor_pos = atoi(userord_anchor);
+ if (!anchor_pos) {
+ LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
+ return LY_EINVAL;
+ }
+
+ found = 0;
+ pos = 1;
+ LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
+ if (pos == anchor_pos) {
+ found = 1;
+ break;
+ }
+ ++pos;
+ }
+ if (!found) {
+ LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
+ new_node->schema->name);
+ return LY_EINVAL;
+ }
+ } else {
+ ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
+ if (ret == LY_ENOTFOUND) {
+ LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
+ new_node->schema->name);
+ return LY_EINVAL;
+ } else if (ret) {
+ return ret;
+ }
+ }
+
+ /* insert after */
+ LY_CHECK_RET(lyd_insert_after(anchor, new_node));
+ assert(new_node->prev == anchor);
+ if (*first_node == new_node) {
+ *first_node = anchor;
+ }
+ } else {
+ /* find the first instance */
+ ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
+ LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
+
+ if (anchor) {
+ /* insert before the first instance */
+ LY_CHECK_RET(lyd_insert_before(anchor, new_node));
+ if ((*first_node)->prev->next) {
+ assert(!new_node->prev->next);
+ *first_node = new_node;
+ }
+ } else {
+ /* insert anywhere */
+ LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Apply diff subtree on data tree nodes, recursively.
+ *
+ * @param[in,out] first_node First sibling of the data tree.
+ * @param[in] parent_node Parent of the first sibling.
+ * @param[in] diff_node Current diff node.
+ * @param[in] diff_cb Optional diff callback.
+ * @param[in] cb_data User data for @p diff_cb.
+ * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
+ lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
+{
+ LY_ERR ret;
+ struct lyd_node *match, *diff_child;
+ const char *str_val, *meta_str;
+ enum lyd_diff_op op;
+ struct lyd_meta *meta;
+ struct lyd_dup_inst *child_dup_inst = NULL;
+ const struct ly_ctx *ctx = LYD_CTX(diff_node);
+
+ /* read all the valid attributes */
+ LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
+
+ /* 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))) {
+ if (op == LYD_DIFF_OP_REPLACE) {
+ /* find the node (we must have some siblings because the node was only moved) */
+ 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);
+ } else {
+ /* duplicate the node */
+ LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
+ }
+
+ /* get "key", "value", or "position" metadata string value */
+ if (lysc_is_dup_inst_list(diff_node->schema)) {
+ meta_str = "yang:position";
+ } else if (diff_node->schema->nodetype == LYS_LIST) {
+ meta_str = "yang:key";
+ } else {
+ meta_str = "yang:value";
+ }
+ meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
+ str_val = lyd_get_meta_value(meta);
+
+ /* insert/move the node */
+ if (str_val[0]) {
+ ret = lyd_diff_insert(first_node, parent_node, match, str_val);
+ } else {
+ ret = lyd_diff_insert(first_node, parent_node, match, NULL);
+ }
+ if (ret) {
+ if (op == LYD_DIFF_OP_CREATE) {
+ lyd_free_tree(match);
+ }
+ return ret;
+ }
+
+ 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;
+ }
+ } 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));
+ 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);
+ } else {
+ ret = lyd_insert_child(parent_node, match);
+ }
+ } else {
+ ret = lyd_insert_sibling(*first_node, match, first_node);
+ }
+ 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:
+ 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) {
+ break;
+ }
+ }
+
+ lyd_dup_inst_free(child_dup_inst);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
+ lyd_diff_cb diff_cb, void *cb_data)
+{
+ const struct lyd_node *root;
+ struct lyd_dup_inst *dup_inst = NULL;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_LIST_FOR(diff, root) {
+ if (mod && (lyd_owner_module(root) != mod)) {
+ /* skip data nodes from different modules */
+ continue;
+ }
+
+ /* apply relevant nodes from the diff datatree */
+ ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
+ if (ret) {
+ break;
+ }
+ }
+
+ lyd_dup_inst_free(dup_inst);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
+{
+ return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
+}
+
+/**
+ * @brief Update operations on a diff node when the new operation is NONE.
+ *
+ * @param[in] diff_match Node from the diff.
+ * @param[in] cur_op Current operation of @p diff_match.
+ * @param[in] src_diff Current source diff node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
+{
+ switch (cur_op) {
+ case LYD_DIFF_OP_NONE:
+ case LYD_DIFF_OP_CREATE:
+ case LYD_DIFF_OP_REPLACE:
+ if (src_diff->schema->nodetype & LYD_NODE_TERM) {
+ /* NONE on a term means only its dflt flag was changed */
+ diff_match->flags &= ~LYD_DEFAULT;
+ diff_match->flags |= src_diff->flags & LYD_DEFAULT;
+ }
+ break;
+ default:
+ /* delete operation is not valid */
+ LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Remove an attribute from a node.
+ *
+ * @param[in] node Node with the metadata.
+ * @param[in] name Metadata name.
+ */
+static void
+lyd_diff_del_meta(struct lyd_node *node, const char *name)
+{
+ struct lyd_meta *meta;
+
+ LY_LIST_FOR(node->meta, meta) {
+ if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
+ lyd_free_meta_single(meta);
+ return;
+ }
+ }
+
+ assert(0);
+}
+
+/**
+ * @brief Set a specific operation of a node. Delete the previous operation, if any.
+ * Does not change the default flag.
+ *
+ * @param[in] node Node to change.
+ * @param[in] op Operation to set.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
+{
+ struct lyd_meta *meta;
+
+ LY_LIST_FOR(node->meta, meta) {
+ if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
+ lyd_free_meta_single(meta);
+ break;
+ }
+ }
+
+ return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
+}
+
+/**
+ * @brief Update operations on a diff node when the new operation is REPLACE.
+ *
+ * @param[in] diff_match Node from the diff.
+ * @param[in] cur_op Current operation of @p diff_match.
+ * @param[in] src_diff Current source diff node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
+{
+ LY_ERR ret;
+ const char *str_val, *meta_name, *orig_meta_name;
+ struct lyd_meta *meta;
+ const struct lys_module *mod;
+ const struct lyd_node_any *any;
+ const struct ly_ctx *ctx = LYD_CTX(diff_match);
+
+ /* get "yang" module for the metadata */
+ mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
+ assert(mod);
+
+ switch (cur_op) {
+ case LYD_DIFF_OP_REPLACE:
+ case LYD_DIFF_OP_CREATE:
+ switch (diff_match->schema->nodetype) {
+ case LYS_LIST:
+ case LYS_LEAFLIST:
+ /* it was created/moved somewhere, but now it will be created/moved somewhere else,
+ * keep orig_key/orig_value (only replace oper) and replace key/value */
+ assert(lysc_is_userordered(diff_match->schema));
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "position";
+ } else if (diff_match->schema->nodetype == LYS_LIST) {
+ meta_name = "key";
+ } else {
+ meta_name = "value";
+ }
+
+ lyd_diff_del_meta(diff_match, meta_name);
+ meta = lyd_find_meta(src_diff->meta, mod, meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ break;
+ case LYS_LEAF:
+ /* replaced with the exact same value, impossible */
+ if (!lyd_compare_single(diff_match, src_diff, 0)) {
+ LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
+ return LY_EINVAL;
+ }
+
+ /* modify the node value */
+ if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
+
+ if (cur_op == LYD_DIFF_OP_REPLACE) {
+ /* compare values whether there is any change at all */
+ meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
+ str_val = lyd_get_meta_value(meta);
+ ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
+ if (!ret) {
+ /* values are the same, remove orig-value meta and set oper to NONE */
+ lyd_free_meta_single(meta);
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ }
+ }
+
+ /* modify the default flag */
+ diff_match->flags &= ~LYD_DEFAULT;
+ diff_match->flags |= src_diff->flags & LYD_DEFAULT;
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ if (!lyd_compare_single(diff_match, src_diff, 0)) {
+ LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
+ return LY_EINVAL;
+ }
+
+ /* modify the node value */
+ any = (struct lyd_node_any *)src_diff;
+ LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
+ break;
+ default:
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
+ break;
+ case LYD_DIFF_OP_NONE:
+ /* it is moved now */
+ assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
+
+ /* change the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+
+ /* set orig-meta and meta */
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "position";
+ orig_meta_name = "orig-position";
+ } else {
+ meta_name = "key";
+ orig_meta_name = "orig-key";
+ }
+
+ meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+
+ meta = lyd_find_meta(src_diff->meta, mod, meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ break;
+ default:
+ /* delete operation is not valid */
+ LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Update operations in a diff node when the new operation is CREATE.
+ *
+ * @param[in] diff_match Node from the diff.
+ * @param[in] cur_op Current operation of @p diff_match.
+ * @param[in] src_diff Current source diff node.
+ * @param[in] options Diff merge options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
+{
+ struct lyd_node *child;
+ const struct lysc_node_leaf *sleaf = NULL;
+ uint32_t trg_flags;
+ const char *meta_name, *orig_meta_name;
+ struct lyd_meta *meta, *orig_meta;
+ const struct ly_ctx *ctx = LYD_CTX(diff_match);
+
+ switch (cur_op) {
+ case LYD_DIFF_OP_DELETE:
+ /* remember current flags */
+ trg_flags = diff_match->flags;
+
+ if (lysc_is_userordered(diff_match->schema)) {
+ /* get anchor metadata */
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "yang:position";
+ orig_meta_name = "yang:orig-position";
+ } else if (diff_match->schema->nodetype == LYS_LIST) {
+ meta_name = "yang:key";
+ orig_meta_name = "yang:orig-key";
+ } else {
+ meta_name = "yang:value";
+ orig_meta_name = "yang:orig-value";
+ }
+ meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
+ orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
+ LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
+
+ /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
+ * the anchors stored in the metadata */
+ if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
+ /* deleted + created at another position -> operation REPLACE */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+
+ /* add anchor metadata */
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ } else {
+ /* deleted + created at the same position -> operation NONE */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+
+ /* delete anchor metadata */
+ lyd_free_meta_single(orig_meta);
+ }
+ } else if (diff_match->schema->nodetype == LYS_LEAF) {
+ if (options & LYD_DIFF_MERGE_DEFAULTS) {
+ /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
+ sleaf = (struct lysc_node_leaf *)diff_match->schema;
+ }
+
+ if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
+ &((struct lyd_node_term *)src_diff)->value)) {
+ /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
+ /* deleted + created -> operation NONE */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ } else {
+ /* we deleted it, but it was created with a different value -> operation REPLACE */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+
+ /* current value is the previous one (meta) */
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
+ lyd_get_value(diff_match), 0, NULL));
+
+ /* update the value itself */
+ LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
+ }
+ } else {
+ /* deleted + created -> operation NONE */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ }
+
+ if (diff_match->schema->nodetype & LYD_NODE_TERM) {
+ /* add orig-dflt metadata */
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
+ trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
+
+ /* update dflt flag itself */
+ diff_match->flags &= ~LYD_DEFAULT;
+ diff_match->flags |= src_diff->flags & LYD_DEFAULT;
+ }
+
+ /* but the operation of its children should remain DELETE */
+ LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
+ LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
+ }
+ break;
+ default:
+ /* create and replace operations are not valid */
+ LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Update operations on a diff node when the new operation is DELETE.
+ *
+ * @param[in] diff_match Node from the diff.
+ * @param[in] cur_op Current operation of @p diff_match.
+ * @param[in] src_diff Current source diff node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
+{
+ struct lyd_node *child;
+ struct lyd_meta *meta;
+ const char *meta_name;
+ const struct ly_ctx *ctx = LYD_CTX(diff_match);
+
+ /* we can delete only exact existing nodes */
+ LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_CTX(src_diff)), LY_EINT);
+
+ switch (cur_op) {
+ case LYD_DIFF_OP_CREATE:
+ /* it was created, but then deleted -> set NONE operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+
+ if (diff_match->schema->nodetype & LYD_NODE_TERM) {
+ /* add orig-default meta because it is expected */
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
+ diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
+ } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
+ /* keep operation for all descendants (for now) */
+ LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
+ LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
+ }
+ } /* else key-less list, for which all the descendants act as keys */
+ break;
+ case LYD_DIFF_OP_REPLACE:
+ /* remove the redundant metadata */
+ if (lysc_is_userordered(diff_match->schema)) {
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "position";
+ } else if (diff_match->schema->nodetype == LYS_LIST) {
+ meta_name = "key";
+ } else {
+ meta_name = "value";
+ }
+ } else {
+ assert(diff_match->schema->nodetype == LYS_LEAF);
+
+ /* switch value for the original one */
+ meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
+ if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
+ LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
+ return LY_EINVAL;
+ }
+
+ /* switch default for the original one, then remove the meta */
+ meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
+ diff_match->flags &= ~LYD_DEFAULT;
+ if (meta->value.boolean) {
+ diff_match->flags |= LYD_DEFAULT;
+ }
+ lyd_free_meta_single(meta);
+
+ meta_name = "orig-value";
+ }
+ lyd_diff_del_meta(diff_match, meta_name);
+
+ /* it was being changed, but should be deleted instead -> set DELETE operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
+ break;
+ case LYD_DIFF_OP_NONE:
+ /* it was not modified, but should be deleted -> set DELETE operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
+ break;
+ default:
+ /* delete operation is not valid */
+ LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @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
+lyd_diff_is_redundant(struct lyd_node *diff)
+{
+ enum lyd_diff_op op;
+ struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
+ struct lyd_node *child;
+ const struct lys_module *mod;
+ const char *str, *orig_meta_name, *meta_name;
+
+ assert(diff);
+
+ if (lysc_is_dup_inst_list(diff->schema)) {
+ /* all descendants are keys */
+ child = NULL;
+ } else {
+ child = lyd_child_no_keys(diff);
+ }
+ mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
+ assert(mod);
+
+ /* get node operation */
+ LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
+
+ if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
+ /* get metadata names */
+ if (lysc_is_dup_inst_list(diff->schema)) {
+ meta_name = "position";
+ orig_meta_name = "orig-position";
+ } else if (diff->schema->nodetype == LYS_LIST) {
+ meta_name = "key";
+ orig_meta_name = "orig-key";
+ } else {
+ meta_name = "value";
+ orig_meta_name = "orig-value";
+ }
+
+ /* check for redundant move */
+ orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
+ val_meta = lyd_find_meta(diff->meta, mod, meta_name);
+ assert(orig_val_meta && val_meta);
+
+ if (!lyd_compare_meta(orig_val_meta, val_meta)) {
+ /* there is actually no move */
+ lyd_free_meta_single(orig_val_meta);
+ lyd_free_meta_single(val_meta);
+ if (child) {
+ /* change operation to NONE, we have siblings */
+ lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
+ return 0;
+ }
+
+ /* redundant node, BUT !!
+ * In diff the move operation is always converted to be INSERT_AFTER, which is fine
+ * because the data that this is applied on should not change for the diff lifetime.
+ * However, when we are merging 2 diffs, this conversion is actually lossy because
+ * if the data change, the move operation can also change its meaning. In this specific
+ * case the move operation will be lost. But it can be considered a feature, it is not supported.
+ */
+ return 1;
+ }
+ } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
+ /* check whether at least the default flags are different */
+ meta = lyd_find_meta(diff->meta, mod, "orig-default");
+ assert(meta);
+ str = lyd_get_meta_value(meta);
+
+ /* if previous and current dflt flags are the same, this node is redundant */
+ if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ return 1;
+ }
+ return 0;
+ }
+
+ if (!child && (op == LYD_DIFF_OP_NONE)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Merge sysrepo diff subtree with another diff, recursively.
+ *
+ * @param[in] src_diff Source diff node.
+ * @param[in] diff_parent Current sysrepo diff parent.
+ * @param[in] diff_cb Optional diff callback.
+ * @param[in] cb_data User data for @p diff_cb.
+ * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
+ * @param[in] options Diff merge options.
+ * @param[in,out] diff Diff root node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
+ struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *child, *diff_node = NULL;
+ enum lyd_diff_op src_op, cur_op;
+ struct lyd_dup_inst *child_dup_inst = NULL;
+
+ /* get source node operation */
+ LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
+
+ /* 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));
+
+ /* merge operations */
+ switch (src_op) {
+ case LYD_DIFF_OP_REPLACE:
+ ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
+ break;
+ case LYD_DIFF_OP_CREATE:
+ if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
+ /* special case of creating duplicate (leaf-)list instances */
+ goto add_diff;
+ }
+
+ ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
+ break;
+ case LYD_DIFF_OP_DELETE:
+ ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
+ break;
+ case LYD_DIFF_OP_NONE:
+ /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
+ assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
+ ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
+ break;
+ default:
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
+ if (ret) {
+ LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
+ return ret;
+ }
+
+ if (diff_cb) {
+ /* call callback */
+ LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
+ }
+
+ /* update diff parent */
+ diff_parent = diff_node;
+
+ /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
+ * so there is nothing to merge for these "keys" */
+ if (!lysc_is_dup_inst_list(src_diff->schema)) {
+ /* merge src_diff recursively */
+ LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
+ ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
+ if (ret) {
+ break;
+ }
+ }
+ lyd_dup_inst_free(child_dup_inst);
+ LY_CHECK_RET(ret);
+ }
+ } 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, &diff_node));
+ } else {
+ LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
+ LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
+ }
+
+ /* insert node into diff if not already */
+ if (!diff_parent) {
+ lyd_insert_sibling(*diff, diff_node, diff);
+ }
+
+ /* update operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
+
+ if (diff_cb) {
+ /* call callback with no source diff node since it was duplicated and just added */
+ LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
+ }
+
+ /* update diff parent */
+ diff_parent = diff_node;
+ }
+
+ /* remove any redundant nodes */
+ if (lyd_diff_is_redundant(diff_parent)) {
+ if (diff_parent == *diff) {
+ *diff = (*diff)->next;
+ }
+ lyd_free_tree(diff_parent);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod,
+ lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
+{
+ const struct lyd_node *src_root;
+ struct lyd_dup_inst *dup_inst = NULL;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_LIST_FOR(src_diff, src_root) {
+ if (mod && (lyd_owner_module(src_root) != mod)) {
+ /* skip data nodes from different modules */
+ continue;
+ }
+
+ /* apply relevant nodes from the diff datatree */
+ LY_CHECK_GOTO(ret = lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, &dup_inst, options, diff), cleanup);
+ }
+
+cleanup:
+ lyd_dup_inst_free(dup_inst);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling,
+ lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
+{
+ LY_ERR ret;
+ struct lyd_dup_inst *dup_inst = NULL;
+
+ if (!src_sibling) {
+ return LY_SUCCESS;
+ }
+
+ ret = lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, &dup_inst, options, diff_first);
+ lyd_dup_inst_free(dup_inst);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
+{
+ return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
+}
+
+static LY_ERR
+lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_meta *meta;
+ const char *val1 = NULL;
+ char *val2;
+ uint32_t flags;
+
+ assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
+
+ meta = lyd_find_meta(node->meta, mod, "orig-value");
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
+
+ /* orig-value */
+ val1 = lyd_get_meta_value(meta);
+
+ /* current value */
+ if (node->schema->nodetype == LYS_LEAF) {
+ val2 = strdup(lyd_get_value(node));
+ } else {
+ LY_CHECK_RET(lyd_any_value_str(node, &val2));
+ }
+
+ /* switch values, keep default flag */
+ flags = node->flags;
+ if (node->schema->nodetype == LYS_LEAF) {
+ LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
+ } else {
+ union lyd_any_value anyval = {.str = val1};
+
+ LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
+ }
+ node->flags = flags;
+ LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
+
+cleanup:
+ free(val2);
+ return ret;
+}
+
+static LY_ERR
+lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
+{
+ struct lyd_meta *meta;
+ uint32_t flag1, flag2;
+
+ meta = lyd_find_meta(node->meta, mod, "orig-default");
+ LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
+
+ /* orig-default */
+ if (meta->value.boolean) {
+ flag1 = LYD_DEFAULT;
+ } else {
+ flag1 = 0;
+ }
+
+ /* current default */
+ flag2 = node->flags & LYD_DEFAULT;
+
+ if (flag1 == flag2) {
+ /* no default state change so nothing to reverse */
+ return LY_SUCCESS;
+ }
+
+ /* switch defaults */
+ node->flags &= ~LYD_DEFAULT;
+ node->flags |= flag1;
+ LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_meta *meta1, *meta2;
+ const char *val1 = NULL;
+ char *val2 = NULL;
+
+ meta1 = lyd_find_meta(node->meta, mod, name1);
+ LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
+
+ meta2 = lyd_find_meta(node->meta, mod, name2);
+ LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
+
+ /* value1 */
+ val1 = lyd_get_meta_value(meta1);
+
+ /* value2 */
+ val2 = strdup(lyd_get_meta_value(meta2));
+
+ /* switch values */
+ LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
+ LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
+
+cleanup:
+ free(val2);
+ return ret;
+}
+
+/**
+ * @brief Remove specific operation from all the nodes in a subtree.
+ *
+ * @param[in] diff Diff subtree to process.
+ * @param[in] op Only expected operation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
+{
+ struct lyd_node *elem;
+ struct lyd_meta *meta;
+
+ LYD_TREE_DFS_BEGIN(diff, elem) {
+ meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
+ if (meta) {
+ LY_CHECK_ERR_RET(lyd_diff_str2op(lyd_get_meta_value(meta)) != op, LOGINT(LYD_CTX(diff)), LY_EINT);
+ lyd_free_meta_single(meta);
+ }
+
+ LYD_TREE_DFS_END(diff, elem);
+ }
+
+ return LY_SUCCESS;
+}
+
+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;
+ struct lyd_node *root, *elem, *iter;
+ enum lyd_diff_op op;
+
+ LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
+
+ if (!src_diff) {
+ *diff = NULL;
+ return LY_SUCCESS;
+ }
+
+ /* duplicate diff */
+ LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, 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 operation attribute, if any */
+ LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), 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);
+
+ /* check all the children for the same operation, nothing else is expected */
+ LY_LIST_FOR(lyd_child(elem), iter) {
+ lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
+ }
+
+ LYD_TREE_DFS_continue = 1;
+ break;
+ case LYD_DIFF_OP_DELETE:
+ /* reverse delete to create */
+ LY_CHECK_GOTO(ret = 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) {
+ lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
+ }
+
+ LYD_TREE_DFS_continue = 1;
+ break;
+ case LYD_DIFF_OP_REPLACE:
+ 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);
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ /* any value change */
+ LY_CHECK_GOTO(ret = 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);
+ if (lysc_is_dup_inst_list(elem->schema)) {
+ LY_CHECK_GOTO(ret = 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);
+ }
+ 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);
+ } else {
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
+ }
+ break;
+ default:
+ LOGINT(LYD_CTX(src_diff));
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ break;
+ case LYD_DIFF_OP_NONE:
+ switch (elem->schema->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ /* default flag change */
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
+ break;
+ default:
+ /* nothing to do */
+ break;
+ }
+ break;
+ }
+ }
+
+ LYD_TREE_DFS_END(root, elem);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ lyd_free_siblings(*diff);
+ *diff = NULL;
+ }
+ return ret;
+}
diff --git a/src/diff.h b/src/diff.h
new file mode 100644
index 0000000..dab1614
--- /dev/null
+++ b/src/diff.h
@@ -0,0 +1,62 @@
+/**
+ * @file diff.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief internal diff header
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_DIFF_H_
+#define LY_DIFF_H_
+
+#include <stdint.h>
+
+#include "log.h"
+
+struct lyd_node;
+
+/**
+ * @brief Internal structure for storing current (virtual) user-ordered instances order.
+ */
+struct lyd_diff_userord {
+ const struct lysc_node *schema; /**< User-ordered list/leaf-list schema node. */
+ uint64_t pos; /**< Current position in the second tree. */
+ const struct lyd_node **inst; /**< Sized array of current instance order. */
+};
+
+/**
+ * @brief Diff operations.
+ */
+enum lyd_diff_op {
+ LYD_DIFF_OP_CREATE, /**< Subtree created. */
+ LYD_DIFF_OP_DELETE, /**< Subtree deleted. */
+ LYD_DIFF_OP_REPLACE, /**< Node value changed or (leaf-)list instance moved. */
+ LYD_DIFF_OP_NONE /**< No change of an existing inner node or default flag change of a term node. */
+};
+
+/**
+ * @brief Add a new change into diff.
+ *
+ * @param[in] node Node (subtree) to add into diff.
+ * @param[in] op Operation to set.
+ * @param[in] orig_default Original default metadata to set.
+ * @param[in] orig_value Original value metadata to set.
+ * @param[in] key Key metadata to set.
+ * @param[in] value Value metadata to set.
+ * @param[in] position Position metadata to set.
+ * @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.
+ * @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);
+
+#endif /* LY_DIFF_H_ */
diff --git a/src/hash_table.c b/src/hash_table.c
new file mode 100644
index 0000000..4f9dec3
--- /dev/null
+++ b/src/hash_table.c
@@ -0,0 +1,880 @@
+/**
+ * @file hash_table.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang dictionary for storing strings and generic hash table
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "hash_table.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "dict.h"
+#include "log.h"
+
+#define LYDICT_MIN_SIZE 1024
+
+/**
+ * @brief Comparison callback for dictionary's hash table
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
+{
+ LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
+
+ const char *str1 = ((struct dict_rec *)val1_p)->value;
+ const char *str2 = ((struct dict_rec *)val2_p)->value;
+
+ LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+ LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+ if (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+lydict_init(struct dict_table *dict)
+{
+ LY_CHECK_ARG_RET(NULL, dict, );
+
+ dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct dict_rec), lydict_val_eq, NULL, 1);
+ LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
+ pthread_mutex_init(&dict->lock, NULL);
+}
+
+void
+lydict_clean(struct dict_table *dict)
+{
+ struct dict_rec *dict_rec = NULL;
+ struct ht_rec *rec = NULL;
+
+ LY_CHECK_ARG_RET(NULL, dict, );
+
+ for (uint32_t i = 0; i < dict->hash_tab->size; i++) {
+ /* get ith record */
+ rec = (struct ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
+ if (rec->hits == 1) {
+ /*
+ * this should not happen, all records inserted into
+ * dictionary are supposed to be removed using lydict_remove()
+ * before calling lydict_clean()
+ */
+ dict_rec = (struct dict_rec *)rec->val;
+ LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
+ /* if record wasn't removed before free string allocated for that record */
+#ifdef NDEBUG
+ free(dict_rec->value);
+#endif
+ }
+ }
+
+ /* free table and destroy mutex */
+ lyht_free(dict->hash_tab);
+ pthread_mutex_destroy(&dict->lock);
+}
+
+/*
+ * Usage:
+ * - init hash to 0
+ * - repeatedly call dict_hash_multi(), provide hash from the last call
+ * - call dict_hash_multi() with key_part = NULL to finish the hash
+ */
+uint32_t
+dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
+{
+ uint32_t i;
+
+ if (key_part && len) {
+ for (i = 0; i < len; ++i) {
+ hash += key_part[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ } else {
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ }
+
+ return hash;
+}
+
+/*
+ * Bob Jenkin's one-at-a-time hash
+ * http://www.burtleburtle.net/bob/hash/doobs.html
+ *
+ * Spooky hash is faster, but it works only for little endian architectures.
+ */
+uint32_t
+dict_hash(const char *key, size_t len)
+{
+ uint32_t hash;
+
+ hash = dict_hash_multi(0, key, len);
+ return dict_hash_multi(hash, NULL, len);
+}
+
+static ly_bool
+lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *cb_data)
+{
+ LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
+
+ const char *str1 = ((struct dict_rec *)val1_p)->value;
+ const char *str2 = ((struct dict_rec *)val2_p)->value;
+
+ LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+ LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+ if (mod) {
+ /* used when inserting new values */
+ if (strcmp(str1, str2) == 0) {
+ return 1;
+ }
+ } else {
+ /* used when finding the original value again in the resized table */
+ return lydict_val_eq(val1_p, val2_p, mod, cb_data);
+ }
+
+ return 0;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_remove(const struct ly_ctx *ctx, const char *value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ size_t len;
+ uint32_t hash;
+ struct dict_rec rec, *match = NULL;
+ char *val_p;
+
+ if (!ctx || !value) {
+ return LY_SUCCESS;
+ }
+
+ LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
+
+ len = strlen(value);
+ hash = dict_hash(value, len);
+
+ /* create record for lyht_find call */
+ rec.value = (char *)value;
+ rec.refcount = 0;
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ /* set len as data for compare callback */
+ lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+ /* check if value is already inserted */
+ ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
+
+ if (ret == LY_SUCCESS) {
+ LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
+
+ /* if value is already in dictionary, decrement reference counter */
+ match->refcount--;
+ if (match->refcount == 0) {
+ /*
+ * remove record
+ * save pointer to stored string before lyht_remove to
+ * free it after it is removed from hash table
+ */
+ val_p = match->value;
+ ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
+ free(val_p);
+ LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
+ }
+ } else if (ret == LY_ENOTFOUND) {
+ LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
+ } else {
+ LOGINT(ctx);
+ }
+
+finish:
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+ return ret;
+}
+
+LY_ERR
+dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct dict_rec *match = NULL, rec;
+ uint32_t hash;
+
+ LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
+
+ hash = dict_hash(value, len);
+ /* set len as data for compare callback */
+ lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+ /* create record for lyht_insert */
+ rec.value = value;
+ rec.refcount = 1;
+
+ ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
+ if (ret == LY_EEXIST) {
+ match->refcount++;
+ if (zerocopy) {
+ free(value);
+ }
+ ret = LY_SUCCESS;
+ } else if (ret == LY_SUCCESS) {
+ if (!zerocopy) {
+ /*
+ * allocate string for new record
+ * record is already inserted in hash table
+ */
+ match->value = malloc(sizeof *match->value * (len + 1));
+ LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
+ if (len) {
+ memcpy(match->value, value, len);
+ }
+ match->value[len] = '\0';
+ }
+ } else {
+ /* lyht_insert returned error */
+ if (zerocopy) {
+ free(value);
+ }
+ return ret;
+ }
+
+ if (str_p) {
+ *str_p = match->value;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
+{
+ LY_ERR result;
+
+ LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+ if (!value) {
+ *str_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ if (!len) {
+ len = strlen(value);
+ }
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ result = dict_insert(ctx, (char *)value, len, 0, str_p);
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+ return result;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
+{
+ LY_ERR result;
+
+ LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+ if (!value) {
+ *str_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ result = dict_insert(ctx, value, strlen(value), 1, str_p);
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+ return result;
+}
+
+struct ht_rec *
+lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
+{
+ return (struct ht_rec *)&recs[idx * rec_size];
+}
+
+struct hash_table *
+lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize)
+{
+ struct hash_table *ht;
+
+ /* check that 2^x == size (power of 2) */
+ assert(size && !(size & (size - 1)));
+ assert(val_equal && val_size);
+ assert(resize == 0 || resize == 1);
+
+ if (size < LYHT_MIN_SIZE) {
+ size = LYHT_MIN_SIZE;
+ }
+
+ ht = malloc(sizeof *ht);
+ LY_CHECK_ERR_RET(!ht, LOGMEM(NULL), NULL);
+
+ ht->used = 0;
+ ht->size = size;
+ ht->invalid = 0;
+ ht->val_equal = val_equal;
+ ht->cb_data = cb_data;
+ ht->resize = resize;
+
+ ht->rec_size = (sizeof(struct ht_rec) - 1) + val_size;
+ /* allocate the records correctly */
+ ht->recs = calloc(size, ht->rec_size);
+ LY_CHECK_ERR_RET(!ht->recs, free(ht); LOGMEM(NULL), NULL);
+
+ return ht;
+}
+
+lyht_value_equal_cb
+lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
+{
+ lyht_value_equal_cb prev;
+
+ prev = ht->val_equal;
+ ht->val_equal = new_val_equal;
+ return prev;
+}
+
+void *
+lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
+{
+ void *prev;
+
+ prev = ht->cb_data;
+ ht->cb_data = new_cb_data;
+ return prev;
+}
+
+struct hash_table *
+lyht_dup(const struct hash_table *orig)
+{
+ struct hash_table *ht;
+
+ LY_CHECK_ARG_RET(NULL, orig, NULL);
+
+ ht = lyht_new(orig->size, orig->rec_size - (sizeof(struct ht_rec) - 1), orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
+ if (!ht) {
+ return NULL;
+ }
+
+ memcpy(ht->recs, orig->recs, (size_t)orig->used * (size_t)orig->rec_size);
+ ht->used = orig->used;
+ ht->invalid = orig->invalid;
+ return ht;
+}
+
+void
+lyht_free(struct hash_table *ht)
+{
+ if (ht) {
+ free(ht->recs);
+ free(ht);
+ }
+}
+
+/**
+ * @brief Resize a hash table.
+ *
+ * @param[in] ht Hash table to resize.
+ * @param[in] operation Operation to perform. 1 to enlarge, -1 to shrink, 0 to only rehash all records.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyht_resize(struct hash_table *ht, int operation)
+{
+ struct ht_rec *rec;
+ unsigned char *old_recs;
+ uint32_t i, old_size;
+
+ old_recs = ht->recs;
+ old_size = ht->size;
+
+ if (operation > 0) {
+ /* double the size */
+ ht->size <<= 1;
+ } else if (operation < 0) {
+ /* half the size */
+ ht->size >>= 1;
+ }
+
+ ht->recs = calloc(ht->size, ht->rec_size);
+ LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL); ht->recs = old_recs; ht->size = old_size, LY_EMEM);
+
+ /* reset used and invalid, it will increase again */
+ ht->used = 0;
+ ht->invalid = 0;
+
+ /* add all the old records into the new records array */
+ for (i = 0; i < old_size; ++i) {
+ rec = lyht_get_rec(old_recs, ht->rec_size, i);
+ if (rec->hits > 0) {
+ LY_ERR ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+
+ assert(!ret);
+ (void)ret;
+ }
+ }
+
+ /* final touches */
+ free(old_recs);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Search for the first match.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] hash Hash to find.
+ * @param[out] rec_p Optional found record.
+ * @return LY_SUCCESS hash found, returned its record,
+ * @return LY_ENOTFOUND hash not found, returned the record where it would be inserted.
+ */
+static LY_ERR
+lyht_find_first(struct hash_table *ht, uint32_t hash, struct ht_rec **rec_p)
+{
+ struct ht_rec *rec;
+ uint32_t i, idx;
+
+ if (rec_p) {
+ *rec_p = NULL;
+ }
+
+ idx = i = hash & (ht->size - 1);
+ rec = lyht_get_rec(ht->recs, ht->rec_size, idx);
+
+ /* skip through overflow and deleted records */
+ while ((rec->hits != 0) && ((rec->hits == -1) || ((rec->hash & (ht->size - 1)) != idx))) {
+ if ((rec->hits == -1) && rec_p && !(*rec_p)) {
+ /* remember this record for return */
+ *rec_p = rec;
+ }
+ i = (i + 1) % ht->size;
+ if (i == idx) {
+ /* we went through all the records (very unlikely, but possible when many records are invalid),
+ * just return not found */
+ assert(!rec_p || *rec_p);
+ return LY_ENOTFOUND;
+ }
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ }
+ if (rec->hits == 0) {
+ /* we could not find the value */
+ if (rec_p && !*rec_p) {
+ *rec_p = rec;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ /* we have found a record with equal (shortened) hash */
+ if (rec_p) {
+ *rec_p = rec;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Search for the next collision.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in,out] last Last returned collision record.
+ * @param[in] first First collision record (hits > 1).
+ * @return LY_SUCCESS when hash collision found, \p last points to this next collision,
+ * @return LY_ENOTFOUND when hash collision not found, \p last points to the record where it would be inserted.
+ */
+static LY_ERR
+lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *first)
+{
+ struct ht_rec *empty = NULL;
+ uint32_t i, idx;
+
+ assert(last && *last);
+
+ idx = (*last)->hash & (ht->size - 1);
+ i = (((unsigned char *)*last) - ht->recs) / ht->rec_size;
+
+ do {
+ i = (i + 1) % ht->size;
+ *last = lyht_get_rec(ht->recs, ht->rec_size, i);
+ if (*last == first) {
+ /* we went through all the records (very unlikely, but possible when many records are invalid),
+ * just return an invalid record */
+ assert(empty);
+ *last = empty;
+ return LY_ENOTFOUND;
+ }
+
+ if (((*last)->hits == -1) && !empty) {
+ empty = *last;
+ }
+ } while (((*last)->hits != 0) && (((*last)->hits == -1) || (((*last)->hash & (ht->size - 1)) != idx)));
+
+ if ((*last)->hits > 0) {
+ /* we found a collision */
+ assert((*last)->hits == 1);
+ return LY_SUCCESS;
+ }
+
+ /* no next collision found, return the record where it would be inserted */
+ if (empty) {
+ *last = empty;
+ } /* else (*last)->hits == 0, it is already correct */
+ return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Search for a record with specific value and hash.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] val_p Pointer to the value to find.
+ * @param[in] hash Hash to find.
+ * @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
+ * @param[out] crec_p Optional found first record.
+ * @param[out] col Optional collision number of @p rec_p, 0 for no collision.
+ * @param[out] rec_p Found exact matching record, may be a collision of @p crec_p.
+ * @return LY_ENOTFOUND if no record found,
+ * @return LY_SUCCESS if record was found.
+ */
+static LY_ERR
+lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, struct ht_rec **crec_p, uint32_t *col,
+ struct ht_rec **rec_p)
+{
+ struct ht_rec *rec, *crec;
+ uint32_t i, c;
+ LY_ERR r;
+
+ if (crec_p) {
+ *crec_p = NULL;
+ }
+ if (col) {
+ *col = 0;
+ }
+ *rec_p = NULL;
+
+ if (lyht_find_first(ht, hash, &rec)) {
+ /* not found */
+ return LY_ENOTFOUND;
+ }
+ if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
+ /* even the value matches */
+ if (crec_p) {
+ *crec_p = rec;
+ }
+ if (col) {
+ *col = 0;
+ }
+ *rec_p = rec;
+ return LY_SUCCESS;
+ }
+
+ /* some collisions, we need to go through them, too */
+ crec = rec;
+ c = crec->hits;
+ for (i = 1; i < c; ++i) {
+ r = lyht_find_collision(ht, &rec, crec);
+ assert(!r);
+ (void)r;
+
+ /* compare values */
+ if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
+ if (crec_p) {
+ *crec_p = crec;
+ }
+ if (col) {
+ *col = i;
+ }
+ *rec_p = rec;
+ return LY_SUCCESS;
+ }
+ }
+
+ /* not found even in collisions */
+ return LY_ENOTFOUND;
+}
+
+LY_ERR
+lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ struct ht_rec *rec;
+
+ lyht_find_rec(ht, val_p, hash, 0, NULL, NULL, &rec);
+
+ if (rec && match_p) {
+ *match_p = rec->val;
+ }
+ return rec ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+LY_ERR
+lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb collision_val_equal, void **match_p)
+{
+ struct ht_rec *rec, *crec;
+ uint32_t i, c;
+ LY_ERR r;
+
+ /* find the record of the previously found value */
+ if (lyht_find_rec(ht, val_p, hash, 1, &crec, &i, &rec)) {
+ /* not found, cannot happen */
+ LOGINT_RET(NULL);
+ }
+
+ /* go through collisions and find the next one after the previous one */
+ c = crec->hits;
+ for (++i; i < c; ++i) {
+ r = lyht_find_collision(ht, &rec, crec);
+ assert(!r);
+ (void)r;
+
+ if (rec->hash != hash) {
+ continue;
+ }
+
+ if (collision_val_equal) {
+ if (collision_val_equal(val_p, &rec->val, 0, ht->cb_data)) {
+ /* even the value matches */
+ if (match_p) {
+ *match_p = rec->val;
+ }
+ return LY_SUCCESS;
+ }
+ } else if (ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
+ /* even the value matches */
+ if (match_p) {
+ *match_p = rec->val;
+ }
+ return LY_SUCCESS;
+ }
+ }
+
+ /* the last equal value was already returned */
+ return LY_ENOTFOUND;
+}
+
+LY_ERR
+lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
+}
+
+LY_ERR
+lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p)
+{
+ LY_ERR r, ret = LY_SUCCESS;
+ struct ht_rec *rec, *crec = NULL;
+ int32_t i;
+ lyht_value_equal_cb old_val_equal = NULL;
+
+ if (!lyht_find_first(ht, hash, &rec)) {
+ /* we found matching shortened hash */
+ if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ /* even the value matches */
+ if (match_p) {
+ *match_p = (void *)&rec->val;
+ }
+ return LY_EEXIST;
+ }
+
+ /* some collisions, we need to go through them, too */
+ crec = rec;
+ for (i = 1; i < crec->hits; ++i) {
+ r = lyht_find_collision(ht, &rec, crec);
+ assert(!r);
+
+ /* compare values */
+ if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ if (match_p) {
+ *match_p = (void *)&rec->val;
+ }
+ return LY_EEXIST;
+ }
+ }
+
+ /* value not found, get the record where it will be inserted */
+ r = lyht_find_collision(ht, &rec, crec);
+ assert(r);
+ }
+
+ /* insert it into the returned record */
+ assert(rec->hits < 1);
+ if (rec->hits < 0) {
+ --ht->invalid;
+ }
+ rec->hash = hash;
+ rec->hits = 1;
+ memcpy(&rec->val, val_p, ht->rec_size - (sizeof(struct ht_rec) - 1));
+ if (match_p) {
+ *match_p = (void *)&rec->val;
+ }
+
+ if (crec) {
+ /* there was a collision, increase hits */
+ if (crec->hits == INT32_MAX) {
+ LOGINT(NULL);
+ }
+ ++crec->hits;
+ }
+
+ /* check size & enlarge if needed */
+ ++ht->used;
+ if (ht->resize) {
+ r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
+ if ((ht->resize == 1) && (r >= LYHT_FIRST_SHRINK_PERCENTAGE)) {
+ /* enable shrinking */
+ ht->resize = 2;
+ }
+ if ((ht->resize == 2) && (r >= LYHT_ENLARGE_PERCENTAGE)) {
+ if (resize_val_equal) {
+ old_val_equal = lyht_set_cb(ht, resize_val_equal);
+ }
+
+ /* enlarge */
+ ret = lyht_resize(ht, 1);
+ /* if hash_table was resized, we need to find new matching value */
+ if ((ret == LY_SUCCESS) && match_p) {
+ lyht_find(ht, val_p, hash, match_p);
+ }
+
+ if (resize_val_equal) {
+ lyht_set_cb(ht, old_val_equal);
+ }
+ }
+ }
+ return ret;
+}
+
+LY_ERR
+lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
+}
+
+LY_ERR
+lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+{
+ struct ht_rec *rec, *crec;
+ int32_t i;
+ ly_bool first_matched = 0;
+ LY_ERR r, ret = LY_SUCCESS;
+ lyht_value_equal_cb old_val_equal;
+
+ LY_CHECK_ERR_RET(lyht_find_first(ht, hash, &rec), LOGARG(NULL, hash), LY_ENOTFOUND); /* hash not found */
+
+ if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ /* even the value matches */
+ first_matched = 1;
+ }
+
+ /* we always need to go through collisions */
+ crec = rec;
+ for (i = 1; i < crec->hits; ++i) {
+ r = lyht_find_collision(ht, &rec, crec);
+ assert(!r);
+
+ /* compare values */
+ if (!first_matched && (rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ break;
+ }
+ }
+
+ if (i < crec->hits) {
+ /* one of collisions matched, reduce collision count, remove the record */
+ assert(!first_matched);
+ --crec->hits;
+ rec->hits = -1;
+ } else if (first_matched) {
+ /* the first record matches */
+ if (crec != rec) {
+ /* ... so put the last collision in its place */
+ rec->hits = crec->hits - 1;
+ memcpy(crec, rec, ht->rec_size);
+ }
+ rec->hits = -1;
+ } else {
+ /* value not found even in collisions */
+ return LY_ENOTFOUND;
+ }
+
+ /* check size & shrink if needed */
+ --ht->used;
+ ++ht->invalid;
+ if (ht->resize == 2) {
+ r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
+ if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
+ if (resize_val_equal) {
+ old_val_equal = lyht_set_cb(ht, resize_val_equal);
+ }
+
+ /* shrink */
+ ret = lyht_resize(ht, -1);
+
+ if (resize_val_equal) {
+ lyht_set_cb(ht, old_val_equal);
+ }
+ }
+ }
+
+ /* rehash all records if needed */
+ r = ((ht->size - ht->used - ht->invalid) * 100) / ht->size;
+ if (r < LYHT_REHASH_PERCENTAGE) {
+ if (resize_val_equal) {
+ old_val_equal = lyht_set_cb(ht, resize_val_equal);
+ }
+
+ /* rehash */
+ ret = lyht_resize(ht, 0);
+
+ if (resize_val_equal) {
+ lyht_set_cb(ht, old_val_equal);
+ }
+ }
+
+ return ret;
+}
+
+LY_ERR
+lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash)
+{
+ return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
+}
+
+uint32_t
+lyht_get_fixed_size(uint32_t item_count)
+{
+ uint32_t i, size = 0;
+
+ /* detect number of upper zero bits in the items' counter value ... */
+ for (i = (sizeof item_count * CHAR_BIT) - 1; i > 0; i--) {
+ size = item_count << i;
+ size = size >> i;
+ if (size == item_count) {
+ break;
+ }
+ }
+ assert(i);
+
+ /* ... and then we convert it to the position of the highest non-zero bit ... */
+ i = (sizeof item_count * CHAR_BIT) - i;
+
+ /* ... and by using it to shift 1 to the left we get the closest sufficient hash table size */
+ size = 1 << i;
+
+ return size;
+}
diff --git a/src/hash_table.h b/src/hash_table.h
new file mode 100644
index 0000000..91ae63d
--- /dev/null
+++ b/src/hash_table.h
@@ -0,0 +1,279 @@
+/**
+ * @file hash_table.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang hash table
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_HASH_TABLE_H_
+#define LY_HASH_TABLE_H_
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "compat.h"
+#include "log.h"
+
+/**
+ * @brief Compute hash from (several) string(s).
+ *
+ * Usage:
+ * - init hash to 0
+ * - repeatedly call ::dict_hash_multi(), provide hash from the last call
+ * - call ::dict_hash_multi() with key_part = NULL to finish the hash
+ */
+uint32_t dict_hash_multi(uint32_t hash, const char *key_part, size_t len);
+
+/**
+ * @brief Compute hash from a string.
+ */
+uint32_t dict_hash(const char *key, size_t len);
+
+/**
+ * @brief Callback for checking hash table values equivalence.
+ *
+ * @param[in] val1_p Pointer to the first value, the one being searched (inserted/removed).
+ * @param[in] val2_p Pointer to the second value, the one stored in the hash table.
+ * @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
+ * @param[in] cb_data User callback data.
+ * @return false (non-equal) or true (equal).
+ */
+typedef ly_bool (*lyht_value_equal_cb)(void *val1_p, void *val2_p, ly_bool mod, void *cb_data);
+
+/** reference value for 100% */
+#define LYHT_HUNDRED_PERCENTAGE 100
+
+/** when the table is at least this much percent full, it is enlarged (double the size) */
+#define LYHT_ENLARGE_PERCENTAGE 75
+
+/** only once the table is this much percent full, enable shrinking */
+#define LYHT_FIRST_SHRINK_PERCENTAGE 50
+
+/** when the table is less than this much percent full, it is shrunk (half the size) */
+#define LYHT_SHRINK_PERCENTAGE 25
+
+/** when the table has less than this much percent empty records, it is rehashed to get rid of all the invalid records */
+#define LYHT_REHASH_PERCENTAGE 2
+
+/** never shrink beyond this size */
+#define LYHT_MIN_SIZE 8
+
+/**
+ * @brief Generic hash table record.
+ */
+struct ht_rec {
+ uint32_t hash; /* hash of the value */
+ int32_t hits; /* collision/overflow value count - 1 (a filled entry has 1 hit,
+ * special value -1 means a deleted record) */
+ unsigned char val[1]; /* arbitrary-size value */
+} _PACKED;
+
+/**
+ * @brief (Very) generic hash table.
+ *
+ * Hash table with open addressing collision resolution and
+ * linear probing of interval 1 (next free record is used).
+ * Removal is lazy (removed records are only marked), but
+ * if possible, they are fully emptied.
+ */
+struct hash_table {
+ uint32_t used; /* number of values stored in the hash table (filled records) */
+ uint32_t size; /* always holds 2^x == size (is power of 2), actually number of records allocated */
+ uint32_t invalid; /* number of invalid records (deleted) */
+ lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
+ void *cb_data; /* user data callback arbitrary value */
+ uint16_t resize; /* 0 - resizing is disabled, *
+ * 1 - enlarging is enabled, *
+ * 2 - both shrinking and enlarging is enabled */
+ uint16_t rec_size; /* real size (in bytes) of one record for accessing recs array */
+ unsigned char *recs; /* pointer to the hash table itself (array of struct ht_rec) */
+};
+
+struct dict_rec {
+ char *value;
+ uint32_t refcount;
+};
+
+/**
+ * dictionary to store repeating strings
+ */
+struct dict_table {
+ struct hash_table *hash_tab;
+ pthread_mutex_t lock;
+};
+
+/**
+ * @brief Initiate content (non-zero values) of the dictionary
+ *
+ * @param[in] dict Dictionary table to initiate
+ */
+void lydict_init(struct dict_table *dict);
+
+/**
+ * @brief Cleanup the dictionary content
+ *
+ * @param[in] dict Dictionary table to cleanup
+ */
+void lydict_clean(struct dict_table *dict);
+
+/**
+ * @brief Create new hash table.
+ *
+ * @param[in] size Starting size of the hash table (capacity of values), must be power of 2.
+ * @param[in] val_size Size in bytes of value (the stored hashed item).
+ * @param[in] val_equal Callback for checking value equivalence.
+ * @param[in] cb_data User data always passed to @p val_equal.
+ * @param[in] resize Whether to resize the table on too few/too many records taken.
+ * @return Empty hash table, NULL on error.
+ */
+struct hash_table *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize);
+
+/**
+ * @brief Set hash table value equal callback.
+ *
+ * @param[in] ht Hash table to modify.
+ * @param[in] new_val_equal New callback for checking value equivalence.
+ * @return Previous callback for checking value equivalence.
+ */
+lyht_value_equal_cb lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal);
+
+/**
+ * @brief Set hash table value equal callback user data.
+ *
+ * @param[in] ht Hash table to modify.
+ * @param[in] new_cb_data New data for values callback.
+ * @return Previous data for values callback.
+ */
+void *lyht_set_cb_data(struct hash_table *ht, void *new_cb_data);
+
+/**
+ * @brief Make a duplicate of an existing hash table.
+ *
+ * @param[in] orig Original hash table to duplicate.
+ * @return Duplicated hash table @p orig, NULL on error.
+ */
+struct hash_table *lyht_dup(const struct hash_table *orig);
+
+/**
+ * @brief Free a hash table.
+ *
+ * @param[in] ht Hash table to be freed.
+ */
+void lyht_free(struct hash_table *ht);
+
+/**
+ * @brief Find a value in a hash table.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] val_p Pointer to the value to find.
+ * @param[in] hash Hash of the stored value.
+ * @param[out] match_p Pointer to the matching value, optional.
+ * @return LY_SUCCESS if value was found,
+ * @return LY_ENOTFOUND if not found.
+ */
+LY_ERR lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Find another equal value in the hash table.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] val_p Pointer to the previously found value in @p ht.
+ * @param[in] hash Hash of the previously found value.
+ * @param[out] match_p Pointer to the matching value, optional.
+ * @return LY_SUCCESS if value was found,
+ * @return LY_ENOTFOUND if not found.
+ */
+LY_ERR lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Find another equal value in the hash table. Same functionality as ::lyht_find_next()
+ * but allows to specify a collision val equal callback to be used for checking for matching colliding values.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] val_p Pointer to the previously found value in @p ht.
+ * @param[in] hash Hash of the previously found value.
+ * @param[in] collision_val_equal Val equal callback to use for checking collisions.
+ * @param[out] match_p Pointer to the matching value, optional.
+ * @return LY_SUCCESS if value was found,
+ * @return LY_ENOTFOUND if not found.
+ */
+LY_ERR lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb collision_val_equal, void **match_p);
+
+/**
+ * @brief Insert a value into a hash table.
+ *
+ * @param[in] ht Hash table to insert into.
+ * @param[in] val_p Pointer to the value to insert. Be careful, if the values stored in the hash table
+ * are pointers, @p val_p must be a pointer to a pointer.
+ * @param[in] hash Hash of the stored value.
+ * @param[out] match_p Pointer to the stored value, optional
+ * @return LY_SUCCESS on success,
+ * @return LY_EEXIST in case the value is already present.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LY_ERR lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Insert a value into hash table. Same functionality as ::lyht_insert()
+ * but allows to specify a temporary val equal callback to be used in case the hash table
+ * will be resized after successful insertion.
+ *
+ * @param[in] ht Hash table to insert into.
+ * @param[in] val_p Pointer to the value to insert. Be careful, if the values stored in the hash table
+ * are pointers, @p val_p must be a pointer to a pointer.
+ * @param[in] hash Hash of the stored value.
+ * @param[in] resize_val_equal Val equal callback to use for resizing.
+ * @param[out] match_p Pointer to the stored value, optional
+ * @return LY_SUCCESS on success,
+ * @return LY_EEXIST in case the value is already present.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LY_ERR lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p);
+
+/**
+ * @brief Remove a value from a hash table.
+ *
+ * @param[in] ht Hash table to remove from.
+ * @param[in] val_p Pointer to value to be removed. Be careful, if the values stored in the hash table
+ * are pointers, @p val_p must be a pointer to a pointer.
+ * @param[in] hash Hash of the stored value.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOTFOUND if value was not found.
+ */
+LY_ERR lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash);
+
+/**
+ * @brief Remove a value from a hash table. Same functionality as ::lyht_remove()
+ * but allows to specify a temporary val equal callback to be used in case the hash table
+ * will be resized after successful removal.
+ *
+ * @param[in] ht Hash table to remove from.
+ * @param[in] val_p Pointer to value to be removed. Be careful, if the values stored in the hash table
+ * are pointers, @p val_p must be a pointer to a pointer.
+ * @param[in] hash Hash of the stored value.
+ * @param[in] resize_val_equal Val equal callback to use for resizing.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOTFOUND if value was not found.
+ */
+LY_ERR lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal);
+
+/**
+ * @brief Get suitable size of a hash table for a fixed number of items.
+ *
+ * @param[in] item_count Number of stored items.
+ * @return Hash table size.
+ */
+uint32_t lyht_get_fixed_size(uint32_t item_count);
+
+#endif /* LY_HASH_TABLE_H_ */
diff --git a/src/in.c b/src/in.c
new file mode 100644
index 0000000..431c10a
--- /dev/null
+++ b/src/in.c
@@ -0,0 +1,329 @@
+/**
+ * @file in.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang input functions.
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
+
+#include "in.h"
+#include "in_internal.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "compat.h"
+#include "dict.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+LIBYANG_API_DEF LY_IN_TYPE
+ly_in_type(const struct ly_in *in)
+{
+ LY_CHECK_ARG_RET(NULL, in, LY_IN_ERROR);
+ return in->type;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_reset(struct ly_in *in)
+{
+ LY_CHECK_ARG_RET(NULL, in, LY_EINVAL);
+
+ in->current = in->func_start = in->start;
+ in->line = 1;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_new_fd(int fd, struct ly_in **in)
+{
+ size_t length;
+ char *addr;
+
+ LY_CHECK_ARG_RET(NULL, fd >= 0, in, LY_EINVAL);
+
+ LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr));
+ if (!addr) {
+ LOGERR(NULL, LY_EINVAL, "Empty input file.");
+ return LY_EINVAL;
+ }
+
+ *in = calloc(1, sizeof **in);
+ LY_CHECK_ERR_RET(!*in, LOGMEM(NULL); ly_munmap(addr, length), LY_EMEM);
+
+ (*in)->type = LY_IN_FD;
+ (*in)->method.fd = fd;
+ (*in)->current = (*in)->start = (*in)->func_start = addr;
+ (*in)->line = 1;
+ (*in)->length = length;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF int
+ly_in_fd(struct ly_in *in, int fd)
+{
+ int prev_fd;
+ size_t length;
+ const char *addr;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FD, -1);
+
+ prev_fd = in->method.fd;
+
+ if (fd != -1) {
+ LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr), -1);
+ if (!addr) {
+ LOGERR(NULL, LY_EINVAL, "Empty input file.");
+ return -1;
+ }
+
+ ly_munmap((char *)in->start, in->length);
+
+ in->method.fd = fd;
+ in->current = in->start = addr;
+ in->line = 1;
+ in->length = length;
+ }
+
+ return prev_fd;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_new_file(FILE *f, struct ly_in **in)
+{
+ LY_CHECK_ARG_RET(NULL, f, in, LY_EINVAL);
+
+ LY_CHECK_RET(ly_in_new_fd(fileno(f), in));
+
+ /* convert the LY_IN_FD input handler into the LY_IN_FILE */
+ (*in)->type = LY_IN_FILE;
+ (*in)->method.f = f;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF FILE *
+ly_in_file(struct ly_in *in, FILE *f)
+{
+ FILE *prev_f;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILE, NULL);
+
+ prev_f = in->method.f;
+
+ if (f) {
+ /* convert LY_IN_FILE handler into LY_IN_FD to be able to update it via ly_in_fd() */
+ in->type = LY_IN_FD;
+ in->method.fd = fileno(prev_f);
+ if (ly_in_fd(in, fileno(f)) == -1) {
+ in->type = LY_IN_FILE;
+ in->method.f = prev_f;
+ return NULL;
+ }
+
+ /* if success, convert the result back */
+ in->type = LY_IN_FILE;
+ in->method.f = f;
+ }
+
+ return prev_f;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_new_memory(const char *str, struct ly_in **in)
+{
+ LY_CHECK_ARG_RET(NULL, str, in, LY_EINVAL);
+
+ *in = calloc(1, sizeof **in);
+ LY_CHECK_ERR_RET(!*in, LOGMEM(NULL), LY_EMEM);
+
+ (*in)->type = LY_IN_MEMORY;
+ (*in)->start = (*in)->current = (*in)->func_start = str;
+ (*in)->line = 1;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const char *
+ly_in_memory(struct ly_in *in, const char *str)
+{
+ const char *data;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_MEMORY, NULL);
+
+ data = in->current;
+
+ if (str) {
+ in->start = in->current = str;
+ in->line = 1;
+ }
+
+ return data;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in)
+{
+ LY_ERR ret;
+ char *fp;
+ int fd;
+
+ LY_CHECK_ARG_RET(NULL, filepath, in, LY_EINVAL);
+
+ if (len) {
+ fp = strndup(filepath, len);
+ } else {
+ fp = strdup(filepath);
+ }
+
+ fd = open(fp, O_RDONLY);
+ LY_CHECK_ERR_RET(fd == -1, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp),
+ LY_ESYS);
+
+ LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, in), free(fp), ret);
+
+ /* convert the LY_IN_FD input handler into the LY_IN_FILE */
+ (*in)->type = LY_IN_FILEPATH;
+ (*in)->method.fpath.fd = fd;
+ (*in)->method.fpath.filepath = fp;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const char *
+ly_in_filepath(struct ly_in *in, const char *filepath, size_t len)
+{
+ int fd, prev_fd;
+ char *fp = NULL;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILEPATH, filepath ? NULL : ((void *)-1));
+
+ if (!filepath) {
+ return in->method.fpath.filepath;
+ }
+
+ if (len) {
+ fp = strndup(filepath, len);
+ } else {
+ fp = strdup(filepath);
+ }
+
+ /* replace filepath */
+ fd = open(fp, O_RDONLY);
+ LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), NULL);
+
+ /* convert LY_IN_FILEPATH handler into LY_IN_FD to be able to update it via ly_in_fd() */
+ in->type = LY_IN_FD;
+ prev_fd = ly_in_fd(in, fd);
+ LY_CHECK_ERR_RET(prev_fd == -1, in->type = LY_IN_FILEPATH; free(fp), NULL);
+
+ /* and convert the result back */
+ in->type = LY_IN_FILEPATH;
+ close(prev_fd);
+ free(in->method.fpath.filepath);
+ in->method.fpath.fd = fd;
+ in->method.fpath.filepath = fp;
+
+ return NULL;
+}
+
+LIBYANG_API_DEF size_t
+ly_in_parsed(const struct ly_in *in)
+{
+ LY_CHECK_ARG_RET(NULL, in, 0);
+
+ return in->current - in->func_start;
+}
+
+LIBYANG_API_DEF void
+ly_in_free(struct ly_in *in, ly_bool destroy)
+{
+ if (!in) {
+ return;
+ } else if (in->type == LY_IN_ERROR) {
+ LOGINT(NULL);
+ return;
+ }
+
+ if (destroy) {
+ if (in->type == LY_IN_MEMORY) {
+ free((char *)in->start);
+ } else {
+ ly_munmap((char *)in->start, in->length);
+
+ if (in->type == LY_IN_FILE) {
+ fclose(in->method.f);
+ } else {
+ close(in->method.fd);
+
+ if (in->type == LY_IN_FILEPATH) {
+ free(in->method.fpath.filepath);
+ }
+ }
+ }
+ } else if (in->type != LY_IN_MEMORY) {
+ ly_munmap((char *)in->start, in->length);
+
+ if (in->type == LY_IN_FILEPATH) {
+ close(in->method.fpath.fd);
+ free(in->method.fpath.filepath);
+ }
+ }
+
+ free(in);
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_read(struct ly_in *in, void *buf, size_t count)
+{
+ LY_CHECK_ARG_RET(NULL, in, buf, LY_EINVAL);
+
+ if (in->length && (in->length - (in->current - in->start) < count)) {
+ /* EOF */
+ return LY_EDENIED;
+ }
+
+ if (count) {
+ memcpy(buf, in->current, count);
+ }
+ in->current += count;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_in_skip(struct ly_in *in, size_t count)
+{
+ LY_CHECK_ARG_RET(NULL, in, LY_EINVAL);
+
+ if (in->length && (in->length - (in->current - in->start) < count)) {
+ /* EOF */
+ return LY_EDENIED;
+ }
+
+ in->current += count;
+ return LY_SUCCESS;
+}
diff --git a/src/in.h b/src/in.h
new file mode 100644
index 0000000..62f5aae
--- /dev/null
+++ b/src/in.h
@@ -0,0 +1,252 @@
+/**
+ * @file in.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang input structures and functions
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_IN_H_
+#define LY_IN_H_
+
+#include <stdio.h>
+
+#include "log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page howtoInput Input Processing
+ *
+ * libyang provides a mechanism to generalize work with the inputs (and [outputs](@ref howtoOutput)) of
+ * the different types. The ::ly_in handler can be created providing necessary information connected with the specific
+ * input type and then used throughout the parser functions processing the input data. Using a generic input handler avoids
+ * need to have a set of functions for each parser functionality and results in simpler API.
+ *
+ * The API allows to alter the source of the data behind the handler by another source. Also resetting a seekable source
+ * input is possible with ::ly_in_reset() to re-read the input.
+ *
+ * @note
+ * Currently, libyang supports only reading data from standard (disk) file, not from sockets, pipes, etc. The reason is
+ * that the parsers expects all the data to be present in the file (input data are complete). In future, we would like
+ * to change the internal mechanism and support sequential processing of the input data. In XML wording - we have DOM
+ * parser, but in future we would like to move to something like a SAX parser.
+ *
+ * @note
+ * This mechanism was introduced in libyang 2.0. To simplify transition from libyang 1.0 to version 2.0 and also for
+ * some simple use case where using the input handler would be an overkill, there are some basic parsers functions
+ * that do not require input handler. But remember, that functionality of these function can be limited in particular cases
+ * in contrast to the functions using input handlers.
+ *
+ * Functions List
+ * --------------
+ * - ::ly_in_new_fd()
+ * - ::ly_in_new_file()
+ * - ::ly_in_new_filepath()
+ * - ::ly_in_new_memory()
+ *
+ * - ::ly_in_fd()
+ * - ::ly_in_file()
+ * - ::ly_in_filepath()
+ * - ::ly_in_memory()
+ *
+ * - ::ly_in_type()
+ * - ::ly_in_parsed()
+ *
+ * - ::ly_in_reset()
+ * - ::ly_in_free()
+ *
+ * libyang Parsers List
+ * --------------------
+ * - @subpage howtoSchemaParsers
+ * - @subpage howtoDataParsers
+ */
+
+/**
+ * @struct ly_in
+ * @brief Parser input structure specifying where the data are read.
+ */
+struct ly_in;
+
+/**
+ * @brief Types of the parser's inputs
+ */
+typedef enum LY_IN_TYPE {
+ LY_IN_ERROR = -1, /**< error value to indicate failure of the functions returning LY_IN_TYPE */
+ LY_IN_FD, /**< file descriptor printer */
+ LY_IN_FILE, /**< FILE stream parser */
+ LY_IN_FILEPATH, /**< filepath parser */
+ LY_IN_MEMORY /**< memory parser */
+} LY_IN_TYPE;
+
+/**
+ * @brief Get input type of the input handler.
+ *
+ * @param[in] in Input handler.
+ * @return Type of the parser's input.
+ */
+LIBYANG_API_DECL LY_IN_TYPE ly_in_type(const struct ly_in *in);
+
+/**
+ * @brief Reset the input medium to read from its beginning, so the following parser function will read from the object's beginning.
+ *
+ * Note that in case the underlying output is not seekable (stream referring a pipe/FIFO/socket or the callback output type),
+ * nothing actually happens despite the function succeeds. Also note that the medium is not returned to the state it was when
+ * the handler was created. For example, file is seeked into the offset zero, not to the offset where it was opened when
+ * ::ly_in_new_file() was called.
+ *
+ * @param[in] in Input handler.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ESYS in case of failure
+ */
+LIBYANG_API_DECL LY_ERR ly_in_reset(struct ly_in *in);
+
+/**
+ * @brief Create input handler using file descriptor.
+ *
+ * @param[in] fd File descriptor to use.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_in_new_fd(int fd, struct ly_in **in);
+
+/**
+ * @brief Get or reset file descriptor input handler.
+ *
+ * @param[in] in Input handler.
+ * @param[in] fd Optional value of a new file descriptor for the handler. If -1, only the current file descriptor value is returned.
+ * @return Previous value of the file descriptor. Note that caller is responsible for closing the returned file descriptor in case of setting new descriptor @p fd.
+ * @return -1 in case of error when setting up the new file descriptor.
+ */
+LIBYANG_API_DECL int ly_in_fd(struct ly_in *in, int fd);
+
+/**
+ * @brief Create input handler using file stream.
+ *
+ * @param[in] f File stream to use.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_in_new_file(FILE *f, struct ly_in **in);
+
+/**
+ * @brief Get or reset file stream input handler.
+ *
+ * @param[in] in Input handler.
+ * @param[in] f Optional new file stream for the handler. If NULL, only the current file stream is returned.
+ * @return NULL in case of invalid argument or an error when setting up the new input file, original input handler @p in is untouched in this case.
+ * @return Previous file stream of the handler. Note that caller is responsible for closing the returned stream in case of setting new stream @p f.
+ */
+LIBYANG_API_DECL FILE *ly_in_file(struct ly_in *in, FILE *f);
+
+/**
+ * @brief Create input handler using memory to read data.
+ *
+ * @param[in] str Pointer where to start reading data. The input data are expected to be NULL-terminated.
+ * Note that in case the destroy argument of ::ly_in_free() is used, the input string is passed to free(),
+ * so if it is really a static string, do not use the destroy argument!
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_in_new_memory(const char *str, struct ly_in **in);
+
+/**
+ * @brief Get or change memory where the data are read from.
+ *
+ * @param[in] in Input handler.
+ * @param[in] str String containing the data to read. The input data are expected to be NULL-terminated.
+ * Note that in case the destroy argument of ::ly_in_free() is used, the input string is passed to free(),
+ * so if it is really a static string, do not use the destroy argument!
+ * @return Previous starting address to read data from. Note that the caller is responsible to free
+ * the data in case of changing string pointer @p str.
+ */
+LIBYANG_API_DECL const char *ly_in_memory(struct ly_in *in, const char *str);
+
+/**
+ * @brief Create input handler file of the given filename.
+ *
+ * @param[in] filepath Path of the file where to read data.
+ * @param[in] len Optional number of bytes to use from @p filepath. If 0, the @p filepath is considered to be NULL-terminated and
+ * the whole string is taken into account.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in);
+
+/**
+ * @brief Get or change the filepath of the file where the parser reads the data.
+ *
+ * Note that in case of changing the filepath, the current file is closed and a new one is
+ * created/opened instead of renaming the previous file. Also note that the previous filepath
+ * string is returned only in case of not changing it's value.
+ *
+ * @param[in] in Input handler.
+ * @param[in] filepath Optional new filepath for the handler. If and only if NULL, the current filepath string is returned.
+ * @param[in] len Optional number of bytes to use from @p filepath. If 0, the @p filepath is considered to be NULL-terminated and
+ * the whole string is taken into account.
+ * @return Previous filepath string in case the @p filepath argument is NULL.
+ * @return NULL if changing filepath succeedes and ((void *)-1) otherwise.
+ */
+LIBYANG_API_DECL const char *ly_in_filepath(struct ly_in *in, const char *filepath, size_t len);
+
+/**
+ * @brief Get the number of parsed bytes by the last function.
+ *
+ * @param[in] in In structure used.
+ * @return Number of parsed bytes.
+ */
+LIBYANG_API_DECL size_t ly_in_parsed(const struct ly_in *in);
+
+/**
+ * @brief Free the input handler.
+ *
+ * @param[in] in Input handler to free.
+ * @param[in] destroy Flag to free the input data buffer (for LY_IN_MEMORY) or to
+ * close stream/file descriptor (for LY_IN_FD and LY_IN_FILE)
+ */
+LIBYANG_API_DECL void ly_in_free(struct ly_in *in, ly_bool destroy);
+
+/**
+ * @brief Read bytes from an input.
+ *
+ * Does not count new lines, which is expected from the caller who has better idea about
+ * the content of the read data and can better optimize counting.
+ *
+ * @param[in] in Input structure.
+ * @param[in] buf Destination buffer.
+ * @param[in] count Number of bytes to read.
+ * @return LY_SUCCESS on success,
+ * @return LY_EDENIED on EOF.
+ */
+LIBYANG_API_DECL LY_ERR ly_in_read(struct ly_in *in, void *buf, size_t count);
+
+/**
+ * @brief Just skip bytes in an input.
+ *
+ * Does not count new lines, which is expected from the caller who has better idea about
+ * the content of the skipped data and can better optimize counting.
+ *
+ * @param[in] in Input structure.
+ * @param[in] count Number of bytes to skip.
+ * @return LY_SUCCESS on success,
+ * @return LY_EDENIED on EOF.
+ */
+LIBYANG_API_DECL LY_ERR ly_in_skip(struct ly_in *in, size_t count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_IN_H_ */
diff --git a/src/in_internal.h b/src/in_internal.h
new file mode 100644
index 0000000..cbc56da
--- /dev/null
+++ b/src/in_internal.h
@@ -0,0 +1,50 @@
+/**
+ * @file in_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Internal structures and functions for libyang parsers
+ *
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_IN_INTERNAL_H_
+#define LY_IN_INTERNAL_H_
+
+#include "in.h"
+
+/**
+ * @brief Parser input structure specifying where the data are read.
+ */
+struct ly_in {
+ LY_IN_TYPE type; /**< type of the output to select the output method */
+ const char *current; /**< Current position in the input data */
+ const char *func_start; /**< Input data position when the last parser function was executed */
+ const char *start; /**< Input data start */
+ size_t length; /**< mmap() length (if used) */
+
+ union {
+ int fd; /**< file descriptor for LY_IN_FD type */
+ FILE *f; /**< file structure for LY_IN_FILE and LY_IN_FILEPATH types */
+
+ struct {
+ int fd; /**< file descriptor for LY_IN_FILEPATH */
+ char *filepath; /**< stored original filepath */
+ } fpath; /**< filepath structure for LY_IN_FILEPATH */
+ } method; /**< type-specific information about the output */
+ uint64_t line; /**< current line of the input */
+};
+
+/**
+ * @brief Increment line counter.
+ * @param[in] IN The input handler.
+ */
+#define LY_IN_NEW_LINE(IN) \
+ (IN)->line++
+
+#endif /* LY_IN_INTERNAL_H_ */
diff --git a/src/json.c b/src/json.c
new file mode 100644
index 0000000..5c45c8c
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,1047 @@
+/**
+ * @file json.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic JSON format parser for libyang
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "common.h"
+#include "in_internal.h"
+#include "json.h"
+#include "tree_schema_internal.h"
+
+const char *
+lyjson_token2str(enum LYJSON_PARSER_STATUS status)
+{
+ switch (status) {
+ case LYJSON_ERROR:
+ return "error";
+ case LYJSON_ROOT:
+ return "document root";
+ case LYJSON_FALSE:
+ return "false";
+ case LYJSON_TRUE:
+ return "true";
+ case LYJSON_NULL:
+ return "null";
+ case LYJSON_OBJECT:
+ return "object";
+ case LYJSON_OBJECT_CLOSED:
+ return "object closed";
+ case LYJSON_OBJECT_EMPTY:
+ return "empty object";
+ case LYJSON_ARRAY:
+ return "array";
+ case LYJSON_ARRAY_CLOSED:
+ return "array closed";
+ case LYJSON_ARRAY_EMPTY:
+ return "empty array";
+ case LYJSON_NUMBER:
+ return "number";
+ case LYJSON_STRING:
+ return "string";
+ case LYJSON_END:
+ return "end of input";
+ }
+
+ return "";
+}
+
+static LY_ERR
+skip_ws(struct lyjson_ctx *jsonctx)
+{
+ /* skip leading whitespaces */
+ while (*jsonctx->in->current != '\0' && is_jsonws(*jsonctx->in->current)) {
+ if (*jsonctx->in->current == '\n') {
+ LY_IN_NEW_LINE(jsonctx->in);
+ }
+ ly_in_skip(jsonctx->in, 1);
+ }
+ if (*jsonctx->in->current == '\0') {
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_END);
+ }
+
+ return LY_SUCCESS;
+}
+
+/*
+ * @brief Set value corresponding to the current context's status
+ */
+static void
+lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
+{
+ assert(jsonctx);
+
+ if (jsonctx->dynamic) {
+ free((char *)jsonctx->value);
+ }
+ jsonctx->value = value;
+ jsonctx->value_len = value_len;
+ jsonctx->dynamic = dynamic;
+}
+
+static LY_ERR
+lyjson_check_next(struct lyjson_ctx *jsonctx)
+{
+ if (jsonctx->status.count == 1) {
+ /* top level value (JSON-text), ws expected */
+ if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
+ return LY_SUCCESS;
+ }
+ } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
+ LY_CHECK_RET(skip_ws(jsonctx));
+ if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
+ return LY_SUCCESS;
+ }
+ } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
+ LY_CHECK_RET(skip_ws(jsonctx));
+ if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
+ return LY_SUCCESS;
+ }
+ }
+
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
+ *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
+ return LY_EVALID;
+}
+
+/**
+ * Input is expected to start after the opening quotation-mark.
+ * When succeeds, input is moved after the closing quotation-mark.
+ */
+static LY_ERR
+lyjson_string_(struct lyjson_ctx *jsonctx)
+{
+#define BUFSIZE 24
+#define BUFSIZE_STEP 128
+
+ const char *in = jsonctx->in->current, *start;
+ char *buf = NULL;
+ size_t offset; /* read offset in input buffer */
+ size_t len; /* length of the output string (write offset in output buffer) */
+ size_t size = 0; /* size of the output buffer */
+ size_t u;
+ uint64_t start_line;
+
+ assert(jsonctx);
+
+ /* init */
+ start = in;
+ start_line = jsonctx->in->line;
+ offset = len = 0;
+
+ /* parse */
+ while (in[offset]) {
+ if (in[offset] == '\\') {
+ /* escape sequence */
+ const char *slash = &in[offset];
+ uint32_t value;
+ uint8_t i = 1;
+
+ if (!buf) {
+ /* prepare output buffer */
+ buf = malloc(BUFSIZE);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
+ size = BUFSIZE;
+ }
+
+ /* allocate enough for the offset and next character,
+ * we will need 4 bytes at most since we support only the predefined
+ * (one-char) entities and character references */
+ if (len + offset + 4 >= size) {
+ size_t increment;
+
+ for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {}
+ buf = ly_realloc(buf, size + increment);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
+ size += BUFSIZE_STEP;
+ }
+
+ if (offset) {
+ /* store what we have so far */
+ memcpy(&buf[len], in, offset);
+ len += offset;
+ in += offset;
+ offset = 0;
+ }
+
+ switch (in[++offset]) {
+ case '"':
+ /* quotation mark */
+ value = 0x22;
+ break;
+ case '\\':
+ /* reverse solidus */
+ value = 0x5c;
+ break;
+ case '/':
+ /* solidus */
+ value = 0x2f;
+ break;
+ case 'b':
+ /* backspace */
+ value = 0x08;
+ break;
+ case 'f':
+ /* form feed */
+ value = 0x0c;
+ break;
+ case 'n':
+ /* line feed */
+ value = 0x0a;
+ break;
+ case 'r':
+ /* carriage return */
+ value = 0x0d;
+ break;
+ case 't':
+ /* tab */
+ value = 0x09;
+ break;
+ case 'u':
+ /* Basic Multilingual Plane character \uXXXX */
+ offset++;
+ for (value = i = 0; i < 4; i++) {
+ if (!in[offset + i]) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash);
+ goto error;
+ } else if (isdigit(in[offset + i])) {
+ u = (in[offset + i] - '0');
+ } else if (in[offset + i] > 'F') {
+ u = LY_BASE_DEC + (in[offset + i] - 'a');
+ } else {
+ u = LY_BASE_DEC + (in[offset + i] - 'A');
+ }
+ value = (LY_BASE_HEX * value) + u;
+ }
+ break;
+ default:
+ /* invalid escape sequence */
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
+ goto error;
+
+ }
+
+ offset += i; /* add read escaped characters */
+ LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
+ (int)(&in[offset] - slash), slash, value),
+ error);
+ len += u; /* update number of bytes in buffer */
+ in += offset; /* move the input by the processed bytes stored in the buffer ... */
+ offset = 0; /* ... and reset the offset index for future moving data into buffer */
+
+ } else if (in[offset] == '"') {
+ /* end of string */
+ if (buf) {
+ /* realloc exact size string */
+ buf = ly_realloc(buf, len + offset + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
+ size = len + offset + 1;
+ if (offset) {
+ memcpy(&buf[len], in, offset);
+ }
+
+ /* set terminating NULL byte */
+ buf[len + offset] = '\0';
+ }
+ len += offset;
+ ++offset;
+ in += offset;
+ goto success;
+ } else {
+ /* get it as UTF-8 character for check */
+ const char *c = &in[offset];
+ uint32_t code = 0;
+ size_t code_len = 0;
+
+ LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
+ LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
+
+ LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
+ (int)(&in[offset] - start + code_len), start, code),
+ error);
+
+ /* character is ok, continue */
+ offset += code_len;
+ }
+ }
+
+ /* EOF reached before endchar */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ LOGVAL_LINE(jsonctx->ctx, start_line, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
+
+error:
+ free(buf);
+ return LY_EVALID;
+
+success:
+ jsonctx->in->current = in;
+ if (buf) {
+ lyjson_ctx_set_value(jsonctx, buf, len, 1);
+ } else {
+ lyjson_ctx_set_value(jsonctx, start, len, 0);
+ }
+
+ return LY_SUCCESS;
+
+#undef BUFSIZE
+#undef BUFSIZE_STEP
+}
+
+/*
+ *
+ * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
+ */
+static LY_ERR
+lyjson_string(struct lyjson_ctx *jsonctx)
+{
+ LY_CHECK_RET(lyjson_string_(jsonctx));
+
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
+ LY_CHECK_RET(lyjson_check_next(jsonctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Calculate how many @p c characters there are in a row.
+ *
+ * @param[in] str Count from this position.
+ * @param[in] end Position after the last checked character.
+ * @param[in] c Checked character.
+ * @param[in] backwards Set to 1, if to proceed from end-1 to str.
+ * @return Number of characters in a row.
+ */
+static uint32_t
+lyjson_count_in_row(const char *str, const char *end, char c, ly_bool backwards)
+{
+ uint32_t cnt;
+
+ assert(str && end);
+
+ if (str >= end) {
+ return 0;
+ }
+
+ if (!backwards) {
+ for (cnt = 0; (str != end) && (*str == c); ++str, ++cnt) {}
+ } else {
+ --end;
+ --str;
+ for (cnt = 0; (str != end) && (*end == c); --end, ++cnt) {}
+ }
+
+ return cnt;
+}
+
+/**
+ * @brief Check if the number can be shortened to zero.
+ *
+ * @param[in] in Start of input string;
+ * @param[in] end End of input string;
+ * @return 1 if number is zero, otherwise 0.
+ */
+static ly_bool
+lyjson_number_is_zero(const char *in, const char *end)
+{
+ assert(in < end);
+
+ if ((in[0] == '-') || (in[0] == '+')) {
+ in++;
+ assert(in < end);
+ }
+ if ((in[0] == '0') && (in[1] == '.')) {
+ in += 2;
+ if (!(in < end)) {
+ return 1;
+ }
+ }
+
+ return lyjson_count_in_row(in, end, '0', 0) == end - in;
+}
+
+/**
+ * @brief Allocate buffer for number in string format.
+ *
+ * @param[in] jsonctx JSON context.
+ * @param[in] num_len Required space in bytes for a number.
+ * Terminating null byte is added by default.
+ * @param[out] buffer Output allocated buffer.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **buffer)
+{
+ *buffer = NULL;
+
+ LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(ctx, LYVE_SEMANTICS,
+ "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
+
+ /* allocate buffer for the result (add NULL-byte) */
+ *buffer = malloc(num_len + 1);
+ LY_CHECK_ERR_RET(!(*buffer), LOGMEM(ctx), LY_EMEM);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Copy the 'numeric part' (@p num) except its decimal point
+ * (@p dec_point) and insert the new decimal point (@p dp_position)
+ * only if it is to be placed in the 'numeric part' range (@p num).
+ *
+ * @param[in] num Begin of the 'numeric part'.
+ * @param[in] num_len Length of the 'numeric part'.
+ * @param[in] dec_point Pointer to the old decimal point.
+ * If it has a NULL value, it is ignored.
+ * @param[in] dp_position Position of the new decimal point.
+ * If it has a negative value, it is ignored.
+ * @param[out] dst Memory into which the copied result is written.
+ * @return Number of characters written to the @p dst.
+ */
+static uint32_t
+lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
+ char *dec_point, int32_t dp_position, char *dst)
+{
+ int32_t dec_point_idx;
+ int32_t n, d;
+
+ assert(num && dst);
+
+ dec_point_idx = dec_point ? dec_point - num : INT32_MAX;
+ assert((dec_point_idx >= 0) && (dec_point_idx != dp_position));
+
+ for (n = 0, d = 0; (uint32_t)n < num_len; n++) {
+ if (n == dec_point_idx) {
+ continue;
+ } else if (d == dp_position) {
+ dst[d++] = '.';
+ dst[d++] = num[n];
+ } else {
+ dst[d++] = num[n];
+ }
+ }
+
+ return d;
+}
+
+/**
+ * @brief Convert JSON number with exponent into the representation
+ * used by YANG.
+ *
+ * The input numeric string must be syntactically valid. Also, before
+ * calling this function, checks should be performed using the
+ * ::lyjson_number_is_zero().
+ *
+ * @param[in] ctx Context for the error message.
+ * @param[in] in Beginning of the string containing the number.
+ * @param[in] exponent Pointer to the letter E/e.
+ * @param[in] total_len Total size of the input number.
+ * @param[out] res Conversion result.
+ * @param[out] res_len Length of the result.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent,
+ uint64_t total_len, char **res, size_t *res_len)
+{
+
+#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
+ if (FLAG) { \
+ ARRAY[INDEX++] = '-'; \
+ }
+
+/* Length of leading zero followed by the decimal point. */
+#define LEADING_ZERO 1
+
+/* Flags for the ::lyjson_count_in_row() */
+#define FORWARD 0
+#define BACKWARD 1
+
+ /* Buffer where the result is stored. */
+ char *buf;
+ /* Size without space for terminating NULL-byte. */
+ uint64_t buf_len;
+ /* Index to buf. */
+ uint32_t i = 0;
+ /* A 'numeric part' doesn't contain a minus sign or an leading zero.
+ * For example, in 0.45, there is the leading zero.
+ */
+ const char *num;
+ /* Length of the 'numeric part' ends before E/e. */
+ uint16_t num_len;
+ /* Position of decimal point in the num. */
+ char *dec_point;
+ /* Final position of decimal point in the buf. */
+ int32_t dp_position;
+ /* Exponent as integer. */
+ long long e_val;
+ /* Byte for the decimal point. */
+ int8_t dot;
+ /* Required additional byte for the minus sign. */
+ uint8_t minus;
+ /* The number of zeros. */
+ long zeros;
+ /* If the number starts with leading zero followed by the decimal point. */
+ ly_bool leading_zero;
+
+ assert(ctx && in && exponent && res && res_len && (total_len > 2));
+ assert((in < exponent) && ((*exponent == 'e') || (*exponent == 'E')));
+
+ if ((exponent - in) > UINT16_MAX) {
+ LOGVAL(ctx, LYVE_SEMANTICS, "JSON number is too long.");
+ return LY_EVALID;
+ }
+
+ /* Convert exponent. */
+ errno = 0;
+ e_val = strtoll(exponent + 1, NULL, LY_BASE_DEC);
+ if (errno || (e_val > UINT16_MAX) || (e_val < -UINT16_MAX)) {
+ LOGVAL(ctx, LYVE_SEMANTICS,
+ "Exponent out-of-bounds in a JSON Number value (%.*s).",
+ (int)total_len, in);
+ return LY_EVALID;
+ }
+
+ minus = in[0] == '-';
+ if (in[minus] == '0') {
+ assert(in[minus + 1] == '.');
+ leading_zero = 1;
+ /* The leading zero has been found, it will be skipped. */
+ num = &in[minus + 1];
+ } else {
+ leading_zero = 0;
+ /* Set to the first number. */
+ num = &in[minus];
+ }
+ num_len = exponent - num;
+
+ /* Find the location of the decimal points. */
+ dec_point = ly_strnchr(num, '.', num_len);
+ dp_position = dec_point ?
+ dec_point - num + e_val :
+ num_len + e_val;
+
+ /* Remove zeros after the decimal point from the end of
+ * the 'numeric part' because these are useless.
+ * (For example, in 40.001000 these are the last 3).
+ */
+ num_len -= dp_position > 0 ?
+ lyjson_count_in_row(num + dp_position - 1, exponent, '0', BACKWARD) :
+ lyjson_count_in_row(num, exponent, '0', BACKWARD);
+
+ /* Decide what to do with the dot from the 'numeric part'. */
+ if (dec_point && ((int32_t)(num_len - 1) == dp_position)) {
+ /* Decimal point in the last place is useless. */
+ dot = -1;
+ } else if (dec_point) {
+ /* Decimal point is shifted. */
+ dot = 0;
+ } else {
+ /* Additional byte for the decimal point is requred. */
+ dot = 1;
+ }
+
+ /* Final composition of the result. */
+ if (dp_position <= 0) {
+ /* Adding decimal point before the integer with adding additional zero(s). */
+
+ zeros = labs(dp_position);
+ buf_len = minus + LEADING_ZERO + dot + zeros + num_len;
+ LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+ MAYBE_WRITE_MINUS(buf, i, minus);
+ buf[i++] = '0';
+ buf[i++] = '.';
+ memset(buf + i, '0', zeros);
+ i += zeros;
+ dp_position = -1;
+ lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
+ } else if (leading_zero && (dp_position < (ssize_t)num_len)) {
+ /* Insert decimal point between the integer's digits. */
+
+ /* Set a new range of 'numeric part'. Old decimal point is skipped. */
+ num++;
+ num_len--;
+ dp_position--;
+ /* Get the number of useless zeros between the old
+ * and new decimal point. For example, in the number 0.005E1,
+ * there is one useless zero.
+ */
+ zeros = lyjson_count_in_row(num, num + dp_position + 1, '0', FORWARD);
+ /* If the new decimal point will be in the place of the first non-zero subnumber. */
+ if (zeros == (dp_position + 1)) {
+ /* keep one zero as leading zero */
+ zeros--;
+ /* new decimal point will be behind the leading zero */
+ dp_position = 1;
+ dot = 1;
+ } else {
+ dot = 0;
+ }
+ buf_len = minus + dot + (num_len - zeros);
+ LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+ MAYBE_WRITE_MINUS(buf, i, minus);
+ /* Skip useless zeros and copy. */
+ lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
+ } else if (dp_position < (ssize_t)num_len) {
+ /* Insert decimal point between the integer's digits. */
+
+ buf_len = minus + dot + num_len;
+ LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+ MAYBE_WRITE_MINUS(buf, i, minus);
+ lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
+ } else if (leading_zero) {
+ /* Adding decimal point after the decimal value make the integer result. */
+
+ /* Set a new range of 'numeric part'. Old decimal point is skipped. */
+ num++;
+ num_len--;
+ /* Get the number of useless zeros. */
+ zeros = lyjson_count_in_row(num, num + num_len, '0', FORWARD);
+ buf_len = minus + dp_position - zeros;
+ LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+ MAYBE_WRITE_MINUS(buf, i, minus);
+ /* Skip useless zeros and copy. */
+ i += lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
+ /* Add multiples of ten behind the 'numeric part'. */
+ memset(buf + i, '0', buf_len - i);
+ } else {
+ /* Adding decimal point after the decimal value make the integer result. */
+
+ buf_len = minus + dp_position;
+ LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+ MAYBE_WRITE_MINUS(buf, i, minus);
+ i += lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
+ /* Add multiples of ten behind the 'numeric part'. */
+ memset(buf + i, '0', buf_len - i);
+ }
+
+ buf[buf_len] = '\0';
+ *res = buf;
+ *res_len = buf_len;
+
+#undef MAYBE_WRITE_MINUS
+#undef LEADING_ZERO
+#undef FORWARD
+#undef BACKWARD
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyjson_number(struct lyjson_ctx *jsonctx)
+{
+ size_t offset = 0, num_len;
+ const char *in = jsonctx->in->current, *exponent = NULL;
+ uint8_t minus = 0;
+ char *num;
+
+ if (in[offset] == '-') {
+ ++offset;
+ minus = 1;
+ }
+
+ if (in[offset] == '0') {
+ ++offset;
+ } else if (isdigit(in[offset])) {
+ ++offset;
+ while (isdigit(in[offset])) {
+ ++offset;
+ }
+ } else {
+invalid_character:
+ if (in[offset]) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
+ } else {
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ }
+ return LY_EVALID;
+ }
+
+ if (in[offset] == '.') {
+ ++offset;
+ if (!isdigit(in[offset])) {
+ goto invalid_character;
+ }
+ while (isdigit(in[offset])) {
+ ++offset;
+ }
+ }
+
+ if ((in[offset] == 'e') || (in[offset] == 'E')) {
+ exponent = &in[offset];
+ ++offset;
+ if ((in[offset] == '+') || (in[offset] == '-')) {
+ ++offset;
+ }
+ if (!isdigit(in[offset])) {
+ goto invalid_character;
+ }
+ while (isdigit(in[offset])) {
+ ++offset;
+ }
+ }
+
+ if (lyjson_number_is_zero(in, exponent ? exponent : &in[offset])) {
+ lyjson_ctx_set_value(jsonctx, in, minus + 1, 0);
+ } else if (exponent && lyjson_number_is_zero(exponent + 1, &in[offset])) {
+ lyjson_ctx_set_value(jsonctx, in, exponent - in, 0);
+ } else if (exponent) {
+ LY_CHECK_RET(lyjson_exp_number(jsonctx->ctx, in, exponent, offset, &num, &num_len));
+ lyjson_ctx_set_value(jsonctx, num, num_len, 1);
+ } else {
+ if (offset > LY_NUMBER_MAXLEN) {
+ LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
+ "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.");
+ return LY_EVALID;
+ }
+ lyjson_ctx_set_value(jsonctx, in, offset, 0);
+ }
+ ly_in_skip(jsonctx->in, offset);
+
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
+ LY_CHECK_RET(lyjson_check_next(jsonctx));
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyjson_object_name(struct lyjson_ctx *jsonctx)
+{
+ if (*jsonctx->in->current != '"') {
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON object's member");
+ return LY_EVALID;
+ }
+ ly_in_skip(jsonctx->in, 1);
+
+ LY_CHECK_RET(lyjson_string_(jsonctx));
+ LY_CHECK_RET(skip_ws(jsonctx));
+ if (*jsonctx->in->current != ':') {
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
+ "a JSON object's name-separator ':'");
+ return LY_EVALID;
+ }
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(skip_ws(jsonctx));
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyjson_object(struct lyjson_ctx *jsonctx)
+{
+ LY_CHECK_RET(skip_ws(jsonctx));
+
+ if (*jsonctx->in->current == '}') {
+ assert(jsonctx->depth);
+ jsonctx->depth--;
+ /* empty object */
+ ly_in_skip(jsonctx->in, 1);
+ lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_EMPTY);
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(lyjson_object_name(jsonctx));
+
+ /* output data are set by lyjson_string_() */
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Process JSON array envelope
+ *
+ * @param[in] jsonctx JSON parser context
+ * @return LY_SUCCESS or LY_EMEM
+ */
+static LY_ERR
+lyjson_array(struct lyjson_ctx *jsonctx)
+{
+ LY_CHECK_RET(skip_ws(jsonctx));
+
+ if (*jsonctx->in->current == ']') {
+ /* empty array */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_EMPTY);
+ } else {
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
+ }
+
+ /* erase previous values, array has no value on its own */
+ lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyjson_value(struct lyjson_ctx *jsonctx)
+{
+ if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
+ return LY_SUCCESS;
+ }
+
+ if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
+ /* false */
+ lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
+ ly_in_skip(jsonctx->in, ly_strlen_const("false"));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE);
+ LY_CHECK_RET(lyjson_check_next(jsonctx));
+
+ } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
+ /* true */
+ lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
+ ly_in_skip(jsonctx->in, ly_strlen_const("true"));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
+ LY_CHECK_RET(lyjson_check_next(jsonctx));
+
+ } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
+ /* none */
+ lyjson_ctx_set_value(jsonctx, "", 0, 0);
+ ly_in_skip(jsonctx->in, ly_strlen_const("null"));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
+ LY_CHECK_RET(lyjson_check_next(jsonctx));
+
+ } else if (*jsonctx->in->current == '"') {
+ /* string */
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(lyjson_string(jsonctx));
+
+ } else if (*jsonctx->in->current == '[') {
+ /* array */
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(lyjson_array(jsonctx));
+
+ } else if (*jsonctx->in->current == '{') {
+ jsonctx->depth++;
+ if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) {
+ LOGERR(jsonctx->ctx, LY_EINVAL,
+ "The maximum number of block nestings has been exceeded.");
+ return LY_EINVAL;
+ }
+ /* object */
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(lyjson_object(jsonctx));
+
+ } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
+ /* number */
+ LY_CHECK_RET(lyjson_number(jsonctx));
+
+ } else {
+ /* unexpected value */
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON value");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyjson_ctx *jsonctx;
+
+ assert(ctx);
+ assert(in);
+ assert(jsonctx_p);
+
+ /* new context */
+ jsonctx = calloc(1, sizeof *jsonctx);
+ LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
+ jsonctx->ctx = ctx;
+ jsonctx->in = in;
+
+ LOG_LOCSET(NULL, NULL, NULL, in);
+
+ /* parse JSON value, if any */
+ LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
+ if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
+ /* empty data input */
+ goto cleanup;
+ }
+
+ if (subtree) {
+ ret = lyjson_object(jsonctx);
+ jsonctx->depth++;
+ } else {
+ ret = lyjson_value(jsonctx);
+ }
+ if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ ret = LY_EVALID;
+ }
+
+cleanup:
+ if (ret) {
+ lyjson_ctx_free(jsonctx);
+ } else {
+ *jsonctx_p = jsonctx;
+ }
+ return ret;
+}
+
+void
+lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
+{
+ if (jsonctx->backup.dynamic) {
+ free((char *)jsonctx->backup.value);
+ }
+ jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
+ jsonctx->backup.status_count = jsonctx->status.count;
+ jsonctx->backup.value = jsonctx->value;
+ jsonctx->backup.value_len = jsonctx->value_len;
+ jsonctx->backup.input = jsonctx->in->current;
+ jsonctx->backup.dynamic = jsonctx->dynamic;
+ jsonctx->backup.depth = jsonctx->depth;
+ jsonctx->dynamic = 0;
+}
+
+void
+lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
+{
+ if (jsonctx->dynamic) {
+ free((char *)jsonctx->value);
+ }
+ jsonctx->status.count = jsonctx->backup.status_count;
+ jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
+ jsonctx->value = jsonctx->backup.value;
+ jsonctx->value_len = jsonctx->backup.value_len;
+ jsonctx->in->current = jsonctx->backup.input;
+ jsonctx->dynamic = jsonctx->backup.dynamic;
+ jsonctx->depth = jsonctx->backup.depth;
+ jsonctx->backup.dynamic = 0;
+}
+
+LY_ERR
+lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
+{
+ LY_ERR ret = LY_SUCCESS;
+ ly_bool toplevel = 0;
+ enum LYJSON_PARSER_STATUS prev;
+
+ assert(jsonctx);
+
+ prev = lyjson_ctx_status(jsonctx, 0);
+
+ if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
+ /* get value for the object's member OR the first value in the array */
+ ret = lyjson_value(jsonctx);
+ goto result;
+ } else {
+ /* the previous token is closed and should be completely processed */
+ LYJSON_STATUS_POP_RET(jsonctx);
+ prev = lyjson_ctx_status(jsonctx, 0);
+ }
+
+ if (!jsonctx->status.count) {
+ /* we are done with the top level value */
+ toplevel = 1;
+ }
+ LY_CHECK_RET(skip_ws(jsonctx));
+ if (toplevel && !jsonctx->status.count) {
+ /* EOF expected, but there are some data after the top level token */
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value.");
+ return LY_EVALID;
+ }
+
+ if (toplevel) {
+ /* we are done */
+ goto result;
+ }
+
+ /* continue with the next token */
+ assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
+
+ if (*jsonctx->in->current == ',') {
+ /* sibling item in the ... */
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(skip_ws(jsonctx));
+
+ if (prev == LYJSON_OBJECT) {
+ /* ... object - get another object's member */
+ ret = lyjson_object_name(jsonctx);
+ } else { /* LYJSON_ARRAY */
+ /* ... array - get another complete value */
+ ret = lyjson_value(jsonctx);
+ }
+ } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) ||
+ ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
+ if (*jsonctx->in->current == '}') {
+ assert(jsonctx->depth);
+ jsonctx->depth--;
+ }
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_POP_RET(jsonctx);
+ LYJSON_STATUS_PUSH_RET(jsonctx, prev + 1);
+ } else {
+ /* unexpected value */
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
+ prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
+ return LY_EVALID;
+ }
+
+result:
+ if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ ret = LY_EVALID;
+ }
+
+ if ((ret == LY_SUCCESS) && status) {
+ *status = lyjson_ctx_status(jsonctx, 0);
+ }
+
+ return ret;
+}
+
+enum LYJSON_PARSER_STATUS
+lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
+{
+ assert(jsonctx);
+
+ if (jsonctx->status.count < index) {
+ return LYJSON_ERROR;
+ } else if (jsonctx->status.count == index) {
+ return LYJSON_ROOT;
+ } else {
+ return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
+ }
+}
+
+void
+lyjson_ctx_free(struct lyjson_ctx *jsonctx)
+{
+ if (!jsonctx) {
+ return;
+ }
+
+ LOG_LOCBACK(0, 0, 0, 1);
+
+ if (jsonctx->dynamic) {
+ free((char *)jsonctx->value);
+ }
+ if (jsonctx->backup.dynamic) {
+ free((char *)jsonctx->backup.value);
+ }
+
+ ly_set_erase(&jsonctx->status, NULL);
+
+ free(jsonctx);
+}
diff --git a/src/json.h b/src/json.h
new file mode 100644
index 0000000..53efe2a
--- /dev/null
+++ b/src/json.h
@@ -0,0 +1,139 @@
+/**
+ * @file json.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic JSON format parser routines.
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_JSON_H_
+#define LY_JSON_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "set.h"
+
+struct ly_ctx;
+struct ly_in;
+
+/* Macro to test if character is whitespace */
+#define is_jsonws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
+
+/* Macro to test if character is valid string character */
+#define is_jsonstrchar(c) (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x10ffff))
+
+/* Macro to push JSON parser status */
+#define LYJSON_STATUS_PUSH_RET(CTX, STATUS) \
+ LY_CHECK_RET(ly_set_add(&CTX->status, (void *)(uintptr_t)(STATUS), 1, NULL))
+
+/* Macro to pop JSON parser status */
+#define LYJSON_STATUS_POP_RET(CTX) \
+ assert(CTX->status.count); CTX->status.count--;
+
+/**
+ * @brief Status of the parser providing information what is expected next (which function is supposed to be called).
+ */
+enum LYJSON_PARSER_STATUS {
+ LYJSON_ERROR, /* JSON parser error - value is used as an error return code */
+ LYJSON_ROOT, /* JSON document root, used internally */
+ LYJSON_OBJECT, /* JSON object */
+ LYJSON_OBJECT_CLOSED, /* JSON object closed */
+ LYJSON_OBJECT_EMPTY, /* empty JSON object { } */
+ LYJSON_ARRAY, /* JSON array */
+ LYJSON_ARRAY_CLOSED, /* JSON array closed */
+ LYJSON_ARRAY_EMPTY, /* empty JSON array */
+ LYJSON_NUMBER, /* JSON number value */
+ LYJSON_STRING, /* JSON string value */
+ LYJSON_FALSE, /* JSON false value */
+ LYJSON_TRUE, /* JSON true value */
+ LYJSON_NULL, /* JSON null value */
+ LYJSON_END /* end of input data */
+};
+
+struct lyjson_ctx {
+ const struct ly_ctx *ctx;
+ struct ly_in *in; /* input structure */
+
+ struct ly_set status; /* stack of ::LYJSON_PARSER_STATUS values corresponding to the JSON items being processed */
+
+ const char *value; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
+ size_t value_len; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
+ ly_bool dynamic; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
+ uint32_t depth; /* current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */
+
+ struct {
+ enum LYJSON_PARSER_STATUS status;
+ uint32_t status_count;
+ const char *value;
+ size_t value_len;
+ ly_bool dynamic;
+ uint32_t depth;
+ const char *input;
+ } backup;
+};
+
+/**
+ * @brief Create a new JSON parser context and start parsing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] in JSON string data to parse.
+ * @param[in] subtree Whether this is a special case of parsing a subtree (starting with object name).
+ * @param[out] jsonctx New JSON context with status referring the parsed value.
+ * @return LY_ERR value.
+ */
+LY_ERR lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx);
+
+/**
+ * @brief Get status of the parser as the last/previous parsed token
+ *
+ * @param[in] jsonctx JSON context to check.
+ * @param[in] index Index of the token, starting by 0 for the last token
+ * @return ::LYJSON_ERROR in case of invalid index, other ::LYJSON_PARSER_STATUS corresponding to the token.
+ */
+enum LYJSON_PARSER_STATUS lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index);
+
+/**
+ * @brief Get string representation of the JSON context status (token).
+ *
+ * @param[in] status Context status (aka JSON token)
+ * @return String representation of the @p status.
+ */
+const char *lyjson_token2str(enum LYJSON_PARSER_STATUS status);
+
+/**
+ * @brief Move to the next JSON artifact and update parser status.
+ *
+ * @param[in] jsonctx XML context to move.
+ * @param[out] status Optional parameter to provide new parser status
+ * @return LY_ERR value.
+ */
+LY_ERR lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status);
+
+/**
+ * @brief Backup the JSON parser context's state To restore the backup, use ::lyjson_ctx_restore().
+ * @param[in] jsonctx JSON parser context to backup.
+ */
+void lyjson_ctx_backup(struct lyjson_ctx *jsonctx);
+
+/**
+ * @brief REstore the JSON parser context's state from the backup created by ::lyjson_ctx_backup().
+ * @param[in] jsonctx JSON parser context to restore.
+ */
+void lyjson_ctx_restore(struct lyjson_ctx *jsonctx);
+
+/**
+ * @brief Remove the allocated working memory of the context.
+ *
+ * @param[in] jsonctx JSON context to clear.
+ */
+void lyjson_ctx_free(struct lyjson_ctx *jsonctx);
+
+#endif /* LY_JSON_H_ */
diff --git a/src/libyang.h b/src/libyang.h
new file mode 100644
index 0000000..2bfc6be
--- /dev/null
+++ b/src/libyang.h
@@ -0,0 +1,167 @@
+/**
+ * @file libyang.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief The main libyang public header.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_LIBYANG_H_
+#define LY_LIBYANG_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "context.h"
+#include "dict.h"
+#include "in.h"
+#include "log.h"
+#include "metadata.h"
+#include "out.h"
+#include "parser_data.h"
+#include "parser_schema.h"
+#include "printer_data.h"
+#include "printer_schema.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+/*
+ * The following headers are supposed to be included explicitly:
+ * - metadata.h
+ * - plugins_types.h
+ * - plugins_exts.h
+ */
+
+/**
+ * @mainpage About
+ *
+ * libyang is a library implementing processing of the YANG schemas and data modeled by the YANG language. The
+ * library is implemented in C for GNU/Linux and provides C API.
+ *
+ * @section about-features Main Features
+ *
+ * - [Parsing (and validating) schemas](@ref howtoSchema) in YANG format.
+ * - [Parsing (and validating) schemas](@ref howtoSchema) in YIN format.
+ * - [Parsing, validating and printing instance data](@ref howtoData) in XML format.
+ * - [Parsing, validating and printing instance data](@ref howtoData) in JSON format
+ * ([RFC 7951](https://tools.ietf.org/html/rfc7951)).
+ * - [Manipulation with the instance data](@ref howtoDataManipulation).
+ * - Support for [default values in the instance data](@ref howtoDataWD) ([RFC 6243](https://tools.ietf.org/html/rfc6243)).
+ * - Support for [YANG extensions and user types](@ref howtoPlugins).
+ * - Support for [YANG Metadata](@ref howtoDataMetadata) ([RFC 7952](https://tools.ietf.org/html/rfc6243)).
+ * - Support for [YANG Schema Mount](@ref howtoDataMountpoint) ([RFC 8528](https://tools.ietf.org/html/rfc8528)).
+ *
+ * The current implementation covers YANG 1.0 ([RFC 6020](https://tools.ietf.org/html/rfc6020)) as well as
+ * YANG 1.1 ([RFC 7950](https://tools.ietf.org/html/rfc7950)).
+ *
+ * @section about-license License
+ *
+ * Copyright (c) 2015-2022 CESNET, z.s.p.o.
+ *
+ * (The BSD 3-Clause License)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of the Company nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ */
+
+/**
+ * @page howto libyang API Overview
+ *
+ * @section howtoGeneral General notes
+ *
+ * libyang is primarily intended for handling data modeled by YANG modeling language, so the library is supposed to be optimized
+ * for this purpose. However, as a side effect, the library has to be able precisely process YANG modules. Thus, it is usable by
+ * YANG module authors to validate their modules and schemas in the development process.
+ *
+ * - @subpage howtoStructures
+ * - @subpage howtoErrors
+ * - @subpage howtoLogger
+ * - @subpage howtoThreads
+ * - @subpage howtoContext
+ * - @subpage howtoInput
+ * - @subpage howtoOutput
+ * - @subpage howtoSchema
+ * - @subpage howtoData
+ * - @subpage howtoXPath
+ * - @subpage howtoPlugins
+ */
+
+/**
+ * @page howtoStructures Data Structures
+ *
+ * @section sizedarrays Sized Arrays
+ *
+ * The structure starts with 32bit number storing size of the array - the number of the items inside. The size is part of the
+ * array to have it allocated together with the array itself only when it is needed. However, the pointers to the array always
+ * points after the 32b number, so items can be accessed directly as for standard C arrays. Because of a known size (available
+ * via ::LY_ARRAY_COUNT macro), it is not terminated by any special byte (sequence), so there is also no limitation for specific
+ * content of the stored records (e.g. that first byte must not be NULL).
+ *
+ * The sized arrays must be carefully freed (which should be done anyway only internally), since pointers to the sized arrays used
+ * in libyang structures, does not point to the beginning of the allocated space.
+ *
+ * - ::LY_ARRAY_COUNT
+ * - ::LY_ARRAY_FOR
+ *
+ * @section struct_lists Lists
+ *
+ * The lists are structures connected via a `next` and `prev` pointers. Iterating over the siblings can be simply done by
+ * ::LY_LIST_FOR macro. Examples of such structures are ::lyd_node or ::lysc_node.
+ *
+ * The `prev` pointer is always filled. In case there is just a single item in the list, the `prev` pointer points to the
+ * item itself. Otherwise, the `prev` pointer of the first item points to the last item of the list. In contrast, the
+ * `next` pointer of the last item in the list is always NULL.
+ */
+
+/**
+ * @page howtoThreads Threading Limitations
+ *
+ * @section context Context
+ *
+ * It is safe to read from ::ly_ctx structure concurrently and use its dictionary, which is protected by a lock.
+ * Thread-safe functions include any ones working with data trees (only context dictionary is accessed) and all
+ * the `ly_ctx_get_*()` functions. Generally, they are the functions with `const` context parameter.
+ *
+ * @section data Data Trees
+ *
+ * Data trees are not internally synchronized so the general safe practice of a single writer **or** several concurrent
+ * readers should be followed. Specifically, only the functions with non-const ::lyd_node parameters modify the node(s)
+ * and no concurrent execution of such functions should be allowed on a single data tree or subtrees of one.
+ */
+
+/**
+ * @internal
+ * @page internals Developers' Notes
+ * @tableofcontents
+ *
+ * Following texts describes various internal subsystems and mechanism in libyang which are hidden from external users, but important
+ * for libyang developers. The texts should explain various decisions made and internal processes utilized in libyang.
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_LIBYANG_H_ */
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..2a1f862
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,773 @@
+/**
+ * @file log.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Logger routines implementations
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "log.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "in_internal.h"
+#include "plugins_exts.h"
+#include "set.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+ATOMIC_T ly_ll = (uint_fast32_t)LY_LLWRN;
+ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST);
+THREAD_LOCAL uint32_t *temp_ly_log_opts;
+static ly_log_clb log_clb;
+static ATOMIC_T path_flag = 1;
+#ifndef NDEBUG
+ATOMIC_T ly_ldbg_groups = 0;
+#endif
+
+THREAD_LOCAL struct ly_log_location_s log_location = {0};
+
+/* how many bytes add when enlarging buffers */
+#define LY_BUF_STEP 128
+
+LIBYANG_API_DEF LY_ERR
+ly_errcode(const struct ly_ctx *ctx)
+{
+ struct ly_err_item *i;
+
+ i = ly_err_last(ctx);
+ if (i) {
+ return i->no;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_VECODE
+ly_vecode(const struct ly_ctx *ctx)
+{
+ struct ly_err_item *i;
+
+ i = ly_err_last(ctx);
+ if (i) {
+ return i->vecode;
+ }
+
+ return LYVE_SUCCESS;
+}
+
+LIBYANG_API_DEF const char *
+ly_errmsg(const struct ly_ctx *ctx)
+{
+ struct ly_err_item *i;
+
+ LY_CHECK_ARG_RET(NULL, ctx, NULL);
+
+ i = ly_err_last(ctx);
+ if (i) {
+ return i->msg;
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF const char *
+ly_errpath(const struct ly_ctx *ctx)
+{
+ struct ly_err_item *i;
+
+ LY_CHECK_ARG_RET(NULL, ctx, NULL);
+
+ i = ly_err_last(ctx);
+ if (i) {
+ return i->path;
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF const char *
+ly_errapptag(const struct ly_ctx *ctx)
+{
+ struct ly_err_item *i;
+
+ LY_CHECK_ARG_RET(NULL, ctx, NULL);
+
+ i = ly_err_last(ctx);
+ if (i) {
+ return i->apptag;
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format, ...)
+{
+ char *msg = NULL;
+ struct ly_err_item *e;
+
+ if (!err || (ecode == LY_SUCCESS)) {
+ /* nothing to do */
+ return ecode;
+ }
+
+ e = malloc(sizeof *e);
+ LY_CHECK_ERR_RET(!e, LOGMEM(NULL), LY_EMEM);
+ e->prev = (*err) ? (*err)->prev : e;
+ e->next = NULL;
+ if (*err) {
+ (*err)->prev->next = e;
+ }
+
+ /* fill in the information */
+ e->level = LY_LLERR;
+ e->no = ecode;
+ e->vecode = vecode;
+ e->path = path;
+ e->apptag = apptag;
+
+ if (err_format) {
+ va_list print_args;
+
+ va_start(print_args, err_format);
+
+ if (vasprintf(&msg, err_format, print_args) == -1) {
+ /* we don't have anything more to do, just set msg to NULL to avoid undefined content,
+ * still keep the information about the original error instead of LY_EMEM or other printf's error */
+ msg = NULL;
+ }
+
+ va_end(print_args);
+ }
+ e->msg = msg;
+
+ if (!(*err)) {
+ *err = e;
+ }
+
+ return e->no;
+}
+
+LIBYANG_API_DEF struct ly_err_item *
+ly_err_first(const struct ly_ctx *ctx)
+{
+ LY_CHECK_ARG_RET(NULL, ctx, NULL);
+
+ return pthread_getspecific(ctx->errlist_key);
+}
+
+LIBYANG_API_DEF struct ly_err_item *
+ly_err_last(const struct ly_ctx *ctx)
+{
+ const struct ly_err_item *e;
+
+ LY_CHECK_ARG_RET(NULL, ctx, NULL);
+
+ e = pthread_getspecific(ctx->errlist_key);
+ return e ? e->prev : NULL;
+}
+
+LIBYANG_API_DEF void
+ly_err_free(void *ptr)
+{
+ struct ly_err_item *i, *next;
+
+ /* clean the error list */
+ for (i = (struct ly_err_item *)ptr; i; i = next) {
+ next = i->next;
+ free(i->msg);
+ free(i->path);
+ free(i->apptag);
+ free(i);
+ }
+}
+
+LIBYANG_API_DEF void
+ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
+{
+ struct ly_err_item *i, *first;
+
+ first = ly_err_first(ctx);
+ if (first == eitem) {
+ eitem = NULL;
+ }
+ if (eitem) {
+ /* disconnect the error */
+ for (i = first; i && (i->next != eitem); i = i->next) {}
+ assert(i);
+ i->next = NULL;
+ first->prev = i;
+ /* free this err and newer */
+ ly_err_free(eitem);
+ } else {
+ /* free all err */
+ ly_err_free(first);
+ pthread_setspecific(ctx->errlist_key, NULL);
+ }
+}
+
+LIBYANG_API_DEF LY_LOG_LEVEL
+ly_log_level(LY_LOG_LEVEL level)
+{
+ LY_LOG_LEVEL prev = ATOMIC_LOAD_RELAXED(ly_ll);
+
+ ATOMIC_STORE_RELAXED(ly_ll, level);
+ return prev;
+}
+
+LIBYANG_API_DEF uint32_t
+ly_log_options(uint32_t opts)
+{
+ uint32_t prev = ATOMIC_LOAD_RELAXED(ly_log_opts);
+
+ ATOMIC_STORE_RELAXED(ly_log_opts, opts);
+ return prev;
+}
+
+LIBYANG_API_DEF void
+ly_temp_log_options(uint32_t *opts)
+{
+ temp_ly_log_opts = opts;
+}
+
+LIBYANG_API_DEF uint32_t
+ly_log_dbg_groups(uint32_t dbg_groups)
+{
+#ifndef NDEBUG
+ uint32_t prev = ATOMIC_LOAD_RELAXED(ly_ldbg_groups);
+
+ ATOMIC_STORE_RELAXED(ly_ldbg_groups, dbg_groups);
+ return prev;
+#else
+ (void)dbg_groups;
+ return 0;
+#endif
+}
+
+LIBYANG_API_DEF void
+ly_set_log_clb(ly_log_clb clb, ly_bool path)
+{
+ log_clb = clb;
+ ATOMIC_STORE_RELAXED(path_flag, path);
+}
+
+LIBYANG_API_DEF ly_log_clb
+ly_get_log_clb(void)
+{
+ return log_clb;
+}
+
+void
+ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode, const char *path, const struct ly_in *in,
+ uint64_t line)
+{
+ if (scnode) {
+ ly_set_add(&log_location.scnodes, (void *)scnode, 1, NULL);
+ }
+ if (dnode) {
+ ly_set_add(&log_location.dnodes, (void *)dnode, 1, NULL);
+ }
+ if (path) {
+ char *s = strdup(path);
+
+ LY_CHECK_ERR_RET(!s, LOGMEM(NULL), );
+ ly_set_add(&log_location.paths, s, 1, NULL);
+ }
+ if (in) {
+ ly_set_add(&log_location.inputs, (void *)in, 1, NULL);
+ }
+ if (line) {
+ log_location.line = line;
+ }
+}
+
+void
+ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps)
+{
+ for (uint32_t i = scnode_steps; i && log_location.scnodes.count; i--) {
+ log_location.scnodes.count--;
+ }
+
+ for (uint32_t i = dnode_steps; i && log_location.dnodes.count; i--) {
+ log_location.dnodes.count--;
+ }
+
+ for (uint32_t i = path_steps; i && log_location.paths.count; i--) {
+ ly_set_rm_index(&log_location.paths, log_location.paths.count - 1, free);
+ }
+
+ for (uint32_t i = in_steps; i && log_location.inputs.count; i--) {
+ log_location.inputs.count--;
+ }
+
+ /* deallocate the empty sets */
+ if (scnode_steps && !log_location.scnodes.count) {
+ ly_set_erase(&log_location.scnodes, NULL);
+ }
+ if (dnode_steps && !log_location.dnodes.count) {
+ ly_set_erase(&log_location.dnodes, NULL);
+ }
+ if (path_steps && !log_location.paths.count) {
+ ly_set_erase(&log_location.paths, free);
+ }
+ if (in_steps && !log_location.inputs.count) {
+ ly_set_erase(&log_location.inputs, NULL);
+ }
+}
+
+static LY_ERR
+log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
+{
+ struct ly_err_item *eitem, *last;
+
+ assert(ctx && (level < LY_LLVRB));
+
+ eitem = pthread_getspecific(ctx->errlist_key);
+ if (!eitem) {
+ /* if we are only to fill in path, there must have been an error stored */
+ assert(msg);
+ eitem = malloc(sizeof *eitem);
+ LY_CHECK_GOTO(!eitem, mem_fail);
+ eitem->prev = eitem;
+ eitem->next = NULL;
+
+ pthread_setspecific(ctx->errlist_key, eitem);
+ } else if (!msg) {
+ /* only filling the path */
+ assert(path);
+
+ /* find last error */
+ eitem = eitem->prev;
+ do {
+ if (eitem->level == LY_LLERR) {
+ /* fill the path */
+ free(eitem->path);
+ eitem->path = path;
+ return LY_SUCCESS;
+ }
+ eitem = eitem->prev;
+ } while (eitem->prev->next);
+ /* last error was not found */
+ assert(0);
+ } else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) ||
+ (!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) {
+ /* overwrite last message */
+ free(eitem->msg);
+ free(eitem->path);
+ free(eitem->apptag);
+ } else {
+ /* store new message */
+ last = eitem->prev;
+ eitem->prev = malloc(sizeof *eitem);
+ LY_CHECK_GOTO(!eitem->prev, mem_fail);
+ eitem = eitem->prev;
+ eitem->prev = last;
+ eitem->next = NULL;
+ last->next = eitem;
+ }
+
+ /* fill in the information */
+ eitem->level = level;
+ eitem->no = no;
+ eitem->vecode = vecode;
+ eitem->msg = msg;
+ eitem->path = path;
+ eitem->apptag = apptag;
+ return LY_SUCCESS;
+
+mem_fail:
+ LOGMEM(NULL);
+ free(msg);
+ free(path);
+ free(apptag);
+ return LY_EMEM;
+}
+
+static void
+log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path, const char *apptag,
+ const char *format, va_list args)
+{
+ char *msg = NULL;
+ ly_bool free_strs, 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)) {
+ /* do not print or store the message */
+ free(path);
+ return;
+ }
+
+ if (no == LY_EMEM) {
+ /* just print it, anything else would most likely fail anyway */
+ if (lolog) {
+ if (log_clb) {
+ log_clb(level, LY_EMEM_MSG, path);
+ } else {
+ fprintf(stderr, "libyang[%d]: ", level);
+ vfprintf(stderr, format, args);
+ if (path) {
+ fprintf(stderr, " (path: %s)\n", path);
+ } else {
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+ free(path);
+ return;
+ }
+
+ /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
+ if ((level < LY_LLVRB) && ctx && lostore) {
+ assert(format);
+ if (vasprintf(&msg, format, args) == -1) {
+ LOGMEM(ctx);
+ free(path);
+ return;
+ }
+ if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
+ /* assume we are inheriting the error, so inherit vecode as well */
+ vecode = ly_vecode(ctx);
+ }
+ if (log_store(ctx, level, no, vecode, msg, path, apptag ? strdup(apptag) : NULL)) {
+ return;
+ }
+ free_strs = 0;
+ } else {
+ if (vasprintf(&msg, format, args) == -1) {
+ LOGMEM(ctx);
+ free(path);
+ return;
+ }
+ free_strs = 1;
+ }
+
+ /* if we are only storing errors internally, never print the message (yet) */
+ if (lolog) {
+ if (log_clb) {
+ log_clb(level, msg, path);
+ } else {
+ fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
+ if (path) {
+ fprintf(stderr, "(path: %s)\n", path);
+ }
+ }
+ }
+
+ if (free_strs) {
+ free(path);
+ free(msg);
+ }
+}
+
+#ifndef NDEBUG
+
+void
+ly_log_dbg(uint32_t group, const char *format, ...)
+{
+ char *dbg_format;
+ const char *str_group;
+ va_list ap;
+
+ if (!(ATOMIC_LOAD_RELAXED(ly_ldbg_groups) & group)) {
+ return;
+ }
+
+ switch (group) {
+ case LY_LDGDICT:
+ str_group = "DICT";
+ break;
+ case LY_LDGXPATH:
+ str_group = "XPATH";
+ break;
+ case LY_LDGDEPSETS:
+ str_group = "DEPSETS";
+ break;
+ default:
+ LOGINT(NULL);
+ return;
+ }
+
+ if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
+ LOGMEM(NULL);
+ return;
+ }
+
+ va_start(ap, format);
+ log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, dbg_format, ap);
+ va_end(ap);
+}
+
+#endif
+
+void
+ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ log_vprintf(ctx, level, no, 0, NULL, NULL, format, ap);
+ va_end(ap);
+}
+
+/**
+ * @brief Append a schema node name to a generated data path, only if it fits.
+ *
+ * @param[in,out] str Generated path to update.
+ * @param[in] snode Schema node to append.
+ * @param[in] parent Last printed data node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struct lyd_node *parent)
+{
+ const struct lys_module *mod, *prev_mod;
+ uint32_t len, new_len;
+ void *mem;
+
+ if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
+ /* schema-only node */
+ return LY_SUCCESS;
+ } else if (lysc_data_parent(snode) != parent->schema) {
+ /* not a direct descendant node */
+ return LY_SUCCESS;
+ }
+
+ /* get module to print, if any */
+ mod = snode->module;
+ prev_mod = (parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ if (prev_mod == mod) {
+ mod = NULL;
+ }
+
+ /* realloc string */
+ len = strlen(*str);
+ new_len = len + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(snode->name);
+ mem = realloc(*str, new_len + 1);
+ LY_CHECK_ERR_RET(!mem, LOGMEM(LYD_CTX(parent)), LY_EMEM);
+ *str = mem;
+
+ /* print the last schema node */
+ sprintf(*str + len, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", snode->name);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Build log path from the stored log location information.
+ *
+ * @param[in] ctx Context to use.
+ * @param[out] path Generated log path.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
+{
+ int r;
+ char *str = NULL, *prev = NULL;
+ const struct lyd_node *dnode;
+
+ *path = NULL;
+
+ if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
+ /* simply get what is in the provided path string */
+ *path = strdup((const char *)log_location.paths.objs[log_location.paths.count - 1]);
+ LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
+ } else {
+ /* data/schema node */
+ if (log_location.dnodes.count) {
+ dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
+ if (dnode->parent || !lysc_data_parent(dnode->schema)) {
+ /* data node with all of its parents */
+ str = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
+ LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
+ } else {
+ /* data parsers put all the parent nodes in the set, but they are not connected */
+ str = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
+ LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
+ }
+
+ /* sometimes the last node is not created yet and we only have the schema node */
+ if (log_location.scnodes.count) {
+ ly_vlog_build_path_append(&str, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
+ }
+
+ r = asprintf(path, "Data location \"%s\"", str);
+ free(str);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ } else if (log_location.scnodes.count) {
+ str = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
+ LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
+
+ r = asprintf(path, "Schema location \"%s\"", str);
+ free(str);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ }
+
+ /* line */
+ prev = *path;
+ if (log_location.line) {
+ r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+
+ log_location.line = 0;
+ } else if (log_location.inputs.count) {
+ r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
+ ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ }
+
+ if (*path) {
+ prev = *path;
+ r = asprintf(path, "%s.", prev);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...)
+{
+ va_list ap;
+ char *path = NULL;
+
+ if (ATOMIC_LOAD_RELAXED(path_flag) && ctx) {
+ ly_vlog_build_path(ctx, &path);
+ }
+
+ va_start(ap, format);
+ log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, apptag, format, ap);
+ /* path is spent and should not be freed! */
+ va_end(ap);
+}
+
+/**
+ * @brief Print a log message from an extension plugin callback.
+ *
+ * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
+ * @param[in] plugin_name Name of the plugin generating the message.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] err_no Error type code.
+ * @param[in] path Optional path of the error.
+ * @param[in] format Format string to print.
+ * @param[in] ap Var arg list for @p format.
+ */
+static void
+ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, const char *path,
+ const char *format, va_list ap)
+{
+ char *plugin_msg;
+
+ if (ATOMIC_LOAD_RELAXED(ly_ll) < level) {
+ return;
+ }
+ if (asprintf(&plugin_msg, "Ext plugin \"%s\": %s", plugin_name, format) == -1) {
+ LOGMEM(ctx);
+ return;
+ }
+
+ log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, NULL,
+ plugin_msg, ap);
+ free(plugin_msg);
+}
+
+LIBYANG_API_DEF void
+lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
+ const char *format, ...)
+{
+ va_list ap;
+ char *path = NULL;
+
+ if (ATOMIC_LOAD_RELAXED(path_flag)) {
+ ly_vlog_build_path(PARSER_CTX(pctx), &path);
+ }
+
+ va_start(ap, format);
+ ly_ext_log(PARSER_CTX(pctx), ext->record->plugin.id, level, err_no, path, format, ap);
+ va_end(ap);
+
+ free(path);
+}
+
+LIBYANG_API_DEF void
+lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, cctx ? cctx->path : NULL, format, ap);
+ va_end(ap);
+}
+
+LIBYANG_API_DEF void
+lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
+ va_end(ap);
+}
+
+/**
+ * @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
+ */
+static void
+_ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem, const char *format, ...)
+{
+ va_list ap;
+ char *path_dup = NULL;
+
+ LY_CHECK_ARG_RET(ctx, eitem, );
+
+ if (eitem->path) {
+ /* duplicate path because it will be freed */
+ path_dup = strdup(eitem->path);
+ LY_CHECK_ERR_RET(!path_dup, LOGMEM(ctx), );
+ }
+
+ va_start(ap, format);
+ log_vprintf(ctx, eitem->level, eitem->no, eitem->vecode, path_dup, eitem->apptag, format, ap);
+ va_end(ap);
+}
+
+LIBYANG_API_DEF void
+ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem)
+{
+ /* String ::ly_err_item.msg cannot be used directly because it may contain the % character */
+ _ly_err_print(ctx, eitem, "%s", eitem->msg);
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..2144668
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,404 @@
+/**
+ * @file log.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Logger manipulation routines and error definitions.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_LOG_H_
+#define LY_LOG_H_
+
+#include <stdint.h>
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* dummy context structure */
+struct ly_ctx;
+
+/**
+ * @brief Type to indicate boolean value.
+ *
+ * Do not test for actual value. Instead, handle it as true/false value in condition.
+ */
+typedef uint8_t ly_bool;
+
+/**
+ * @page howtoLogger Information Logging
+ *
+ * The libyang logger is supposed to process all the messages (and some other accompanied information) generated by the performed
+ * functions. According to the logger settings, the information can be printed, stored or further processed by a callback
+ * functions.
+ *
+ * The logger is tightly connected with [errors handling](@ref howtoErrors), because when an error appears, the logger (according
+ * to [logger options](@ref logopts)) generates error records available via libyang context.
+ *
+ * There are 4 verbosity levels defined as ::LY_LOG_LEVEL. The level can be changed by the ::ly_log_level() function.
+ * By default, the verbosity level is set to #LY_LLERR value, so only the errors are processed.
+ *
+ * By default, all libyang messages are printed to `stderr`. However, the callers are able to set their own logging callback
+ * function (::ly_log_clb). In that case, instead of printing messages, libyang passes error level, message and path (if any) to
+ * the caller's callback function set via ::ly_set_log_clb(). In case of error level, the error information is still
+ * automatically stored and available via the [error handling functions](@ref howtoErrors).
+ *
+ * With [logging options](@ref logopts) set via ::ly_log_options(), the caller can modify what is done with all the messages.
+ * Default flags are ::LY_LOLOG and ::LY_LOSTORE_LAST so that messages are logged and the last one is stored. If you set the flag
+ * ::LY_LOSTORE, all the messages will be stored. Be careful because unless you regularly clean them, the error list in context
+ * will grow indefinitely.
+ *
+ * As a separate group, there are @ref dbggroup to select group of debugging messages to print. The options can be set via
+ * ::ly_log_dbg_groups() function, but note that the options take effect only in case the libyang is compiled in
+ * [Debug build mode](@ref build).
+ *
+ * \note API for this group of functions is described in the [logger module](@ref log).
+ *
+ * Functions List
+ * --------------
+ * - ::ly_log_level()
+ * - ::ly_log_dbg_groups()
+ * - ::ly_log_options()
+ * - ::ly_set_log_clb()
+ * - ::ly_get_log_clb()
+ *
+ */
+
+/**
+ * @defgroup log Logger
+ * @{
+ *
+ * Publicly visible functions and values of the libyang logger. For more
+ * information, see @ref howtoLogger.
+ */
+
+/**
+ * @typedef LY_LOG_LEVEL
+ * @brief Verbosity levels of the libyang logger.
+ */
+typedef enum {
+ LY_LLERR = 0, /**< Print only error messages. */
+ LY_LLWRN = 1, /**< Print error and warning messages, default value. */
+ LY_LLVRB = 2, /**< Besides errors and warnings, print some other verbose messages. */
+ LY_LLDBG = 3 /**< Print all messages including some development debug messages (be careful,
+ without subsequently calling ::ly_log_dbg_groups() no debug messages will be printed!). */
+} LY_LOG_LEVEL;
+
+/**
+ * @brief Set logger verbosity level.
+ *
+ * To get the current value, the function must be called twice resetting the level by the received value.
+ *
+ * @param[in] level Verbosity level.
+ * @return Previous verbosity level.
+ */
+LIBYANG_API_DECL LY_LOG_LEVEL ly_log_level(LY_LOG_LEVEL level);
+
+/**
+ * @ingroup logger
+ * @defgroup logopts Logging options
+ *
+ * Logging option bits of libyang.
+ *
+ * Can be set via ::ly_log_options().
+ *
+ * @{
+ */
+#define LY_LOLOG 0x01 /**< Log messages normally, using callback if set. If not set, messages will
+ not be printed by libyang. */
+#define LY_LOSTORE 0x02 /**< Store any generated errors or warnings, never verbose or debug messages.
+ Note that if #LY_LOLOG is not set then verbose and debug messages are always lost. */
+#define LY_LOSTORE_LAST 0x06 /**< Store any generated errors or warnings but only the last message, always overwrite
+ the previous one. */
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Set logger options. Default is #LY_LOLOG | #LY_LOSTORE_LAST.
+ *
+ * To get the current value, the function must be called twice resetting the level by the received value.
+ *
+ * @param[in] opts Bitfield of @ref logopts.
+ * @return Previous logger options.
+ */
+LIBYANG_API_DECL uint32_t ly_log_options(uint32_t opts);
+
+/**
+ * @brief Set temporary thread-safe logger options overwriting those set by ::ly_log_options().
+ *
+ * @param[in] opts Pointer to the temporary @ref logopts. If NULL, restores the effect of global logger options.
+ */
+LIBYANG_API_DECL void ly_temp_log_options(uint32_t *opts);
+
+#ifndef NDEBUG
+
+/**
+ * @ingroup log
+ * @defgroup dbggroup Debug messages groups
+ *
+ * Categories of the debug messages.
+ *
+ * Allows to show only the selected group(s) of the debug messages.
+ *
+ * @{
+ */
+
+#define LY_LDGDICT 0x01 /**< Dictionary additions and deletions. */
+#define LY_LDGXPATH 0x02 /**< XPath parsing end evaluation. */
+#define LY_LDGDEPSETS 0x04 /**< Dependency module sets for schema compilation. */
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Enable specific debugging messages (independent of log level).
+ *
+ * To get the current value, the function must be called twice resetting the level by the received value.
+ *
+ * @param[in] dbg_groups Bitfield of enabled debug message groups (see @ref dbggroup).
+ * @return Previous options bitfield.
+ */
+uint32_t ly_log_dbg_groups(uint32_t dbg_groups);
+
+#endif
+
+/**
+ * @brief Logger callback.
+ *
+ * !IMPORTANT! If an error has a specific error-app-tag defined in the model, it will NOT be set
+ * at the time of calling this callback. It will be set right after, so to retrieve it
+ * it must be checked afterwards with ::ly_errapptag().
+ *
+ * @param[in] level Log level of the message.
+ * @param[in] msg Message.
+ * @param[in] path Optional path of the concerned node.
+ */
+typedef void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
+
+/**
+ * @brief Set logger callback.
+ *
+ * @param[in] clb Logging callback.
+ * @param[in] path flag to resolve and provide path as the third parameter of the callback function. In case of
+ * validation and some other errors, it can be useful to get the path to the problematic element. Note,
+ * that according to the tree type and the specific situation, the path can slightly differs (keys
+ * presence) or it can be NULL, so consider it as an optional parameter. If the flag is 0, libyang will
+ * not bother with resolving the path.
+ */
+LIBYANG_API_DECL void ly_set_log_clb(ly_log_clb clb, ly_bool path);
+
+/**
+ * @brief Get logger callback.
+ * @return Logger callback (can be NULL).
+ */
+LIBYANG_API_DECL ly_log_clb ly_get_log_clb(void);
+
+/** @} log */
+
+/**
+ * @page howtoErrors Errors Handling
+ *
+ * The most of the API functions directly returns error code in the form of ::LY_ERR value. In addition, if the ::LY_EVALID error
+ * arises, additional [validation error code](@ref ::LY_VECODE) is provided to categorize validation failures into several groups.
+ *
+ * All the errors arisen in connection with manipulation with the [context](@ref howtoContext), [YANG modules](@ref howtoSchema)
+ * or [YANG data](@ref howtoData), are recorded into the context and can be examined for the more detailed information. These
+ * records are stored as ::ly_err_item structures and they are not only context-specific, but also thread-specific.
+ *
+ * Storing error information is tightly connected with
+ * [logging](@ref howtoLogger). So the @ref logopts control if and which errors are stored in the context. By default, only the
+ * last error is recorded, so a new error replaces the previous one. This can be changed with ::LY_LOSTORE option set via
+ * ::ly_log_options(). Then, the errors are stored as a list preserving the previous error records. The stored records can be
+ * accessed using ::ly_err_last() or ::ly_err_first() functions. The ::ly_err_clean() is used to remove error records from the
+ * context.
+ *
+ * To print a specific error information via libyang logger, there is ::ly_err_print().
+ *
+ * To simplify access to the last error record in the context, there is a set of functions returning important error information.
+ * - ::ly_errapptag()
+ * - ::ly_errcode()
+ * - ::ly_vecode()
+ * - ::ly_errmsg()
+ * - ::ly_errpath()
+ *
+ * \note API for this group of functions is described in the [error information module](@ref errors).
+ */
+
+/**
+ * @defgroup errors Error information
+ *
+ * Structures and functions to allow error information processing.
+ *
+ * @{
+ */
+
+/**
+ * @typedef LY_ERR
+ * @brief libyang's error codes returned by the libyang functions.
+ */
+typedef enum {
+ LY_SUCCESS = 0, /**< no error, not set by functions, included just to complete #LY_ERR enumeration */
+ LY_EMEM, /**< Memory allocation failure */
+ LY_ESYS, /**< System call failure */
+ LY_EINVAL, /**< Invalid value */
+ LY_EEXIST, /**< Item already exists */
+ LY_ENOTFOUND, /**< Item does not exists */
+ LY_EINT, /**< Internal error */
+ LY_EVALID, /**< Validation failure */
+ LY_EDENIED, /**< Operation is not allowed */
+ LY_EINCOMPLETE, /**< The operation did not fail, but for some reason it was not possible to finish it completely.
+ According to the specific use case, the caller is usually supposed to perform the operation again. */
+ LY_ERECOMPILE, /**< The operation did not fail, but requires context recompilation before it can be completed.
+ According to the specific use case, the caller should react appropriately. */
+ LY_ENOT, /**< Negative result */
+ LY_EOTHER, /**< Unknown error */
+
+ LY_EPLUGIN = 128/**< Error reported by a plugin - the highest bit in the first byte is set.
+ This value is used ORed with one of the other LY_ERR value and can be simply masked. */
+} LY_ERR;
+
+/**
+ * @ingroup logger
+ * @typedef LY_VECODE
+ * @brief libyang's codes of validation error. Whenever ly_errno is set to LY_EVALID, the ly_vecode is also set
+ * to the appropriate LY_VECODE value.
+ */
+typedef enum {
+ LYVE_SUCCESS = 0, /**< no error */
+ LYVE_SYNTAX, /**< generic syntax error */
+ LYVE_SYNTAX_YANG, /**< YANG-related syntax error */
+ LYVE_SYNTAX_YIN, /**< YIN-related syntax error */
+ LYVE_REFERENCE, /**< invalid referencing or using an item */
+ LYVE_XPATH, /**< invalid XPath expression */
+ LYVE_SEMANTICS, /**< generic semantic error */
+ LYVE_SYNTAX_XML, /**< XML-related syntax error */
+ LYVE_SYNTAX_JSON, /**< JSON-related syntax error */
+ LYVE_DATA, /**< YANG data does not reflect some of the module restrictions */
+
+ LYVE_OTHER /**< Unknown error */
+} LY_VECODE;
+
+/**
+ * @brief Libyang full error structure.
+ */
+struct ly_err_item {
+ LY_LOG_LEVEL level;
+ LY_ERR no;
+ LY_VECODE vecode;
+ char *msg;
+ char *path;
+ char *apptag;
+ struct ly_err_item *next;
+ struct ly_err_item *prev; /* first item's prev points to the last item */
+};
+
+/**
+ * @brief Get the last (thread, context-specific) validation error code.
+ *
+ * This value is set only if ly_errno is #LY_EVALID.
+ *
+ * @param[in] ctx Relative context.
+ * @return Validation error code.
+ */
+LIBYANG_API_DECL LY_VECODE ly_vecode(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the last (thread, context-specific) error code.
+ *
+ * @param[in] ctx Relative context.
+ * @return LY_ERR value of the last error code.
+ */
+LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the last (thread, context-specific) error message. If the coresponding module defined
+ * a specific error message, it will be used instead the default one.
+ *
+ * Sometimes, the error message is extended with path of the element where the problem is.
+ * The path is available via ::ly_errpath().
+ *
+ * @param[in] ctx Relative context.
+ * @return Text of the last error message, empty string if there is no error.
+ */
+LIBYANG_API_DECL const char *ly_errmsg(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the last (thread, context-specific) path of the element where was an error.
+ *
+ * The path always corresponds to the error message available via ::ly_errmsg(), so
+ * whenever a subsequent error message is printed, the path is erased or rewritten.
+ * The path reflects the type of the processed tree - data path for data tree functions
+ * and schema path in case of schema tree functions. In case of processing YIN schema
+ * or XML data, the path can be just XML path. In such a case, the corresponding
+ * ly_vecode (value 1-3) is set.
+ *
+ * @param[in] ctx Relative context.
+ * @return Path of the error element, empty string if error path does not apply to the last error.
+ */
+LIBYANG_API_DECL const char *ly_errpath(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the last (thread, context-specific) error-app-tag if there was a specific one defined
+ * in the module for the last error.
+ *
+ * The app-tag always corresponds to the error message available via ::ly_errmsg(), so
+ * whenever a subsequent error message is printed, the app-tag is erased or rewritten.
+ *
+ * @param[in] ctx Relative context.
+ * @return Error-app-tag of the last error, empty string if the error-app-tag does not apply to the last error.
+ */
+LIBYANG_API_DECL const char *ly_errapptag(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the first (thread, context-specific) generated error structure.
+ *
+ * @param[in] ctx Relative context.
+ * @return The first error structure (can be NULL), do not modify!
+ */
+LIBYANG_API_DECL struct ly_err_item *ly_err_first(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the latest (thread, context-specific) generated error structure.
+ *
+ * @param[in] ctx Relative context.
+ * @return The last error structure (can be NULL), do not modify!
+ */
+LIBYANG_API_DECL struct ly_err_item *ly_err_last(const struct ly_ctx *ctx);
+
+/**
+ * @brief Print the error structure as if just generated.
+ *
+ * @param[in] ctx Optional context to store the message in.
+ * @param[in] eitem Error item structure to print.
+ */
+LIBYANG_API_DECL void ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem);
+
+/**
+ * @brief Free error structures from a context.
+ *
+ * If \p eitem is not set, free all the error structures.
+ *
+ * @param[in] ctx Relative context.
+ * @param[in] eitem Oldest error structure to remove, optional.
+ */
+LIBYANG_API_DECL void ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem);
+
+/** @} errors */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_LOG_H_ */
diff --git a/src/lyb.c b/src/lyb.c
new file mode 100644
index 0000000..87806c8
--- /dev/null
+++ b/src/lyb.c
@@ -0,0 +1,125 @@
+/**
+ * @file lyb.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief LYB format common functionality.
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "lyb.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Generate single hash for a schema node to be used for LYB data.
+ *
+ * @param[in] node Node to hash.
+ * @param[in] collision_id Collision ID of the hash to generate.
+ * @return Generated hash.
+ */
+static LYB_HASH
+lyb_generate_hash(const struct lysc_node *node, uint8_t collision_id)
+{
+ const struct lys_module *mod = node->module;
+ uint32_t full_hash;
+ LYB_HASH hash;
+
+ /* generate full hash */
+ full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
+ full_hash = dict_hash_multi(full_hash, node->name, strlen(node->name));
+ if (collision_id) {
+ size_t ext_len;
+
+ if (collision_id > strlen(mod->name)) {
+ /* fine, we will not hash more bytes, just use more bits from the hash than previously */
+ ext_len = strlen(mod->name);
+ } else {
+ /* use one more byte from the module name than before */
+ ext_len = collision_id;
+ }
+ full_hash = dict_hash_multi(full_hash, mod->name, ext_len);
+ }
+ full_hash = dict_hash_multi(full_hash, NULL, 0);
+
+ /* use the shortened hash */
+ hash = full_hash & (LYB_HASH_MASK >> collision_id);
+ /* add collision identificator */
+ hash |= LYB_HASH_COLLISION_ID >> collision_id;
+
+ return hash;
+}
+
+LYB_HASH
+lyb_get_hash(const struct lysc_node *node, uint8_t collision_id)
+{
+ /* hashes must be cached */
+ assert(node->hash[0]);
+
+ if (collision_id < LYS_NODE_HASH_COUNT) {
+ /* read from cache */
+ return node->hash[collision_id];
+ }
+
+ /* generate */
+ return lyb_generate_hash(node, collision_id);
+}
+
+/**
+ * @brief Module DFS callback filling all cached hashes of a schema node.
+ */
+static LY_ERR
+lyb_cache_node_hash_cb(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue))
+{
+ if (node->hash[0]) {
+ /* already cached, stop the DFS */
+ return LY_EEXIST;
+ }
+
+ for (uint8_t i = 0; i < LYS_NODE_HASH_COUNT; ++i) {
+ /* store the hash in the cache */
+ node->hash[i] = lyb_generate_hash(node, i);
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lyb_cache_module_hash(const struct lys_module *mod)
+{
+ /* LOCK */
+ pthread_mutex_lock(&mod->ctx->lyb_hash_lock);
+
+ /* store all cached hashes for all the nodes */
+ lysc_module_dfs_full(mod, lyb_cache_node_hash_cb, NULL);
+
+ /* UNLOCK */
+ pthread_mutex_unlock(&mod->ctx->lyb_hash_lock);
+}
+
+ly_bool
+lyb_has_schema_model(const struct lysc_node *node, const struct lys_module **models)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(models, u) {
+ if (node->module == models[u]) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/lyb.h b/src/lyb.h
new file mode 100644
index 0000000..b123ee5
--- /dev/null
+++ b/src/lyb.h
@@ -0,0 +1,199 @@
+/**
+ * @file lyb.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for LYB format printer & parser
+ *
+ * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_LYB_H_
+#define LY_LYB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "parser_internal.h"
+
+struct ly_ctx;
+struct lysc_node;
+
+/*
+ * LYB format
+ *
+ * Unlike XML or JSON, it is binary format so most data are represented in similar way but in binary.
+ * Some notable differences:
+ *
+ * - schema nodes are identified based on their hash instead of their string name. In case of collisions
+ * an array of hashes is created with each next hash one bit shorter until a unique sequence of all these
+ * hashes is found and then all of them are stored.
+ *
+ * - tree structure is represented as individual strictly bounded "siblings". Each "siblings" begins
+ * with its metadata, which consist of 1) the whole "sibling" length in bytes and 2) number
+ * of included metadata chunks of nested "siblings".
+ *
+ * - since length of a "sibling" is not known before it is printed, holes are first written and
+ * after the "sibling" is printed, they are filled with actual valid metadata. As a consequence,
+ * LYB data cannot be directly printed into streams!
+ *
+ * - data are preceded with information about all the used modules. It is needed because of
+ * possible augments and deviations which must be known beforehand, otherwise schema hashes
+ * could be matched to the wrong nodes.
+ *
+ * This is a short summary of the format:
+ * @verbatim
+
+ sb = siblings_start
+ se = siblings_end
+ siblings = zero-LYB_SIZE_BYTES | (sb instance+ se)
+ instance = node_type model hash node
+ model = 16bit_zero | (model_name_length model_name revision)
+ node = opaq | leaflist | list | any | inner | leaf
+ opaq = opaq_data siblings
+ leaflist = sb leaf+ se
+ list = sb (node_header siblings)+ se
+ any = node_header anydata_data
+ inner = node_header siblings
+ leaf = node_header term_value
+ node_header = metadata node_flags
+
+ @endverbatim
+ */
+
+/**
+ * @brief LYB data node type
+ */
+enum lylyb_node_type {
+ LYB_NODE_TOP, /**< top-level node */
+ LYB_NODE_CHILD, /**< child node with a parent */
+ LYB_NODE_OPAQ, /**< opaque node */
+ LYB_NODE_EXT /**< nested extension data node */
+};
+
+/**
+ * @brief LYB format parser context
+ */
+struct lylyb_ctx {
+ const struct ly_ctx *ctx;
+ uint64_t line; /* current line */
+ struct ly_in *in; /* input structure */
+
+ const struct lys_module **models;
+
+ struct lyd_lyb_sibling {
+ size_t written;
+ size_t position;
+ uint16_t inner_chunks;
+ } *siblings;
+ LY_ARRAY_COUNT_TYPE sibling_size;
+
+ /* LYB printer only */
+ struct lyd_lyb_sib_ht {
+ struct lysc_node *first_sibling;
+ struct hash_table *ht;
+ } *sib_hts;
+};
+
+/**
+ * @brief Destructor for the lylyb_ctx structure
+ */
+void lyd_lyb_ctx_free(struct lyd_ctx *lydctx);
+
+/* just a shortcut */
+#define LYB_LAST_SIBLING(lybctx) lybctx->siblings[LY_ARRAY_COUNT(lybctx->siblings) - 1]
+
+/* struct lyd_lyb_sibling allocation step */
+#define LYB_SIBLING_STEP 4
+
+/* current LYB format version */
+#define LYB_VERSION_NUM 0x05
+
+/* LYB format version mask of the header byte */
+#define LYB_VERSION_MASK 0x0F
+
+/**
+ * LYB schema hash constants
+ *
+ * Hash is divided to collision ID and hash itself.
+ *
+ * @anchor collisionid
+ *
+ * First bits are collision ID until 1 is found. The rest is truncated 32b hash.
+ * 1xxx xxxx - collision ID 0 (no collisions)
+ * 01xx xxxx - collision ID 1 (collision ID 0 hash collided)
+ * 001x xxxx - collision ID 2 ...
+ *
+ * When finding a match for a unique schema (siblings) hash (sequence of hashes with increasing collision ID), the highest
+ * collision ID can be read from the last hash (LYB parser).
+ *
+ * To learn what is the highest collision ID of a hash that must be included in a unique schema (siblings) hash,
+ * collisions with all the preceding sibling schema hashes must be checked (LYB printer).
+ */
+
+/* Number of bits the whole hash will take (including hash collision ID) */
+#define LYB_HASH_BITS 8
+
+/* Masking 32b hash (collision ID 0) */
+#define LYB_HASH_MASK 0x7f
+
+/* Type for storing the whole hash (used only internally, publicly defined directly) */
+#define LYB_HASH uint8_t
+
+/* Need to move this first >> collision number (from 0) to get collision ID hash part */
+#define LYB_HASH_COLLISION_ID 0x80
+
+/* How many bytes are reserved for one data chunk SIZE (8B is maximum) */
+#define LYB_SIZE_BYTES 2
+
+/* Maximum size that will be written into LYB_SIZE_BYTES (must be large enough) */
+#define LYB_SIZE_MAX UINT16_MAX
+
+/* How many bytes are reserved for one data chunk inner chunk count */
+#define LYB_INCHUNK_BYTES 2
+
+/* Maximum size that will be written into LYB_INCHUNK_BYTES (must be large enough) */
+#define LYB_INCHUNK_MAX UINT16_MAX
+
+/* Just a helper macro */
+#define LYB_META_BYTES (LYB_INCHUNK_BYTES + LYB_SIZE_BYTES)
+
+/* model revision as XXXX XXXX XXXX XXXX (2B) (year is offset from 2000)
+ * YYYY YYYM MMMD DDDD */
+#define LYB_REV_YEAR_OFFSET 2000
+#define LYB_REV_YEAR_MASK 0xfe00U
+#define LYB_REV_YEAR_SHIFT 9
+#define LYB_REV_MONTH_MASK 0x01E0U
+#define LYB_REV_MONTH_SHIFT 5
+#define LYB_REV_DAY_MASK 0x001fU
+
+/**
+ * @brief Get single hash for a schema node to be used for LYB data. Read from cache, if possible.
+ *
+ * @param[in] node Node to hash.
+ * @param[in] collision_id Collision ID of the hash to generate, see @ref collisionid.
+ * @return Generated hash.
+ */
+LYB_HASH lyb_get_hash(const struct lysc_node *node, uint8_t collision_id);
+
+/**
+ * @brief Fill the hash cache of all the schema nodes of a module.
+ *
+ * @param[in] mod Module to process.
+ */
+void lyb_cache_module_hash(const struct lys_module *mod);
+
+/**
+ * @brief Check whether a node's module is in a module array.
+ *
+ * @param[in] node Node to check.
+ * @param[in] models Modules in a sized array.
+ * @return Boolean value whether @p node's module was found in the given @p models array.
+ */
+ly_bool lyb_has_schema_model(const struct lysc_node *node, const struct lys_module **models);
+
+#endif /* LY_LYB_H_ */
diff --git a/src/out.c b/src/out.c
new file mode 100644
index 0000000..5567387
--- /dev/null
+++ b/src/out.c
@@ -0,0 +1,768 @@
+/**
+ * @file out.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang output functions.
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "out.h"
+#include "out_internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+#include "printer_data.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Align the desired size to 1 KB.
+ */
+#define REALLOC_CHUNK(NEW_SIZE) \
+ NEW_SIZE + (1024 - (NEW_SIZE % 1024))
+
+LIBYANG_API_DEF ly_bool
+lyd_node_should_print(const struct lyd_node *node, uint32_t options)
+{
+ const struct lyd_node *elem;
+
+ if (options & LYD_PRINT_WD_TRIM) {
+ /* do not print default nodes */
+ if (node->flags & LYD_DEFAULT) {
+ /* implicit default node/NP container with only default nodes */
+ return 0;
+ } else if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) {
+ if (lyd_is_default(node)) {
+ /* explicit default node */
+ return 0;
+ }
+ }
+ } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER)) {
+ if (options & LYD_PRINT_KEEPEMPTYCONT) {
+ /* explicit request to print */
+ return 1;
+ }
+
+ /* avoid empty default containers */
+ LYD_TREE_DFS_BEGIN(node, elem) {
+ if ((elem != node) && lyd_node_should_print(elem, options)) {
+ return 1;
+ }
+ assert(elem->flags & LYD_DEFAULT);
+ LYD_TREE_DFS_END(node, elem)
+ }
+ return 0;
+ } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
+ /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
+ if (!(node->schema->flags & (LYS_IS_INPUT | LYS_IS_OUTPUT | LYS_IS_NOTIF)) && (node->schema->flags & LYS_CONFIG_W)) {
+ /* print only if it contains status data in its subtree */
+ LYD_TREE_DFS_BEGIN(node, elem) {
+ if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
+ if (elem->schema->flags & LYS_CONFIG_R) {
+ return 1;
+ }
+ }
+ LYD_TREE_DFS_END(node, elem)
+ }
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+LIBYANG_API_DEF LY_OUT_TYPE
+ly_out_type(const struct ly_out *out)
+{
+ LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
+ return out->type;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_CALLBACK;
+ (*out)->method.clb.func = writeclb;
+ (*out)->method.clb.arg = user_data;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF ly_write_clb
+ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
+{
+ ly_write_clb prev_clb;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
+
+ prev_clb = out->method.clb.func;
+
+ if (writeclb) {
+ out->method.clb.func = writeclb;
+ }
+
+ return prev_clb;
+}
+
+LIBYANG_API_DEF void *
+ly_out_clb_arg(struct ly_out *out, void *arg)
+{
+ void *prev_arg;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
+
+ prev_arg = out->method.clb.arg;
+
+ if (arg) {
+ out->method.clb.arg = arg;
+ }
+
+ return prev_arg;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_fd(int fd, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+ (*out)->type = LY_OUT_FD;
+ (*out)->method.fd = fd;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF int
+ly_out_fd(struct ly_out *out, int fd)
+{
+ int prev_fd;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
+
+ if (out->type == LY_OUT_FDSTREAM) {
+ prev_fd = out->method.fdstream.fd;
+ } else { /* LY_OUT_FD */
+ prev_fd = out->method.fd;
+ }
+
+ if (fd != -1) {
+ /* replace output stream */
+ if (out->type == LY_OUT_FDSTREAM) {
+ int streamfd;
+ FILE *stream;
+
+ streamfd = dup(fd);
+ if (streamfd < 0) {
+ LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
+ return -1;
+ }
+ stream = fdopen(streamfd, "a");
+ if (!stream) {
+ LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
+ close(streamfd);
+ return -1;
+ }
+ /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
+ fclose(out->method.fdstream.f);
+ out->method.fdstream.f = stream;
+ out->method.fdstream.fd = streamfd;
+ } else { /* LY_OUT_FD */
+ out->method.fd = fd;
+ }
+ }
+
+ return prev_fd;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_file(FILE *f, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_FILE;
+ (*out)->method.f = f;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF FILE *
+ly_out_file(struct ly_out *out, FILE *f)
+{
+ FILE *prev_f;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
+
+ prev_f = out->method.f;
+
+ if (f) {
+ out->method.f = f;
+ }
+
+ return prev_f;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_MEMORY;
+ (*out)->method.mem.buf = strp;
+ if (!size) {
+ /* buffer is supposed to be allocated */
+ *strp = NULL;
+ } else if (*strp) {
+ /* there is already buffer to use */
+ (*out)->method.mem.size = size;
+ }
+
+ return LY_SUCCESS;
+}
+
+char *
+ly_out_memory(struct ly_out *out, char **strp, size_t size)
+{
+ char *data;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
+
+ data = *out->method.mem.buf;
+
+ if (strp) {
+ out->method.mem.buf = strp;
+ out->method.mem.len = out->method.mem.size = 0;
+ out->printed = 0;
+ if (!size) {
+ /* buffer is supposed to be allocated */
+ *strp = NULL;
+ } else if (*strp) {
+ /* there is already buffer to use */
+ out->method.mem.size = size;
+ }
+ }
+
+ return data;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_reset(struct ly_out *out)
+{
+ LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
+
+ switch (out->type) {
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ case LY_OUT_FD:
+ if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
+ LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+ if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
+ LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+ break;
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILE:
+ case LY_OUT_FILEPATH:
+ if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
+ LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+ if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
+ LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+ break;
+ case LY_OUT_MEMORY:
+ if (out->method.mem.buf && *out->method.mem.buf) {
+ memset(*out->method.mem.buf, 0, out->method.mem.len);
+ }
+ out->printed = 0;
+ out->method.mem.len = 0;
+ break;
+ case LY_OUT_CALLBACK:
+ /* nothing to do (not seekable) */
+ break;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_filepath(const char *filepath, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_FILEPATH;
+ (*out)->method.fpath.f = fopen(filepath, "wb");
+ if (!(*out)->method.fpath.f) {
+ LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
+ free(*out);
+ *out = NULL;
+ return LY_ESYS;
+ }
+ (*out)->method.fpath.filepath = strdup(filepath);
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const char *
+ly_out_filepath(struct ly_out *out, const char *filepath)
+{
+ FILE *f;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
+
+ if (!filepath) {
+ return out->method.fpath.filepath;
+ }
+
+ /* replace filepath */
+ f = out->method.fpath.f;
+ out->method.fpath.f = fopen(filepath, "wb");
+ if (!out->method.fpath.f) {
+ LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
+ out->method.fpath.f = f;
+ return (void *)-1;
+ }
+ fclose(f);
+ free(out->method.fpath.filepath);
+ out->method.fpath.filepath = strdup(filepath);
+
+ return NULL;
+}
+
+LIBYANG_API_DEF void
+ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
+{
+ if (!out) {
+ return;
+ }
+
+ switch (out->type) {
+ case LY_OUT_CALLBACK:
+ if (clb_arg_destructor) {
+ clb_arg_destructor(out->method.clb.arg);
+ }
+ break;
+ case LY_OUT_FDSTREAM:
+ fclose(out->method.fdstream.f);
+ if (destroy) {
+ close(out->method.fdstream.fd);
+ }
+ break;
+ case LY_OUT_FD:
+ if (destroy) {
+ close(out->method.fd);
+ }
+ break;
+ case LY_OUT_FILE:
+ if (destroy) {
+ fclose(out->method.f);
+ }
+ break;
+ case LY_OUT_MEMORY:
+ if (destroy) {
+ free(*out->method.mem.buf);
+ }
+ break;
+ case LY_OUT_FILEPATH:
+ free(out->method.fpath.filepath);
+ fclose(out->method.fpath.f);
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ }
+
+ free(out->buffered);
+ free(out);
+}
+
+static LY_ERR
+ly_vprint_(struct ly_out *out, const char *format, va_list ap)
+{
+ LY_ERR ret;
+ int written = 0;
+ char *msg = NULL, *aux;
+
+ switch (out->type) {
+ case LY_OUT_FD:
+ written = vdprintf(out->method.fd, format, ap);
+ break;
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ written = vfprintf(out->method.f, format, ap);
+ break;
+ case LY_OUT_MEMORY:
+ if ((written = vasprintf(&msg, format, ap)) < 0) {
+ break;
+ }
+ if (out->method.mem.len + written + 1 > out->method.mem.size) {
+ aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
+ if (!aux) {
+ out->method.mem.buf = NULL;
+ out->method.mem.len = 0;
+ out->method.mem.size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ *out->method.mem.buf = aux;
+ out->method.mem.size = out->method.mem.len + written + 1;
+ }
+ if (written) {
+ memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
+ }
+ out->method.mem.len += written;
+ (*out->method.mem.buf)[out->method.mem.len] = '\0';
+ free(msg);
+ break;
+ case LY_OUT_CALLBACK:
+ if ((written = vasprintf(&msg, format, ap)) < 0) {
+ break;
+ }
+ written = out->method.clb.func(out->method.clb.arg, msg, written);
+ free(msg);
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ if (written < 0) {
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
+ written = 0;
+ ret = LY_ESYS;
+ } else {
+ if (out->type == LY_OUT_FDSTREAM) {
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ }
+ ret = LY_SUCCESS;
+ }
+
+ out->printed += written;
+ out->func_printed += written;
+ return ret;
+}
+
+LY_ERR
+ly_print_(struct ly_out *out, const char *format, ...)
+{
+ LY_ERR ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = ly_vprint_(out, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_print(struct ly_out *out, const char *format, ...)
+{
+ LY_ERR ret;
+ va_list ap;
+
+ out->func_printed = 0;
+
+ va_start(ap, format);
+ ret = ly_vprint_(out, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+LIBYANG_API_DEF void
+ly_print_flush(struct ly_out *out)
+{
+ switch (out->type) {
+ case LY_OUT_FDSTREAM:
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ fflush(out->method.fdstream.f);
+ break;
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ fflush(out->method.f);
+ break;
+ case LY_OUT_FD:
+ fsync(out->method.fd);
+ break;
+ case LY_OUT_MEMORY:
+ case LY_OUT_CALLBACK:
+ /* nothing to do */
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ }
+
+ free(out->buffered);
+ out->buf_size = out->buf_len = 0;
+}
+
+LY_ERR
+ly_write_(struct ly_out *out, const char *buf, size_t len)
+{
+ LY_ERR ret = LY_SUCCESS;
+ size_t written = 0, new_mem_size;
+
+ if (out->hole_count) {
+ /* we are buffering data after a hole */
+ if (out->buf_len + len > out->buf_size) {
+ out->buffered = ly_realloc(out->buffered, out->buf_len + len);
+ if (!out->buffered) {
+ out->buf_len = 0;
+ out->buf_size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->buf_size = out->buf_len + len;
+ }
+
+ if (len) {
+ memcpy(&out->buffered[out->buf_len], buf, len);
+ }
+ out->buf_len += len;
+
+ out->printed += len;
+ out->func_printed += len;
+ return LY_SUCCESS;
+ }
+
+repeat:
+ switch (out->type) {
+ case LY_OUT_MEMORY:
+ new_mem_size = out->method.mem.len + len + 1;
+ if (new_mem_size > out->method.mem.size) {
+ new_mem_size = REALLOC_CHUNK(new_mem_size);
+ *out->method.mem.buf = ly_realloc(*out->method.mem.buf, new_mem_size);
+ if (!*out->method.mem.buf) {
+ out->method.mem.len = 0;
+ out->method.mem.size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->method.mem.size = new_mem_size;
+ }
+ if (len) {
+ memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
+ }
+ out->method.mem.len += len;
+ (*out->method.mem.buf)[out->method.mem.len] = '\0';
+
+ written = len;
+ break;
+ case LY_OUT_FD: {
+ ssize_t r;
+
+ r = write(out->method.fd, buf, len);
+ if (r < 0) {
+ ret = LY_ESYS;
+ } else {
+ written = (size_t)r;
+ }
+ break;
+ }
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ written = fwrite(buf, sizeof *buf, len, out->method.f);
+ if (written != len) {
+ ret = LY_ESYS;
+ }
+ break;
+ case LY_OUT_CALLBACK: {
+ ssize_t r;
+
+ r = out->method.clb.func(out->method.clb.arg, buf, len);
+ if (r < 0) {
+ ret = LY_ESYS;
+ } else {
+ written = (size_t)r;
+ }
+ break;
+ }
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ if (ret) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+ ret = LY_SUCCESS;
+ goto repeat;
+ }
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
+ written = 0;
+ } else if ((size_t)written != len) {
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
+ len - (size_t)written, len);
+ ret = LY_ESYS;
+ } else {
+ if (out->type == LY_OUT_FDSTREAM) {
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ }
+ ret = LY_SUCCESS;
+ }
+
+ out->printed += written;
+ out->func_printed += written;
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_write(struct ly_out *out, const char *buf, size_t len)
+{
+ out->func_printed = 0;
+
+ return ly_write_(out, buf, len);
+}
+
+LIBYANG_API_DEF size_t
+ly_out_printed(const struct ly_out *out)
+{
+ return out->func_printed;
+}
+
+LY_ERR
+ly_write_skip(struct ly_out *out, size_t count, size_t *position)
+{
+ switch (out->type) {
+ case LY_OUT_MEMORY:
+ if (out->method.mem.len + count > out->method.mem.size) {
+ *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
+ if (!(*out->method.mem.buf)) {
+ out->method.mem.len = 0;
+ out->method.mem.size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->method.mem.size = out->method.mem.len + count;
+ }
+
+ /* save the current position */
+ *position = out->method.mem.len;
+
+ /* skip the memory */
+ out->method.mem.len += count;
+ break;
+ case LY_OUT_FD:
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ case LY_OUT_CALLBACK:
+ /* buffer the hole */
+ if (out->buf_len + count > out->buf_size) {
+ out->buffered = ly_realloc(out->buffered, out->buf_len + count);
+ if (!out->buffered) {
+ out->buf_len = 0;
+ out->buf_size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->buf_size = out->buf_len + count;
+ }
+
+ /* save the current position */
+ *position = out->buf_len;
+
+ /* skip the memory */
+ out->buf_len += count;
+
+ /* increase hole counter */
+ ++out->hole_count;
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ /* update printed bytes counter despite we actually printed just a hole */
+ out->printed += count;
+ out->func_printed += count;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(count);
+
+ switch (out->type) {
+ case LY_OUT_MEMORY:
+ /* write */
+ memcpy(&(*out->method.mem.buf)[position], buf, count);
+ break;
+ case LY_OUT_FD:
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ case LY_OUT_CALLBACK:
+ if (out->buf_len < position + count) {
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+
+ /* write into the hole */
+ memcpy(&out->buffered[position], buf, count);
+
+ /* decrease hole counter */
+ --out->hole_count;
+
+ if (!out->hole_count) {
+ /* all holes filled, we can write the buffer,
+ * printed bytes counter is updated by ly_write_() */
+ ret = ly_write_(out, out->buffered, out->buf_len);
+ out->buf_len = 0;
+ }
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ if (out->type == LY_OUT_FILEPATH) {
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ }
+ return ret;
+}
diff --git a/src/out.h b/src/out.h
new file mode 100644
index 0000000..ca97eff
--- /dev/null
+++ b/src/out.h
@@ -0,0 +1,307 @@
+/**
+ * @file out.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang output structures and functions
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_OUT_H_
+#define LY_OUT_H_
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef _MSC_VER
+# define ssize_t SSIZE_T
+#endif
+
+#include "log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page howtoOutput Output Processing
+ *
+ * libyang provides a mechanism to generalize work with the outputs (and [inputs](@ref howtoInput)) of
+ * the different types. The ::ly_out handler can be created providing necessary information connected with the specific
+ * output type and then used throughout the printers functions. The API allows to combine output from libyang (data or schema)
+ * printers and output directly provided by the caller (via ::ly_print() or ::ly_write()).
+ *
+ * Using a generic output handler avoids need to have a set of functions for each printer functionality and results in simpler API.
+ *
+ * The API allows to alter the target of the data behind the handler by another target (of the same type). Also resetting
+ * a seekable output is possible with ::ly_out_reset() to re-write the output.
+ *
+ * @note
+ * This mechanism was introduced in libyang 2.0. To simplify transition from libyang 1.0 to version 2.0 and also for
+ * some simple use case where using the output handler would be an overkill, there are some basic printer functions
+ * that do not require output handler. But remember, that functionality of these function can be limited in particular cases
+ * in contrast to the functions using output handlers.
+ *
+ * Functions List
+ * --------------
+ * - ::ly_out_new_clb()
+ * - ::ly_out_new_fd()
+ * - ::ly_out_new_file()
+ * - ::ly_out_new_filepath()
+ * - ::ly_out_new_memory()
+ *
+ * - ::ly_out_clb()
+ * - ::ly_out_clb_arg()
+ * - ::ly_out_fd()
+ * - ::ly_out_file()
+ * - ::ly_out_filepath()
+ * - ::ly_out_memory()
+ *
+ * - ::ly_out_type()
+ * - ::ly_out_printed()
+ *
+ * - ::ly_out_reset()
+ * - ::ly_out_free()
+ *
+ * - ::ly_print()
+ * - ::ly_print_flush()
+ * - ::ly_write()
+ *
+ * libyang Printers List
+ * --------------------
+ * - @subpage howtoSchemaPrinters
+ * - @subpage howtoDataPrinters
+ */
+
+/**
+ * @struct ly_out
+ * @brief Printer output structure specifying where the data are printed.
+ */
+struct ly_out;
+
+/**
+ * @brief Common value for data as well as schema printers to avoid formatting indentations and new lines
+ */
+#define LY_PRINT_SHRINK 0x02
+
+/**
+ * @brief Types of the printer's output
+ */
+typedef enum LY_OUT_TYPE {
+ LY_OUT_ERROR = -1, /**< error value to indicate failure of the functions returning LY_OUT_TYPE */
+ LY_OUT_FD, /**< file descriptor printer */
+ LY_OUT_FDSTREAM, /**< internal replacement for LY_OUT_FD in case vdprintf() is not available */
+ LY_OUT_FILE, /**< FILE stream printer */
+ LY_OUT_FILEPATH, /**< filepath printer */
+ LY_OUT_MEMORY, /**< memory printer */
+ LY_OUT_CALLBACK /**< callback printer */
+} LY_OUT_TYPE;
+
+/**
+ * @brief Get output type of the printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @return Type of the printer's output.
+ */
+LIBYANG_API_DECL LY_OUT_TYPE ly_out_type(const struct ly_out *out);
+
+/**
+ * @brief Reset the output medium to write from its beginning, so the following printer function will rewrite the current data
+ * instead of appending.
+ *
+ * Note that in case the underlying output is not seekable (stream referring a pipe/FIFO/socket or the callback output type),
+ * nothing actually happens despite the function succeeds. Also note that the medium is not returned to the state it was when
+ * the handler was created. For example, file is seeked into the offset zero and truncated, the content from the time it was opened with
+ * ::ly_out_new_file() is not restored.
+ *
+ * @param[in] out Printer handler.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ESYS in case of failure
+ */
+LIBYANG_API_DECL LY_ERR ly_out_reset(struct ly_out *out);
+
+/**
+ * @brief Generic write callback for data printed by libyang.
+ *
+ * @param[in] user_data Optional caller-specific argument.
+ * @param[in] buf Data to write.
+ * @param[in] count Number of bytes to write.
+ * @return Number of printed bytes.
+ * @return Negative value in case of error.
+ */
+typedef ssize_t (*ly_write_clb)(void *user_data, const void *buf, size_t count);
+
+/**
+ * @brief Create printer handler using callback printer function.
+ *
+ * @param[in] writeclb Pointer to the printer callback function writing the data (see write(2)).
+ * @param[in] user_data Optional caller-specific argument to be passed to the @p writeclb callback.
+ * @param[out] out Created printer handler supposed to be passed to different ly*_print() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_EMEM in case allocating the @p out handler fails.
+ */
+LIBYANG_API_DECL LY_ERR ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out);
+
+/**
+ * @brief Get or reset callback function associated with a callback printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] writeclb Optional argument providing a new printer callback function for the handler. If NULL, only the current
+ * printer callback is returned.
+ * @return Previous printer callback.
+ */
+LIBYANG_API_DECL ly_write_clb ly_out_clb(struct ly_out *out, ly_write_clb writeclb);
+
+/**
+ * @brief Get or reset callback function's argument associated with a callback printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] arg caller-specific argument to be passed to the callback function associated with the printer handler.
+ * If NULL, only the current file descriptor value is returned.
+ * @return The previous callback argument.
+ */
+LIBYANG_API_DECL void *ly_out_clb_arg(struct ly_out *out, void *arg);
+
+/**
+ * @brief Create printer handler using file descriptor.
+ *
+ * @param[in] fd File descriptor to use.
+ * @param[out] out Created printer handler supposed to be passed to different ly*_print() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_out_new_fd(int fd, struct ly_out **out);
+
+/**
+ * @brief Get or reset file descriptor printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] fd Optional value of a new file descriptor for the handler. If -1, only the current file descriptor value is returned.
+ * @return Previous value of the file descriptor. Note that caller is responsible for closing the returned file descriptor in case of setting new descriptor @p fd.
+ * @return -1 in case of error when setting up the new file descriptor.
+ */
+LIBYANG_API_DECL int ly_out_fd(struct ly_out *out, int fd);
+
+/**
+ * @brief Create printer handler using file stream.
+ *
+ * @param[in] f File stream to use.
+ * @param[out] out Created printer handler supposed to be passed to different ly*_print() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_out_new_file(FILE *f, struct ly_out **out);
+
+/**
+ * @brief Get or reset file stream printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] f Optional new file stream for the handler. If NULL, only the current file stream is returned.
+ * @return Previous file stream of the handler. Note that caller is responsible for closing the returned stream in case of setting new stream @p f.
+ */
+LIBYANG_API_DECL FILE *ly_out_file(struct ly_out *out, FILE *f);
+
+/**
+ * @brief Create printer handler using memory to dump data.
+ *
+ * @param[in] strp Pointer to store the resulting data. If it points to a pointer to an allocated buffer and
+ * @p size of the buffer is set, the buffer is used (and extended if needed) to store the printed data.
+ * @param[in] size Size of the buffer provided via @p strp. In case it is 0, the buffer for the printed data
+ * is newly allocated even if @p strp points to a pointer to an existing buffer.
+ * @param[out] out Created printer handler supposed to be passed to different ly*_print() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_out_new_memory(char **strp, size_t size, struct ly_out **out);
+
+/**
+ * @brief Get or change memory where the data are dumped.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] strp Optional new string pointer to store the resulting data, same rules as in ::ly_out_new_memory() are applied.
+ * @param[in] size Size of the buffer provided via @p strp. In case it is 0, the buffer for the printed data
+ * is newly allocated even if @p strp points to a pointer to an existing buffer. In case the @p strp is NULL, this
+ * parameter is ignored.
+ * @return Previous dumped data. Note that the caller is responsible to free the data in case of changing string pointer @p strp.
+ */
+LIBYANG_API_DECL char *ly_out_memory(struct ly_out *out, char **strp, size_t size);
+
+/**
+ * @brief Create printer handler file of the given filename.
+ *
+ * @param[in] filepath Path of the file where to write data.
+ * @param[out] out Created printer handler supposed to be passed to different ly*_print() functions.
+ * @return NULL in case of error.
+ * @return Created printer handler supposed to be passed to different ly*_print_*() functions.
+ */
+LIBYANG_API_DECL LY_ERR ly_out_new_filepath(const char *filepath, struct ly_out **out);
+
+/**
+ * @brief Get or change the filepath of the file where the printer prints the data.
+ *
+ * Note that in case of changing the filepath, the current file is closed and a new one is
+ * created/opened instead of renaming the previous file. Also note that the previous filepath
+ * string is returned only in case of not changing it's value.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] filepath Optional new filepath for the handler. If and only if NULL, the current filepath string is returned.
+ * @return Previous filepath string in case the @p filepath argument is NULL.
+ * @return NULL if changing filepath succeeds and ((void *)-1) otherwise.
+ */
+LIBYANG_API_DECL const char *ly_out_filepath(struct ly_out *out, const char *filepath);
+
+/**
+ * @brief Generic printer of the given format string into the specified output.
+ *
+ * Alternatively, ::ly_write() can be used.
+ *
+ * @param[in] out Output specification.
+ * @param[in] format Format string to be printed.
+ * @return LY_ERR value, get number of the printed bytes using ::ly_out_printed.
+ */
+LIBYANG_API_DECL LY_ERR ly_print(struct ly_out *out, const char *format, ...);
+
+/**
+ * @brief Flush the output from any internal buffers and clean any auxiliary data.
+ * @param[in] out Output specification.
+ */
+LIBYANG_API_DECL void ly_print_flush(struct ly_out *out);
+
+/**
+ * @brief Generic printer of the given string buffer into the specified output.
+ *
+ * Alternatively, ::ly_print() can be used.
+ *
+ * @param[in] out Output specification.
+ * @param[in] buf Memory buffer with the data to print.
+ * @param[in] len Length of the data to print in the @p buf.
+ * @return LY_ERR value, get number of the printed bytes using ::ly_out_printed.
+ */
+LIBYANG_API_DECL LY_ERR ly_write(struct ly_out *out, const char *buf, size_t len);
+
+/**
+ * @brief Get the number of printed bytes by the last function.
+ *
+ * @param[in] out Out structure used.
+ * @return Number of printed bytes.
+ */
+LIBYANG_API_DECL size_t ly_out_printed(const struct ly_out *out);
+
+/**
+ * @brief Free the printer handler.
+ * @param[in] out Printer handler to free.
+ * @param[in] clb_arg_destructor Freeing function for printer callback (LY_OUT_CALLBACK) argument.
+ * @param[in] destroy Flag to free allocated buffer (for LY_OUT_MEMORY) or to
+ * close stream/file descriptor (for LY_OUT_FD, LY_OUT_FDSTREAM and LY_OUT_FILE)
+ */
+LIBYANG_API_DECL void ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_OUT_H_ */
diff --git a/src/out_internal.h b/src/out_internal.h
new file mode 100644
index 0000000..96c468c
--- /dev/null
+++ b/src/out_internal.h
@@ -0,0 +1,110 @@
+/**
+ * @file out_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internal structures and functions for libyang
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_OUT_INTERNAL_H_
+#define LY_OUT_INTERNAL_H_
+
+#include "out.h"
+
+struct lyd_node;
+
+/**
+ * @brief Printer output structure specifying where the data are printed.
+ */
+struct ly_out {
+ LY_OUT_TYPE type; /**< type of the output to select the output method */
+
+ union {
+ int fd; /**< file descriptor for LY_OUT_FD type */
+ FILE *f; /**< file structure for LY_OUT_FILE, LY_OUT_FDSTREAM and LY_OUT_FILEPATH types */
+
+ struct {
+ FILE *f; /**< file stream from the original file descriptor, variable is mapped to the LY_OUT_FILE's f */
+ int fd; /**< original file descriptor, which was not used directly because of missing vdprintf() */
+ } fdstream; /**< structure for LY_OUT_FDSTREAM type, which is LY_OUT_FD when vdprintf() is missing */
+ struct {
+ FILE *f; /**< file structure for LY_OUT_FILEPATH, variable is mapped to the LY_OUT_FILE's f */
+ char *filepath; /**< stored original filepath */
+ } fpath; /**< filepath structure for LY_OUT_FILEPATH */
+ struct {
+ char **buf; /**< storage for the pointer to the memory buffer to store the output */
+ size_t len; /**< number of used bytes in the buffer */
+ size_t size; /**< allocated size of the buffer */
+ } mem; /**< memory buffer information for LY_OUT_MEMORY type */
+ struct {
+ ssize_t (*func)(void *arg, const void *buf, size_t count); /**< callback function */
+ void *arg; /**< optional argument for the callback function */
+ } clb; /**< printer callback for LY_OUT_CALLBACK type */
+ } method; /**< type-specific information about the output */
+
+ /* LYB only */
+ char *buffered; /**< additional buffer for holes */
+ size_t buf_len; /**< number of used bytes in the additional buffer for holes */
+ size_t buf_size; /**< allocated size of the buffer for holes */
+ size_t hole_count; /**< hole counter */
+
+ size_t printed; /**< Total number of printed bytes */
+ size_t func_printed; /**< Number of bytes printed by the last function */
+};
+
+/**
+ * @brief Generic printer of the given format string into the specified output.
+ *
+ * Does not reset printed bytes. Adds to printed bytes.
+ *
+ * @param[in] out Output specification.
+ * @param[in] format Format string to be printed.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_print_(struct ly_out *out, const char *format, ...);
+
+/**
+ * @brief Generic printer of the given string buffer into the specified output.
+ *
+ * Does not reset printed bytes. Adds to printed bytes.
+ *
+ * @param[in] out Output specification.
+ * @param[in] buf Memory buffer with the data to print.
+ * @param[in] len Length of the data to print in the @p buf.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_write_(struct ly_out *out, const char *buf, size_t len);
+
+/**
+ * @brief Create a hole in the output data that will be filled later.
+ *
+ * Adds printed bytes.
+ *
+ * @param[in] out Output specification.
+ * @param[in] len Length of the created hole.
+ * @param[out] position Position of the hole, value must be later provided to the ::ly_write_skipped() call.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_write_skip(struct ly_out *out, size_t len, size_t *position);
+
+/**
+ * @brief Write data into the hole at given position.
+ *
+ * Does not change printed bytes.
+ *
+ * @param[in] out Output specification.
+ * @param[in] position Position of the hole to fill, the value was provided by ::ly_write_skip().
+ * @param[in] buf Memory buffer with the data to print.
+ * @param[in] len Length of the data to print in the @p buf. Not that the length must correspond
+ * to the len value specified in the corresponding ::ly_write_skip() call.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t len);
+
+#endif /* LY_OUT_INTERNAL_H_ */
diff --git a/src/parser_common.c b/src/parser_common.c
new file mode 100644
index 0000000..6fe068b
--- /dev/null
+++ b/src/parser_common.c
@@ -0,0 +1,3567 @@
+/**
+ * @file parser_common.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang common parser functions.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
+
+#ifdef __APPLE__
+#define _DARWIN_C_SOURCE /* F_GETPATH */
+#endif
+
+#if defined (__NetBSD__) || defined (__OpenBSD__)
+/* realpath */
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+#endif
+
+#include "parser_internal.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "compat.h"
+#include "dict.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_data.h"
+#include "path.h"
+#include "plugins_exts/metadata.h"
+#include "schema_features.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+void
+lyd_ctx_free(struct lyd_ctx *lydctx)
+{
+ ly_set_erase(&lydctx->node_types, NULL);
+ ly_set_erase(&lydctx->meta_types, NULL);
+ ly_set_erase(&lydctx->node_when, NULL);
+ ly_set_erase(&lydctx->ext_node, free);
+ ly_set_erase(&lydctx->ext_val, free);
+}
+
+LY_ERR
+lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
+{
+ const struct lyd_node *iter;
+
+ *op = NULL;
+
+ if (!parent) {
+ /* no parent, nothing to look for */
+ return LY_SUCCESS;
+ }
+
+ /* we need to find the operation node if it already exists */
+ for (iter = parent; iter; iter = lyd_parent(iter)) {
+ if (iter->schema && (iter->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) {
+ break;
+ }
+ }
+
+ if (!iter) {
+ /* no operation found */
+ return LY_SUCCESS;
+ }
+
+ if (!(int_opts & LYD_INTOPT_ANY)) {
+ if (!(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY))) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent %s \"%s\" node when not parsing any operation.",
+ lys_nodetype2str(iter->schema->nodetype), iter->schema->name);
+ return LY_EINVAL;
+ } else if ((iter->schema->nodetype == LYS_RPC) && !(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY))) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent RPC \"%s\" node when not parsing RPC nor rpc-reply.",
+ iter->schema->name);
+ return LY_EINVAL;
+ } else if ((iter->schema->nodetype == LYS_ACTION) && !(int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY))) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent action \"%s\" node when not parsing action nor rpc-reply.",
+ iter->schema->name);
+ return LY_EINVAL;
+ } else if ((iter->schema->nodetype == LYS_NOTIF) && !(int_opts & LYD_INTOPT_NOTIF)) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent notification \"%s\" node when not parsing a notification.",
+ iter->schema->name);
+ return LY_EINVAL;
+ }
+ }
+
+ *op = (struct lyd_node *)iter;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
+{
+ LY_ERR rc = LY_SUCCESS;
+
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+
+ if (lydctx->int_opts & LYD_INTOPT_ANY) {
+ /* nothing to check, everything is allowed */
+ goto cleanup;
+ }
+
+ if ((lydctx->parse_opts & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+ LOGVAL(lydctx->data_ctx->ctx, LY_VCODE_UNEXPNODE, "state", snode->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (snode->nodetype == LYS_RPC) {
+ if (lydctx->int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) {
+ if (lydctx->op_node) {
+ goto error_node_dup;
+ }
+ } else {
+ goto error_node_inval;
+ }
+ } else if (snode->nodetype == LYS_ACTION) {
+ if (lydctx->int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) {
+ if (lydctx->op_node) {
+ goto error_node_dup;
+ }
+ } else {
+ goto error_node_inval;
+ }
+ } else if (snode->nodetype == LYS_NOTIF) {
+ if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
+ if (lydctx->op_node) {
+ goto error_node_dup;
+ }
+ } else {
+ goto error_node_inval;
+ }
+ }
+
+ /* success */
+ goto cleanup;
+
+error_node_dup:
+ LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+ lys_nodetype2str(snode->nodetype), snode->name, lys_nodetype2str(lydctx->op_node->schema->nodetype),
+ lydctx->op_node->schema->name);
+ rc = LY_EVALID;
+ goto cleanup;
+
+error_node_inval:
+ LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".", lys_nodetype2str(snode->nodetype),
+ snode->name);
+ rc = LY_EVALID;
+
+cleanup:
+ LOG_LOCBACK(1, 0, 0, 0);
+ return rc;
+}
+
+LY_ERR
+lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
+ ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
+{
+ ly_bool incomplete;
+
+ LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
+
+ if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ LY_CHECK_RET(ly_set_add(&lydctx->node_types, *node, 1, NULL));
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod,
+ const char *name, size_t name_len, const void *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node)
+{
+ ly_bool incomplete;
+ struct lyd_meta *first = NULL;
+
+ if (meta && *meta) {
+ /* remember the first metadata */
+ first = *meta;
+ }
+
+ LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
+ hints, ctx_node, 0, &incomplete));
+
+ if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ LY_CHECK_RET(ly_set_add(&lydctx->meta_types, *meta, 1, NULL));
+ }
+
+ if (first) {
+ /* always return the first metadata */
+ *meta = first;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parse_check_keys(struct lyd_node *node)
+{
+ const struct lysc_node *skey = NULL;
+ const struct lyd_node *key;
+
+ assert(node->schema->nodetype == LYS_LIST);
+
+ key = lyd_child(node);
+ while ((skey = lys_getnext(skey, node->schema, NULL, 0)) && (skey->flags & LYS_KEY)) {
+ if (!key || (key->schema != skey)) {
+ LOGVAL(LYD_CTX(node), LY_VCODE_NOKEY, skey->name);
+ return LY_EVALID;
+ }
+
+ key = key->next;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
+ struct lysc_ext_instance *ext)
+{
+ struct lyd_meta *meta2, *prev_meta = NULL;
+ struct lyd_ctx_ext_val *ext_val;
+
+ if (lydctx->parse_opts & LYD_PARSE_NO_NEW) {
+ node->flags &= ~LYD_NEW;
+ }
+
+ if (lysc_has_when(node->schema)) {
+ if (lydctx->parse_opts & LYD_PARSE_WHEN_TRUE) {
+ /* the condition was true before */
+ node->flags |= LYD_WHEN_TRUE;
+ }
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* remember we need to evaluate this node's when */
+ LY_CHECK_RET(ly_set_add(&lydctx->node_when, node, 1, NULL));
+ }
+ }
+
+ LY_LIST_FOR(*meta, meta2) {
+ if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults") &&
+ meta2->value.boolean) {
+ /* node is default according to the metadata */
+ node->flags |= LYD_DEFAULT;
+
+ /* delete the metadata */
+ if (prev_meta) {
+ prev_meta->next = meta2->next;
+ } else if (meta != &node->meta) {
+ *meta = (*meta)->next;
+ }
+ lyd_free_meta_single(meta2);
+
+ /* update dflt flag for all parent NP containers */
+ lyd_cont_set_dflt(lyd_parent(node));
+ break;
+ }
+
+ prev_meta = meta2;
+ }
+
+ if (ext) {
+ /* parsed for an extension */
+ node->flags |= LYD_EXT;
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* rememeber for validation */
+ ext_val = malloc(sizeof *ext_val);
+ LY_CHECK_ERR_RET(!ext_val, LOGMEM(LYD_CTX(node)), LY_EMEM);
+ ext_val->ext = ext;
+ ext_val->sibling = node;
+ LY_CHECK_RET(ly_set_add(&lydctx->ext_val, ext_val, 1, NULL));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath)
+{
+ char path[PATH_MAX];
+
+#ifndef __APPLE__
+ char proc_path[32];
+ int len;
+#endif
+
+ LY_CHECK_ARG_RET(NULL, ctx, in, filepath, );
+ if (*filepath) {
+ /* filepath already set */
+ return;
+ }
+
+ switch (in->type) {
+ case LY_IN_FILEPATH:
+ if (realpath(in->method.fpath.filepath, path) != NULL) {
+ lydict_insert(ctx, path, 0, filepath);
+ } else {
+ lydict_insert(ctx, in->method.fpath.filepath, 0, filepath);
+ }
+
+ break;
+ case LY_IN_FD:
+#ifdef __APPLE__
+ if (fcntl(in->method.fd, F_GETPATH, path) != -1) {
+ lydict_insert(ctx, path, 0, filepath);
+ }
+#elif defined _WIN32
+ HANDLE h = _get_osfhandle(in->method.fd);
+ FILE_NAME_INFO info;
+
+ if (GetFileInformationByHandleEx(h, FileNameInfo, &info, sizeof info)) {
+ char *buf = calloc(info.FileNameLength + 1 /* trailing NULL */, MB_CUR_MAX);
+
+ len = wcstombs(buf, info.FileName, info.FileNameLength * MB_CUR_MAX);
+ lydict_insert(ctx, buf, len, filepath);
+ }
+#else
+ /* get URI if there is /proc */
+ sprintf(proc_path, "/proc/self/fd/%d", in->method.fd);
+ if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
+ lydict_insert(ctx, path, len, filepath);
+ }
+#endif
+ break;
+ case LY_IN_MEMORY:
+ case LY_IN_FILE:
+ /* nothing to do */
+ break;
+ default:
+ LOGINT(ctx);
+ break;
+ }
+}
+
+static LY_ERR lysp_stmt_container(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings);
+static LY_ERR lysp_stmt_choice(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings);
+static LY_ERR lysp_stmt_case(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings);
+static LY_ERR lysp_stmt_uses(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings);
+static LY_ERR lysp_stmt_grouping(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node_grp **groupings);
+static LY_ERR lysp_stmt_list(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings);
+
+/**
+ * @brief Validate stmt string value.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] val_type String value type.
+ * @param[in] val Value to validate.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_stmt_validate_value(struct lysp_ctx *ctx, enum yang_arg val_type, const char *val)
+{
+ uint8_t prefix = 0;
+ ly_bool first = 1;
+ uint32_t c;
+ size_t utf8_char_len;
+
+ while (*val) {
+ LY_CHECK_ERR_RET(ly_getutf8(&val, &c, &utf8_char_len),
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, (val)[-utf8_char_len]), LY_EVALID);
+
+ switch (val_type) {
+ case Y_IDENTIF_ARG:
+ LY_CHECK_RET(lysp_check_identifierchar(ctx, c, first, NULL));
+ break;
+ case Y_PREF_IDENTIF_ARG:
+ LY_CHECK_RET(lysp_check_identifierchar(ctx, c, first, &prefix));
+ break;
+ case Y_STR_ARG:
+ case Y_MAYBE_STR_ARG:
+ LY_CHECK_RET(lysp_check_stringchar(ctx, c));
+ break;
+ }
+ first = 0;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Duplicate statement siblings, recursively.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] stmt Statement to duplicate.
+ * @param[out] first First duplicated statement, the rest follow.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_stmt_dup(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_stmt **first)
+{
+ struct lysp_stmt *child, *last = NULL;
+
+ LY_LIST_FOR(stmt, stmt) {
+ child = calloc(1, sizeof *child);
+ LY_CHECK_ERR_RET(!child, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+
+ if (last) {
+ last->next = child;
+ } else {
+ assert(!*first);
+ *first = child;
+ }
+ last = child;
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->stmt, 0, &child->stmt));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &child->arg));
+ child->format = stmt->format;
+ LY_CHECK_RET(ly_dup_prefix_data(PARSER_CTX(ctx), stmt->format, stmt->prefix_data, &child->prefix_data));
+ child->flags = stmt->flags;
+ child->kw = stmt->kw;
+
+ /* recursively */
+ LY_CHECK_RET(lysp_stmt_dup(ctx, stmt->child, &child->child));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse extension instance.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] insubstmt The statement this extension instance is a substatement of.
+ * @param[in] insubstmt_index Index of the keyword instance this extension instance is a substatement of.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_ext(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt insubstmt,
+ LY_ARRAY_COUNT_TYPE insubstmt_index, struct lysp_ext_instance **exts)
+{
+ struct lysp_ext_instance *e;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *exts, e, LY_EMEM);
+
+ /* store name and insubstmt info */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->stmt, 0, &e->name));
+ e->parent_stmt = insubstmt;
+ e->parent_stmt_index = insubstmt_index;
+ e->parsed = NULL;
+ LY_CHECK_RET(lysp_stmt_dup(ctx, stmt->child, &e->child));
+
+ /* get optional argument */
+ if (stmt->arg) {
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &e->argument));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a generic text field without specific constraints. Those are contact, organization,
+ * description, etc...
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] substmt_index Index of this substatement.
+ * @param[in,out] value Place to store the parsed value.
+ * @param[in] arg Type of the YANG keyword argument (of the value).
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_text_field(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t substmt_index, const char **value,
+ enum yang_arg arg, struct lysp_ext_instance **exts)
+{
+ if (*value) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, arg, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, value));
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, substmt_index, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a qname that can have more instances such as if-feature.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] qnames Parsed qnames to add to.
+ * @param[in] arg Type of the expected argument.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_qnames(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_qname **qnames, enum yang_arg arg,
+ struct lysp_ext_instance **exts)
+{
+ struct lysp_qname *item;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, arg, stmt->arg));
+
+ /* allocate new pointer */
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *qnames, item, LY_EMEM);
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &item->str));
+ item->mod = PARSER_CUR_PMOD(ctx);
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, LY_ARRAY_COUNT(*qnames) - 1, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a generic text field that can have more instances such as base.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] texts Parsed values to add to.
+ * @param[in] arg Type of the expected argument.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_text_fields(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, const char ***texts, enum yang_arg arg,
+ struct lysp_ext_instance **exts)
+{
+ const char **item;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, arg, stmt->arg));
+
+ /* allocate new pointer */
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *texts, item, LY_EMEM);
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, item));
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, LY_ARRAY_COUNT(*texts) - 1, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the status statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_status(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+
+ if (*flags & LYS_STATUS_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "status");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if ((arg_len == ly_strlen_const("current")) && !strncmp(stmt->arg, "current", arg_len)) {
+ *flags |= LYS_STATUS_CURR;
+ } else if ((arg_len == ly_strlen_const("deprecated")) && !strncmp(stmt->arg, "deprecated", arg_len)) {
+ *flags |= LYS_STATUS_DEPRC;
+ } else if ((arg_len == ly_strlen_const("obsolete")) && !strncmp(stmt->arg, "obsolete", arg_len)) {
+ *flags |= LYS_STATUS_OBSLT;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "status");
+ return LY_EVALID;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_STATUS, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "status");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the when statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] when_p When pointer to parse to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_when(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_when **when_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_when *when;
+
+ if (*when_p) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "when");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+
+ when = calloc(1, sizeof *when);
+ LY_CHECK_ERR_RET(!when, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+ *when_p = when;
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &when->cond));
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &when->dsc, Y_STR_ARG, &when->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &when->ref, Y_STR_ARG, &when->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_WHEN, 0, &when->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "when");
+ return LY_EVALID;
+ }
+ }
+ return ret;
+}
+
+/**
+ * @brief Parse the config statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_config(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+
+ if (*flags & LYS_CONFIG_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "config");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if ((arg_len == ly_strlen_const("true")) && !strncmp(stmt->arg, "true", arg_len)) {
+ *flags |= LYS_CONFIG_W;
+ } else if ((arg_len == ly_strlen_const("false")) && !strncmp(stmt->arg, "false", arg_len)) {
+ *flags |= LYS_CONFIG_R;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "config");
+ return LY_EVALID;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_CONFIG, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "config");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the mandatory statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_mandatory(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+
+ if (*flags & LYS_MAND_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "mandatory");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if ((arg_len == ly_strlen_const("true")) && !strncmp(stmt->arg, "true", arg_len)) {
+ *flags |= LYS_MAND_TRUE;
+ } else if ((arg_len == ly_strlen_const("false")) && !strncmp(stmt->arg, "false", arg_len)) {
+ *flags |= LYS_MAND_FALSE;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "mandatory");
+ return LY_EVALID;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_MANDATORY, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "mandatory");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a restriction such as range or length.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_restr(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_restr *restr)
+{
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &restr->arg.str));
+ restr->arg.mod = PARSER_CUR_PMOD(ctx);
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->ref, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_APP_TAG:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_MESSAGE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &restr->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a restriction that can have more instances such as must.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] restrs Restrictions to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_restrs(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_restr **restrs)
+{
+ struct lysp_restr *restr;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *restrs, restr, LY_EMEM);
+ return lysp_stmt_restr(ctx, stmt, restr);
+}
+
+/**
+ * @brief Parse the anydata or anyxml statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_any(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ struct lysp_node_anydata *any;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new structure and insert into siblings */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, any, next, LY_EMEM);
+
+ any->nodetype = stmt->kw == LY_STMT_ANYDATA ? LYS_ANYDATA : LYS_ANYXML;
+ any->parent = parent;
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &any->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &any->flags, &any->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &any->dsc, Y_STR_ARG, &any->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &any->iffeatures, Y_STR_ARG, &any->exts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(lysp_stmt_mandatory(ctx, child, &any->flags, &any->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &any->musts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &any->ref, Y_STR_ARG, &any->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &any->flags, &any->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &any->when));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &any->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw),
+ (any->nodetype & LYS_ANYDATA) == LYS_ANYDATA ? lyplg_ext_stmt2str(LY_STMT_ANYDATA) : lyplg_ext_stmt2str(LY_STMT_ANYXML));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the value or position statement. Substatement of type enum statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] value Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type_enum_value_pos(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, int64_t *value, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+ char *ptr = NULL;
+ long long num = 0;
+ unsigned long long unum = 0;
+
+ if (*flags & LYS_SET_VALUE) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_VALUE;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+
+ arg_len = strlen(stmt->arg);
+ if (!arg_len || (stmt->arg[0] == '+') || ((stmt->arg[0] == '0') && (arg_len > 1)) ||
+ ((stmt->kw == LY_STMT_POSITION) && !strncmp(stmt->arg, "-0", 2))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
+ goto error;
+ }
+
+ errno = 0;
+ if (stmt->kw == LY_STMT_VALUE) {
+ num = strtoll(stmt->arg, &ptr, LY_BASE_DEC);
+ if ((num < INT64_C(-2147483648)) || (num > INT64_C(2147483647))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
+ goto error;
+ }
+ } else {
+ unum = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
+ if (unum > UINT64_C(4294967295)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
+ goto error;
+ }
+ }
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - stmt->arg) != arg_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
+ goto error;
+ }
+ if (errno == ERANGE) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
+ goto error;
+ }
+ if (stmt->kw == LY_STMT_VALUE) {
+ *value = num;
+ } else {
+ *value = unum;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw == LY_STMT_VALUE ? LY_STMT_VALUE : LY_STMT_POSITION, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+
+error:
+ return LY_EVALID;
+}
+
+/**
+ * @brief Parse the enum or bit statement. Substatement of type statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] enums Enums or bits to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type_enum(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_type_enum **enums)
+{
+ struct lysp_type_enum *enm;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, stmt->kw == LY_STMT_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, stmt->arg));
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *enums, enm, LY_EMEM);
+
+ if (stmt->kw == LY_STMT_ENUM) {
+ LY_CHECK_RET(lysp_check_enum_name(ctx, stmt->arg, strlen(stmt->arg)));
+ } /* else nothing specific for YANG_BIT */
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &enm->name));
+ CHECK_UNIQUENESS(ctx, *enums, name, lyplg_ext_stmt2str(stmt->kw), enm->name);
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &enm->dsc, Y_STR_ARG, &enm->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "if-feature", lyplg_ext_stmt2str(stmt->kw));
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &enm->iffeatures, Y_STR_ARG, &enm->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &enm->ref, Y_STR_ARG, &enm->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &enm->flags, &enm->exts));
+ break;
+ case LY_STMT_VALUE:
+ LY_CHECK_ERR_RET(stmt->kw == LY_STMT_BIT, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw),
+ lyplg_ext_stmt2str(stmt->kw)), LY_EVALID);
+ LY_CHECK_RET(lysp_stmt_type_enum_value_pos(ctx, child, &enm->value, &enm->flags, &enm->exts));
+ break;
+ case LY_STMT_POSITION:
+ LY_CHECK_ERR_RET(stmt->kw == LY_STMT_ENUM, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw),
+ lyplg_ext_stmt2str(stmt->kw)), LY_EVALID);
+ LY_CHECK_RET(lysp_stmt_type_enum_value_pos(ctx, child, &enm->value, &enm->flags, &enm->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &enm->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the fraction-digits statement. Substatement of type statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] fracdig Value to write to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type_fracdigits(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *fracdig,
+ struct lysp_ext_instance **exts)
+{
+ char *ptr;
+ size_t arg_len;
+ unsigned long long num;
+
+ if (*fracdig) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "fraction-digits");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if (!arg_len || (stmt->arg[0] == '0') || !isdigit(stmt->arg[0])) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "fraction-digits");
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - stmt->arg) != arg_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "fraction-digits");
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > LY_TYPE_DEC64_FD_MAX)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, arg_len, stmt->arg, "fraction-digits");
+ return LY_EVALID;
+ }
+ *fracdig = num;
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_FRACTION_DIGITS, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "fraction-digits");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the require-instance statement. Substatement of type statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] reqinst Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type_reqinstance(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *reqinst, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+
+ if (*flags & LYS_SET_REQINST) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "require-instance");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_REQINST;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if ((arg_len == ly_strlen_const("true")) && !strncmp(stmt->arg, "true", arg_len)) {
+ *reqinst = 1;
+ } else if ((arg_len != ly_strlen_const("false")) || strncmp(stmt->arg, "false", arg_len)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "require-instance");
+ return LY_EVALID;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_REQUIRE_INSTANCE, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "require-instance");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the modifier statement. Substatement of type pattern statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] pat Value to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type_pattern_modifier(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, const char **pat,
+ struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+ char *buf;
+
+ if ((*pat)[0] == LYSP_RESTR_PATTERN_NACK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "modifier");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if ((arg_len != ly_strlen_const("invert-match")) || strncmp(stmt->arg, "invert-match", arg_len)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "modifier");
+ return LY_EVALID;
+ }
+
+ /* replace the value in the dictionary */
+ buf = malloc(strlen(*pat) + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+ strcpy(buf, *pat);
+ lydict_remove(PARSER_CTX(ctx), *pat);
+
+ assert(buf[0] == LYSP_RESTR_PATTERN_ACK);
+ buf[0] = LYSP_RESTR_PATTERN_NACK;
+ LY_CHECK_RET(lydict_insert_zc(PARSER_CTX(ctx), buf, pat));
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_MODIFIER, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "modifier");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the pattern statement. Substatement of type statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] patterns Restrictions to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type_pattern(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_restr **patterns)
+{
+ char *buf;
+ size_t arg_len;
+ struct lysp_restr *restr;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *patterns, restr, LY_EMEM);
+ arg_len = strlen(stmt->arg);
+
+ /* add special meaning first byte */
+ buf = malloc(arg_len + 2);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+ memmove(buf + 1, stmt->arg, arg_len);
+ buf[0] = LYSP_RESTR_PATTERN_ACK; /* pattern's default regular-match flag */
+ buf[arg_len + 1] = '\0'; /* terminating NULL byte */
+ LY_CHECK_RET(lydict_insert_zc(PARSER_CTX(ctx), buf, &restr->arg.str));
+ restr->arg.mod = PARSER_CUR_PMOD(ctx);
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->ref, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_APP_TAG:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_MESSAGE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_MODIFIER:
+ PARSER_CHECK_STMTVER2_RET(ctx, "modifier", "pattern");
+ LY_CHECK_RET(lysp_stmt_type_pattern_modifier(ctx, child, &restr->arg.str, &restr->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_PATTERN, 0, &restr->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "pattern");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the deviate statement. Substatement of deviation statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] devs Array of deviates to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_deviate(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_deviate **devs, struct lysp_ext_instance **exts)
+{
+ (void)stmt;
+ (void)devs;
+ (void)exts;
+
+ /* TODO */
+ LOGERR(PARSER_CTX(ctx), LY_EINVAL, "Extension instance \"deviate\" substatement is not supported.");
+ return LY_EINVAL;
+}
+
+/**
+ * @brief Parse the deviation statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] deviations Array of deviations to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_deviation(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_deviation **deviations)
+{
+ struct lysp_deviation *dev;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *deviations, dev, LY_EMEM);
+
+ /* store nodeid */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &dev->nodeid));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &dev->dsc, Y_STR_ARG, &dev->exts));
+ break;
+ case LY_STMT_DEVIATE:
+ LY_CHECK_RET(lysp_stmt_deviate(ctx, child, &dev->deviates, &dev->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &dev->ref, Y_STR_ARG, &dev->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &dev->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_DEVIATION));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the yang-version statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[out] version Version to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_yangver(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *version, struct lysp_ext_instance **exts)
+{
+ if (*version) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "yin-element");
+ return LY_EVALID;
+ }
+
+ /* store flag */
+ if (!strcmp(stmt->arg, "1")) {
+ *version = LYS_VERSION_1_0;
+ } else if (!strcmp(stmt->arg, "1.1")) {
+ *version = LYS_VERSION_1_1;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, strlen(stmt->arg), stmt->arg, "yang-version");
+ return LY_EVALID;
+ }
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_YANG_VERSION));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the module statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] mod Module to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_module(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_module *mod)
+{
+ (void)stmt;
+ (void)mod;
+
+ /* TODO */
+ LOGERR(PARSER_CTX(ctx), LY_EINVAL, "Extension instance \"module\" substatement is not supported.");
+ return LY_EINVAL;
+}
+
+/**
+ * @brief Parse the submodule statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] submod Module to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_submodule(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_submodule *submod)
+{
+ (void)stmt;
+ (void)submod;
+
+ /* TODO */
+ LOGERR(PARSER_CTX(ctx), LY_EINVAL, "Extension instance \"submodule\" substatement is not supported.");
+ return LY_EINVAL;
+}
+
+/**
+ * @brief Parse the yin-element statement. Substatement of argument statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_yinelem(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ if (*flags & LYS_YINELEM_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "yin-element");
+ return LY_EVALID;
+ }
+
+ /* store flag */
+ if (!strcmp(stmt->arg, "true")) {
+ *flags |= LYS_YINELEM_TRUE;
+ } else if (!strcmp(stmt->arg, "false")) {
+ *flags |= LYS_YINELEM_FALSE;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, strlen(stmt->arg), stmt->arg, "yin-element");
+ return LY_EVALID;
+ }
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_YIN_ELEMENT));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the argument statement. Substatement of extension statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] ex Extension to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_argument(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_ext *ex)
+{
+ if (ex->argname) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "argument");
+ return LY_EVALID;
+ }
+
+ /* store argument name */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_PREF_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &ex->argname));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_YIN_ELEMENT:
+ LY_CHECK_RET(lysp_stmt_yinelem(ctx, child, &ex->flags, &ex->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &ex->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_ARGUMENT));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the extension statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] extensions Array of extensions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_extension(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_ext **extensions)
+{
+ struct lysp_ext *ex;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *extensions, ex, LY_EMEM);
+
+ /* store name */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &ex->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &ex->dsc, Y_STR_ARG, &ex->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &ex->ref, Y_STR_ARG, &ex->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &ex->flags, &ex->exts));
+ break;
+ case LY_STMT_ARGUMENT:
+ LY_CHECK_RET(lysp_stmt_argument(ctx, child, ex));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &ex->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_EXTENSION));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the feature statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] features Array of features to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_feature(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_feature **features)
+{
+ struct lysp_feature *feat;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *features, feat, LY_EMEM);
+
+ /* store name */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &feat->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &feat->dsc, Y_STR_ARG, &feat->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &feat->iffeatures, Y_STR_ARG, &feat->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &feat->ref, Y_STR_ARG, &feat->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &feat->flags, &feat->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &feat->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_FEATURE));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the identity statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] identities Array of identities to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_identity(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_ident **identities)
+{
+ struct lysp_ident *ident;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *identities, ident, LY_EMEM);
+
+ /* store name */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &ident->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &ident->dsc, Y_STR_ARG, &ident->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &ident->iffeatures, Y_STR_ARG, &ident->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &ident->ref, Y_STR_ARG, &ident->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &ident->flags, &ident->exts));
+ break;
+ case LY_STMT_BASE:
+ LY_CHECK_RET(lysp_stmt_text_fields(ctx, child, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &ident->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_IDENTITY));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the import statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] imports Array of imports to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_import(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_import **imports)
+{
+ struct lysp_import *imp;
+ const char *str = NULL;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *imports, imp, LY_EMEM);
+
+ /* store name */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &imp->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_PREFIX:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts));
+ LY_CHECK_RET(lysp_check_prefix(ctx, *imports, NULL, &imp->prefix), LY_EVALID);
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &imp->dsc, Y_STR_ARG, &imp->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &imp->ref, Y_STR_ARG, &imp->exts));
+ break;
+ case LY_STMT_REVISION_DATE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, stmt, 0, &str, Y_STR_ARG, &imp->exts));
+ strcpy(imp->rev, str);
+ lydict_remove(PARSER_CTX(ctx), str);
+ LY_CHECK_RET(lysp_check_date(ctx, imp->rev, LY_REV_SIZE - 1, "revision-date"));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &imp->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_IMPORT));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the include statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] includes Array of identities to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_include(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_include **includes)
+{
+ struct lysp_include *inc;
+ const char *str = NULL;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *includes, inc, LY_EMEM);
+
+ /* store name */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &inc->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &inc->dsc, Y_STR_ARG, &inc->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &inc->ref, Y_STR_ARG, &inc->exts));
+ break;
+ case LY_STMT_REVISION_DATE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, stmt, 0, &str, Y_STR_ARG, &inc->exts));
+ strcpy(inc->rev, str);
+ lydict_remove(PARSER_CTX(ctx), str);
+ LY_CHECK_RET(lysp_check_date(ctx, inc->rev, LY_REV_SIZE - 1, "revision-date"));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &inc->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_INCLUDE));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the revision statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] includes Array of identities to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_revision(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_revision **revs)
+{
+ struct lysp_revision *rev;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *revs, rev, LY_EMEM);
+
+ /* store date */
+ LY_CHECK_RET(lysp_check_date(ctx, stmt->arg, strlen(stmt->arg), "revision"));
+ strncpy(rev->date, stmt->arg, LY_REV_SIZE - 1);
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &rev->dsc, Y_STR_ARG, &rev->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &rev->ref, Y_STR_ARG, &rev->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &rev->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(LY_STMT_REVISION));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the type statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] type Type to wrote to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_type(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_type *type)
+{
+ struct lysp_type *nest_type;
+ const char *str_path = NULL;
+ LY_ERR ret;
+
+ if (type->name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "type");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_PREF_IDENTIF_ARG, stmt->arg));
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &type->name));
+ type->pmod = PARSER_CUR_PMOD(ctx);
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_BASE:
+ LY_CHECK_RET(lysp_stmt_text_fields(ctx, child, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts));
+ type->flags |= LYS_SET_BASE;
+ break;
+ case LY_STMT_BIT:
+ LY_CHECK_RET(lysp_stmt_type_enum(ctx, child, &type->bits));
+ type->flags |= LYS_SET_BIT;
+ break;
+ case LY_STMT_ENUM:
+ LY_CHECK_RET(lysp_stmt_type_enum(ctx, child, &type->enums));
+ type->flags |= LYS_SET_ENUM;
+ break;
+ case LY_STMT_FRACTION_DIGITS:
+ LY_CHECK_RET(lysp_stmt_type_fracdigits(ctx, child, &type->fraction_digits, &type->exts));
+ type->flags |= LYS_SET_FRDIGITS;
+ break;
+ case LY_STMT_LENGTH:
+ if (type->length) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(child->kw));
+ return LY_EVALID;
+ }
+ type->length = calloc(1, sizeof *type->length);
+ LY_CHECK_ERR_RET(!type->length, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+
+ LY_CHECK_RET(lysp_stmt_restr(ctx, child, type->length));
+ type->flags |= LYS_SET_LENGTH;
+ break;
+ case LY_STMT_PATH:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &str_path, Y_STR_ARG, &type->exts));
+ ret = ly_path_parse(PARSER_CTX(ctx), NULL, str_path, 0, 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &type->path);
+ lydict_remove(PARSER_CTX(ctx), str_path);
+ LY_CHECK_RET(ret);
+ type->flags |= LYS_SET_PATH;
+ break;
+ case LY_STMT_PATTERN:
+ LY_CHECK_RET(lysp_stmt_type_pattern(ctx, child, &type->patterns));
+ type->flags |= LYS_SET_PATTERN;
+ break;
+ case LY_STMT_RANGE:
+ if (type->range) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(child->kw));
+ return LY_EVALID;
+ }
+ type->range = calloc(1, sizeof *type->range);
+ LY_CHECK_ERR_RET(!type->range, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+
+ LY_CHECK_RET(lysp_stmt_restr(ctx, child, type->range));
+ type->flags |= LYS_SET_RANGE;
+ break;
+ case LY_STMT_REQUIRE_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_type_reqinstance(ctx, child, &type->require_instance, &type->flags, &type->exts));
+ /* LYS_SET_REQINST checked and set inside lysp_stmt_type_reqinstance() */
+ break;
+ case LY_STMT_TYPE:
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), type->types, nest_type, LY_EMEM);
+ LY_CHECK_RET(lysp_stmt_type(ctx, child, nest_type));
+ type->flags |= LYS_SET_TYPE;
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_TYPE, 0, &type->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "type");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the leaf statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_leaf(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ struct lysp_node_leaf *leaf;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new leaf structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, leaf, next, LY_EMEM);
+ leaf->nodetype = LYS_LEAF;
+ leaf->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &leaf->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &leaf->flags, &leaf->exts));
+ break;
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &leaf->dflt.str, Y_STR_ARG, &leaf->exts));
+ leaf->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &leaf->iffeatures, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(lysp_stmt_mandatory(ctx, child, &leaf->flags, &leaf->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &leaf->musts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &leaf->ref, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &leaf->flags, &leaf->exts));
+ break;
+ case LY_STMT_TYPE:
+ LY_CHECK_RET(lysp_stmt_type(ctx, child, &leaf->type));
+ break;
+ case LY_STMT_UNITS:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &leaf->units, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &leaf->when));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_LEAF, 0, &leaf->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "leaf");
+ return LY_EVALID;
+ }
+ }
+
+ /* mandatory substatements */
+ if (!leaf->type.name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "leaf");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the max-elements statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] max Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_maxelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t *max, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+ char *ptr;
+ unsigned long long num;
+
+ if (*flags & LYS_SET_MAX) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "max-elements");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_MAX;
+
+ /* get value */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+
+ if (!arg_len || (stmt->arg[0] == '0') || ((stmt->arg[0] != 'u') && !isdigit(stmt->arg[0]))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "max-elements");
+ return LY_EVALID;
+ }
+
+ if ((arg_len != ly_strlen_const("unbounded")) || strncmp(stmt->arg, "unbounded", arg_len)) {
+ errno = 0;
+ num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - stmt->arg) != arg_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "max-elements");
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, arg_len, stmt->arg, "max-elements");
+ return LY_EVALID;
+ }
+
+ *max = num;
+ } else {
+ /* unbounded */
+ *max = 0;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_MAX_ELEMENTS, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "max-elements");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the min-elements statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] min Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t *min, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+ char *ptr;
+ unsigned long long num;
+
+ if (*flags & LYS_SET_MIN) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "min-elements");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_MIN;
+
+ /* get value */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+
+ if (!arg_len || !isdigit(stmt->arg[0]) || ((stmt->arg[0] == '0') && (arg_len > 1))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "min-elements");
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - stmt->arg) != arg_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "min-elements");
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, arg_len, stmt->arg, "min-elements");
+ return LY_EVALID;
+ }
+ *min = num;
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_MIN_ELEMENTS, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "min-elements");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the ordered-by statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_orderedby(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ size_t arg_len;
+
+ if (*flags & LYS_ORDBY_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "ordered-by");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+ arg_len = strlen(stmt->arg);
+ if ((arg_len == ly_strlen_const("system")) && !strncmp(stmt->arg, "system", arg_len)) {
+ *flags |= LYS_MAND_TRUE;
+ } else if ((arg_len == ly_strlen_const("user")) && !strncmp(stmt->arg, "user", arg_len)) {
+ *flags |= LYS_MAND_FALSE;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "ordered-by");
+ return LY_EVALID;
+ }
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_ORDERED_BY, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "ordered-by");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the leaf-list statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_leaflist(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings)
+{
+ struct lysp_node_leaflist *llist;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new leaf-list structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, llist, next, LY_EMEM);
+ llist->nodetype = LYS_LEAFLIST;
+ llist->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &llist->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_DEFAULT:
+ PARSER_CHECK_STMTVER2_RET(ctx, "default", "leaf-list");
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &llist->dflts, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &llist->dsc, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &llist->iffeatures, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ LY_CHECK_RET(lysp_stmt_maxelements(ctx, child, &llist->max, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ LY_CHECK_RET(lysp_stmt_minelements(ctx, child, &llist->min, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &llist->musts));
+ break;
+ case LY_STMT_ORDERED_BY:
+ LY_CHECK_RET(lysp_stmt_orderedby(ctx, child, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &llist->ref, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_TYPE:
+ LY_CHECK_RET(lysp_stmt_type(ctx, child, &llist->type));
+ break;
+ case LY_STMT_UNITS:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &llist->units, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &llist->when));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_LEAF_LIST, 0, &llist->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "llist");
+ return LY_EVALID;
+ }
+ }
+
+ /* mandatory substatements */
+ if (!llist->type.name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "leaf-list");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the refine statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in,out] refines Refines to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_refine(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_refine **refines)
+{
+ struct lysp_refine *rf;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *refines, rf, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &rf->nodeid));
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &rf->dflts, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &rf->dsc, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "if-feature", "refine");
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &rf->iffeatures, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ LY_CHECK_RET(lysp_stmt_maxelements(ctx, child, &rf->max, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ LY_CHECK_RET(lysp_stmt_minelements(ctx, child, &rf->min, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &rf->musts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(lysp_stmt_mandatory(ctx, child, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &rf->ref, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_PRESENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &rf->presence, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_REFINE, 0, &rf->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "refine");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the typedef statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] typedefs Typedefs to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_typedef(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_tpdf **typedefs)
+{
+ struct lysp_tpdf *tpdf;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *typedefs, tpdf, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &tpdf->name));
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &tpdf->dflt.str, Y_STR_ARG, &tpdf->exts));
+ tpdf->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &tpdf->flags, &tpdf->exts));
+ break;
+ case LY_STMT_TYPE:
+ LY_CHECK_RET(lysp_stmt_type(ctx, child, &tpdf->type));
+ break;
+ case LY_STMT_UNITS:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_TYPEDEF, 0, &tpdf->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "typedef");
+ return LY_EVALID;
+ }
+ }
+
+ /* mandatory substatements */
+ if (!tpdf->type.name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "typedef");
+ return LY_EVALID;
+ }
+
+ /* store data for collision check */
+ if (parent && !(parent->nodetype & (LYS_GROUPING | LYS_RPC | LYS_ACTION | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF))) {
+ LY_CHECK_RET(ly_set_add(&ctx->tpdfs_nodes, parent, 0, NULL));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the input or output statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] inout_p Input/output pointer to write to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_inout(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node_action_inout *inout_p)
+{
+ if (inout_p->nodetype) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+
+ /* initiate structure */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->kw == LY_STMT_INPUT ? "input" : "output", 0, &inout_p->name));
+ inout_p->nodetype = stmt->kw == LY_STMT_INPUT ? LYS_INPUT : LYS_OUTPUT;
+ inout_p->parent = parent;
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", lyplg_ext_stmt2str(stmt->kw));
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &inout_p->node, &inout_p->child));
+ break;
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(lysp_stmt_typedef(ctx, child, &inout_p->node, &inout_p->typedefs));
+ break;
+ case LY_STMT_MUST:
+ PARSER_CHECK_STMTVER2_RET(ctx, "must", lyplg_ext_stmt2str(stmt->kw));
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &inout_p->musts));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(lysp_stmt_grouping(ctx, child, &inout_p->node, &inout_p->groupings));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, stmt->kw, 0, &inout_p->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+ }
+
+ if (!inout_p->child) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "data-def-stmt", lyplg_ext_stmt2str(stmt->kw));
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the action statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] actions Actions to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_action(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node_action **actions)
+{
+ struct lysp_node_action *act;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), actions, act, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &act->name));
+ act->nodetype = parent ? LYS_ACTION : LYS_RPC;
+ act->parent = parent;
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &act->dsc, Y_STR_ARG, &act->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &act->iffeatures, Y_STR_ARG, &act->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &act->ref, Y_STR_ARG, &act->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &act->flags, &act->exts));
+ break;
+
+ case LY_STMT_INPUT:
+ LY_CHECK_RET(lysp_stmt_inout(ctx, child, &act->node, &act->input));
+ break;
+ case LY_STMT_OUTPUT:
+ LY_CHECK_RET(lysp_stmt_inout(ctx, child, &act->node, &act->output));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(lysp_stmt_typedef(ctx, child, &act->node, &act->typedefs));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(lysp_stmt_grouping(ctx, child, &act->node, &act->groupings));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, parent ? LY_STMT_ACTION : LY_STMT_RPC, 0, &act->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), parent ? "action" : "rpc");
+ return LY_EVALID;
+ }
+ }
+
+ /* always initialize inout, they are technically present (needed for later deviations/refines) */
+ if (!act->input.nodetype) {
+ act->input.nodetype = LYS_INPUT;
+ act->input.parent = &act->node;
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), "input", 0, &act->input.name));
+ }
+ if (!act->output.nodetype) {
+ act->output.nodetype = LYS_OUTPUT;
+ act->output.parent = &act->node;
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), "output", 0, &act->output.name));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the notification statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] notifs Notifications to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_notif(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node_notif **notifs)
+{
+ struct lysp_node_notif *notif;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), notifs, notif, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &notif->name));
+ notif->nodetype = LYS_NOTIF;
+ notif->parent = parent;
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &notif->dsc, Y_STR_ARG, &notif->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &notif->iffeatures, Y_STR_ARG, &notif->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &notif->ref, Y_STR_ARG, &notif->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &notif->flags, &notif->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "notification");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &notif->node, &notif->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_case(ctx, child, &notif->node, &notif->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &notif->node, &notif->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &notif->node, &notif->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &notif->node, &notif->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &notif->node, &notif->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &notif->node, &notif->child));
+ break;
+
+ case LY_STMT_MUST:
+ PARSER_CHECK_STMTVER2_RET(ctx, "must", "notification");
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &notif->musts));
+ break;
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(lysp_stmt_typedef(ctx, child, &notif->node, &notif->typedefs));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(lysp_stmt_grouping(ctx, child, &notif->node, &notif->groupings));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_NOTIFICATION, 0, &notif->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "notification");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the grouping statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] groupings Groupings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_grouping(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node_grp **groupings)
+{
+ struct lysp_node_grp *grp;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), groupings, grp, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &grp->name));
+ grp->nodetype = LYS_GROUPING;
+ grp->parent = parent;
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &grp->dsc, Y_STR_ARG, &grp->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &grp->ref, Y_STR_ARG, &grp->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &grp->flags, &grp->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "grouping");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &grp->node, &grp->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &grp->node, &grp->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &grp->node, &grp->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &grp->node, &grp->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &grp->node, &grp->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &grp->node, &grp->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &grp->node, &grp->child));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(lysp_stmt_typedef(ctx, child, &grp->node, &grp->typedefs));
+ break;
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "grouping");
+ LY_CHECK_RET(lysp_stmt_action(ctx, child, &grp->node, &grp->actions));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(lysp_stmt_grouping(ctx, child, &grp->node, &grp->groupings));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "grouping");
+ LY_CHECK_RET(lysp_stmt_notif(ctx, child, &grp->node, &grp->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_GROUPING, 0, &grp->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "grouping");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the augment statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] augments Augments to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_augment(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node_augment **augments)
+{
+ struct lysp_node_augment *aug;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_STR_ARG, stmt->arg));
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), augments, aug, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &aug->nodeid));
+ aug->nodetype = LYS_AUGMENT;
+ aug->parent = parent;
+
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &aug->dsc, Y_STR_ARG, &aug->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &aug->iffeatures, Y_STR_ARG, &aug->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &aug->ref, Y_STR_ARG, &aug->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &aug->flags, &aug->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &aug->when));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "augment");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_CASE:
+ LY_CHECK_RET(lysp_stmt_case(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &aug->node, &aug->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &aug->node, &aug->child));
+ break;
+
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "augment");
+ LY_CHECK_RET(lysp_stmt_action(ctx, child, &aug->node, &aug->actions));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "augment");
+ LY_CHECK_RET(lysp_stmt_notif(ctx, child, &aug->node, &aug->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_AUGMENT, 0, &aug->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "augment");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the uses statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_uses(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings)
+{
+ struct lysp_node_uses *uses;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_PREF_IDENTIF_ARG, stmt->arg));
+
+ /* create uses structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, uses, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &uses->name));
+ uses->nodetype = LYS_USES;
+ uses->parent = parent;
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &uses->dsc, Y_STR_ARG, &uses->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &uses->iffeatures, Y_STR_ARG, &uses->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &uses->ref, Y_STR_ARG, &uses->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &uses->flags, &uses->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &uses->when));
+ break;
+
+ case LY_STMT_REFINE:
+ LY_CHECK_RET(lysp_stmt_refine(ctx, child, &uses->refines));
+ break;
+ case LY_STMT_AUGMENT:
+ LY_CHECK_RET(lysp_stmt_augment(ctx, child, &uses->node, &uses->augments));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_USES, 0, &uses->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "uses");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the case statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_case(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings)
+{
+ struct lysp_node_case *cas;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new case structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, cas, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &cas->name));
+ cas->nodetype = LYS_CASE;
+ cas->parent = parent;
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &cas->dsc, Y_STR_ARG, &cas->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &cas->iffeatures, Y_STR_ARG, &cas->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &cas->ref, Y_STR_ARG, &cas->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &cas->flags, &cas->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &cas->when));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "case");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &cas->node, &cas->child));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_CASE, 0, &cas->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "case");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the choice statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_choice(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings)
+{
+ struct lysp_node_choice *choice;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new choice structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, choice, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &choice->name));
+ choice->nodetype = LYS_CHOICE;
+ choice->parent = parent;
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &choice->flags, &choice->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &choice->dsc, Y_STR_ARG, &choice->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &choice->iffeatures, Y_STR_ARG, &choice->exts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(lysp_stmt_mandatory(ctx, child, &choice->flags, &choice->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &choice->ref, Y_STR_ARG, &choice->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &choice->flags, &choice->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &choice->when));
+ break;
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &choice->dflt.str, Y_PREF_IDENTIF_ARG, &choice->exts));
+ choice->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "choice");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_CASE:
+ LY_CHECK_RET(lysp_stmt_case(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_CHOICE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "choice", "choice");
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &choice->node, &choice->child));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_CHOICE, 0, &choice->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "choice");
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the container statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_container(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings)
+{
+ struct lysp_node_container *cont;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new container structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, cont, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &cont->name));
+ cont->nodetype = LYS_CONTAINER;
+ cont->parent = parent;
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &cont->flags, &cont->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &cont->dsc, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &cont->iffeatures, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &cont->ref, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &cont->flags, &cont->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &cont->when));
+ break;
+ case LY_STMT_PRESENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &cont->presence, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "container");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &cont->node, &cont->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &cont->node, &cont->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &cont->node, &cont->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &cont->node, &cont->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &cont->node, &cont->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &cont->node, &cont->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &cont->node, &cont->child));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(lysp_stmt_typedef(ctx, child, &cont->node, &cont->typedefs));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &cont->musts));
+ break;
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "container");
+ LY_CHECK_RET(lysp_stmt_action(ctx, child, &cont->node, &cont->actions));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(lysp_stmt_grouping(ctx, child, &cont->node, &cont->groupings));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "container");
+ LY_CHECK_RET(lysp_stmt_notif(ctx, child, &cont->node, &cont->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_CONTAINER, 0, &cont->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "container");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the list statement.
+ *
+ * @param[in] ctx parser context.
+ * @param[in] stmt Source statement data from the parsed extension instance.
+ * @param[in] parent Parent node to connect to (not into).
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lysp_stmt_list(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_node *parent,
+ struct lysp_node **siblings)
+{
+ struct lysp_node_list *list;
+
+ LY_CHECK_RET(lysp_stmt_validate_value(ctx, Y_IDENTIF_ARG, stmt->arg));
+
+ /* create new list structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, list, next, LY_EMEM);
+
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), stmt->arg, 0, &list->name));
+ list->nodetype = LYS_LIST;
+ list->parent = parent;
+
+ /* parse substatements */
+ for (const struct lysp_stmt *child = stmt->child; child; child = child->next) {
+ switch (child->kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(lysp_stmt_config(ctx, child, &list->flags, &list->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &list->dsc, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &list->iffeatures, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &list->ref, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(lysp_stmt_status(ctx, child, &list->flags, &list->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(lysp_stmt_when(ctx, child, &list->when));
+ break;
+ case LY_STMT_KEY:
+ LY_CHECK_RET(lysp_stmt_text_field(ctx, child, 0, &list->key, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ LY_CHECK_RET(lysp_stmt_maxelements(ctx, child, &list->max, &list->flags, &list->exts));
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ LY_CHECK_RET(lysp_stmt_minelements(ctx, child, &list->min, &list->flags, &list->exts));
+ break;
+ case LY_STMT_ORDERED_BY:
+ LY_CHECK_RET(lysp_stmt_orderedby(ctx, child, &list->flags, &list->exts));
+ break;
+ case LY_STMT_UNIQUE:
+ LY_CHECK_RET(lysp_stmt_qnames(ctx, child, &list->uniques, Y_STR_ARG, &list->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "list");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(lysp_stmt_any(ctx, child, &list->node, &list->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(lysp_stmt_choice(ctx, child, &list->node, &list->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(lysp_stmt_container(ctx, child, &list->node, &list->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(lysp_stmt_leaf(ctx, child, &list->node, &list->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(lysp_stmt_leaflist(ctx, child, &list->node, &list->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(lysp_stmt_list(ctx, child, &list->node, &list->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(lysp_stmt_uses(ctx, child, &list->node, &list->child));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(lysp_stmt_typedef(ctx, child, &list->node, &list->typedefs));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(lysp_stmt_restrs(ctx, child, &list->musts));
+ break;
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "list");
+ LY_CHECK_RET(lysp_stmt_action(ctx, child, &list->node, &list->actions));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(lysp_stmt_grouping(ctx, child, &list->node, &list->groupings));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "list");
+ LY_CHECK_RET(lysp_stmt_notif(ctx, child, &list->node, &list->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(lysp_stmt_ext(ctx, child, LY_STMT_LIST, 0, &list->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(child->kw), "list");
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse generic statement structure into a specific parsed-schema structure.
+ *
+ * @param[in] pctx Parse context of the @p stmt being processed.
+ * @param[in] stmt Generic statement structure to process.
+ * @param[out] result Specific parsed-schema structure for the given statement. For the specific type for the particular statement, check the function code.
+ * @param[in,out] exts [sized array](@ref sizedarrays) For extension instances in case of statements that do not store extension instances in their own list.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_stmt_parse(struct lysp_ctx *pctx, const struct lysp_stmt *stmt, void **result, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint16_t flags;
+
+ switch (stmt->kw) {
+ case LY_STMT_NOTIFICATION:
+ ret = lysp_stmt_notif(pctx, stmt, NULL, (struct lysp_node_notif **)result);
+ break;
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT: {
+ struct lysp_node_action_inout *inout;
+
+ *result = inout = calloc(1, sizeof *inout);
+ LY_CHECK_ERR_RET(!inout, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ ret = lysp_stmt_inout(pctx, stmt, NULL, inout);
+ break;
+ }
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ ret = lysp_stmt_action(pctx, stmt, NULL, (struct lysp_node_action **)result);
+ break;
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ ret = lysp_stmt_any(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_AUGMENT:
+ ret = lysp_stmt_augment(pctx, stmt, NULL, (struct lysp_node_augment **)result);
+ break;
+ case LY_STMT_CASE:
+ ret = lysp_stmt_case(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_CHOICE:
+ ret = lysp_stmt_choice(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_CONTAINER:
+ ret = lysp_stmt_container(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_GROUPING:
+ ret = lysp_stmt_grouping(pctx, stmt, NULL, (struct lysp_node_grp **)result);
+ break;
+ case LY_STMT_LEAF:
+ ret = lysp_stmt_leaf(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_LEAF_LIST:
+ ret = lysp_stmt_leaflist(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_LIST:
+ ret = lysp_stmt_list(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_USES:
+ ret = lysp_stmt_uses(pctx, stmt, NULL, (struct lysp_node **)result);
+ break;
+ case LY_STMT_BASE:
+ ret = lysp_stmt_text_fields(pctx, stmt, (const char ***)result, Y_PREF_IDENTIF_ARG, exts);
+ break;
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_KEY:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_UNITS:
+ ret = lysp_stmt_text_field(pctx, stmt, 0, (const char **)result, Y_STR_ARG, exts);
+ break;
+ case LY_STMT_BIT:
+ case LY_STMT_ENUM:
+ ret = lysp_stmt_type_enum(pctx, stmt, (struct lysp_type_enum **)result);
+ break;
+ case LY_STMT_CONFIG:
+ assert(*result);
+ ret = lysp_stmt_config(pctx, stmt, *(uint16_t **)result, exts);
+ break;
+ case LY_STMT_DEFAULT:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_UNIQUE:
+ ret = lysp_stmt_qnames(pctx, stmt, (struct lysp_qname **)result, Y_STR_ARG, exts);
+ break;
+ case LY_STMT_DEVIATE:
+ ret = lysp_stmt_deviate(pctx, stmt, (struct lysp_deviate **)result, exts);
+ break;
+ case LY_STMT_DEVIATION:
+ ret = lysp_stmt_deviation(pctx, stmt, (struct lysp_deviation **)result);
+ break;
+ case LY_STMT_EXTENSION:
+ ret = lysp_stmt_extension(pctx, stmt, (struct lysp_ext **)result);
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ ret = lysp_stmt_ext(pctx, stmt, LY_STMT_EXTENSION_INSTANCE, 0, (struct lysp_ext_instance **)result);
+ break;
+ case LY_STMT_FEATURE:
+ ret = lysp_stmt_feature(pctx, stmt, (struct lysp_feature **)result);
+ break;
+ case LY_STMT_FRACTION_DIGITS:
+ ret = lysp_stmt_type_fracdigits(pctx, stmt, *(uint8_t **)result, exts);
+ break;
+ case LY_STMT_LENGTH:
+ case LY_STMT_RANGE: {
+ struct lysp_restr *restr;
+
+ *result = restr = calloc(1, sizeof *restr);
+ LY_CHECK_ERR_RET(!restr, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+
+ ret = lysp_stmt_restr(pctx, stmt, restr);
+ break;
+ }
+ case LY_STMT_MUST:
+ ret = lysp_stmt_restrs(pctx, stmt, (struct lysp_restr **)result);
+ break;
+ case LY_STMT_IDENTITY:
+ ret = lysp_stmt_identity(pctx, stmt, (struct lysp_ident **)result);
+ break;
+ case LY_STMT_IMPORT:
+ ret = lysp_stmt_import(pctx, stmt, (struct lysp_import **)result);
+ break;
+ case LY_STMT_INCLUDE:
+ ret = lysp_stmt_include(pctx, stmt, (struct lysp_include **)result);
+ break;
+ case LY_STMT_MANDATORY:
+ ret = lysp_stmt_mandatory(pctx, stmt, *(uint16_t **)result, exts);
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ flags = 0;
+ ret = lysp_stmt_maxelements(pctx, stmt, *(uint32_t **)result, &flags, exts);
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ flags = 0;
+ ret = lysp_stmt_minelements(pctx, stmt, *(uint32_t **)result, &flags, exts);
+ break;
+ case LY_STMT_MODIFIER:
+ ret = lysp_stmt_type_pattern_modifier(pctx, stmt, (const char **)result, exts);
+ break;
+ case LY_STMT_MODULE: {
+ struct lysp_module *mod;
+
+ *result = mod = calloc(1, sizeof *mod);
+ LY_CHECK_ERR_RET(!mod, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ ret = lysp_stmt_module(pctx, stmt, mod);
+ break;
+ }
+ case LY_STMT_ORDERED_BY:
+ ret = lysp_stmt_orderedby(pctx, stmt, *(uint16_t **)result, exts);
+ break;
+ case LY_STMT_PATH: {
+ const char *str_path = NULL;
+
+ LY_CHECK_RET(lysp_stmt_text_field(pctx, stmt, 0, &str_path, Y_STR_ARG, exts));
+ ret = ly_path_parse(PARSER_CTX(pctx), NULL, str_path, 0, 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, (struct lyxp_expr **)result);
+ lydict_remove(PARSER_CTX(pctx), str_path);
+ break;
+ }
+ case LY_STMT_PATTERN:
+ ret = lysp_stmt_type_pattern(pctx, stmt, (struct lysp_restr **)result);
+ break;
+ case LY_STMT_POSITION:
+ case LY_STMT_VALUE:
+ flags = 0;
+ ret = lysp_stmt_type_enum_value_pos(pctx, stmt, *(int64_t **)result, &flags, exts);
+ break;
+ case LY_STMT_PREFIX:
+ ret = lysp_stmt_text_field(pctx, stmt, 0, (const char **)result, Y_IDENTIF_ARG, exts);
+ break;
+ case LY_STMT_REFINE:
+ ret = lysp_stmt_refine(pctx, stmt, (struct lysp_refine **)result);
+ break;
+ case LY_STMT_REQUIRE_INSTANCE:
+ flags = 0;
+ ret = lysp_stmt_type_reqinstance(pctx, stmt, *(uint8_t **)result, &flags, exts);
+ break;
+ case LY_STMT_REVISION:
+ ret = lysp_stmt_revision(pctx, stmt, (struct lysp_revision **)result);
+ break;
+ case LY_STMT_STATUS:
+ ret = lysp_stmt_status(pctx, stmt, (uint16_t *)result, exts);
+ break;
+ case LY_STMT_SUBMODULE: {
+ struct lysp_submodule *submod;
+
+ *result = submod = calloc(1, sizeof *submod);
+ LY_CHECK_ERR_RET(!submod, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ ret = lysp_stmt_submodule(pctx, stmt, submod);
+ break;
+ }
+ case LY_STMT_TYPE: {
+ struct lysp_type *type;
+
+ *result = type = calloc(1, sizeof *type);
+ LY_CHECK_ERR_RET(!type, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ ret = lysp_stmt_type(pctx, stmt, type);
+ break;
+ }
+ case LY_STMT_TYPEDEF:
+ ret = lysp_stmt_typedef(pctx, stmt, NULL, (struct lysp_tpdf **)result);
+ break;
+ case LY_STMT_WHEN:
+ ret = lysp_stmt_when(pctx, stmt, (struct lysp_when **)result);
+ break;
+ case LY_STMT_YANG_VERSION:
+ ret = lysp_stmt_yangver(pctx, stmt, *(uint8_t **)result, exts);
+ break;
+ case LY_STMT_YIN_ELEMENT:
+ ret = lysp_stmt_yinelem(pctx, stmt, *(uint16_t **)result, exts);
+ break;
+ default:
+ LOGINT(PARSER_CTX(pctx));
+ return LY_EINT;
+ }
+
+ return ret;
+}
+
+LY_ERR
+lys_parse_ext_instance_stmt(struct lysp_ctx *pctx, struct lysp_ext_substmt *substmt, struct lysp_stmt *stmt)
+{
+ LY_ERR rc = LY_SUCCESS;
+
+ if (!substmt->storage) {
+ /* nothing to parse, ignored */
+ goto cleanup;
+ }
+
+ switch (stmt->kw) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_AUGMENT:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_GROUPING:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_USES: {
+ struct lysp_node **pnodes_p, *pnode = NULL;
+
+ /* parse the node */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(pctx, stmt, (void **)&pnode, NULL), cleanup);
+
+ /* usually is a linked-list of all the parsed schema nodes */
+ pnodes_p = substmt->storage;
+ while (*pnodes_p) {
+ pnodes_p = &(*pnodes_p)->next;
+ }
+ *pnodes_p = pnode;
+
+ break;
+ }
+ case LY_STMT_BASE:
+ case LY_STMT_BIT:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_ENUM:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_EXTENSION_INSTANCE:
+ case LY_STMT_FEATURE:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ case LY_STMT_MUST:
+ case LY_STMT_PATTERN:
+ case LY_STMT_REFINE:
+ case LY_STMT_REVISION:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_UNIQUE:
+ /* parse, sized array */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(pctx, stmt, substmt->storage, NULL), cleanup);
+ break;
+
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_KEY:
+ case LY_STMT_LENGTH:
+ case LY_STMT_MANDATORY:
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_MODULE:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PATH:
+ case LY_STMT_POSITION:
+ case LY_STMT_PREFIX:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_RANGE:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_REQUIRE_INSTANCE:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPE:
+ case LY_STMT_UNITS:
+ case LY_STMT_VALUE:
+ case LY_STMT_WHEN:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ /* single item */
+ if (*(void **)substmt->storage) {
+ LOGVAL(PARSER_CTX(pctx), LY_VCODE_DUPSTMT, stmt->stmt);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parse */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(pctx, stmt, substmt->storage, NULL), cleanup);
+ break;
+
+ case LY_STMT_CONFIG:
+ /* single item */
+ if ((*(uint16_t *)substmt->storage) & LYS_CONFIG_MASK) {
+ LOGVAL(PARSER_CTX(pctx), LY_VCODE_DUPSTMT, stmt->stmt);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parse */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(pctx, stmt, substmt->storage, NULL), cleanup);
+ break;
+
+ case LY_STMT_ORDERED_BY:
+ /* single item */
+ if ((*(uint16_t *)substmt->storage) & LYS_ORDBY_MASK) {
+ LOGVAL(PARSER_CTX(pctx), LY_VCODE_DUPSTMT, stmt->stmt);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parse */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(pctx, stmt, substmt->storage, NULL), cleanup);
+ break;
+
+ case LY_STMT_STATUS:
+ /* single item */
+ if ((*(uint16_t *)substmt->storage) & LYS_STATUS_MASK) {
+ LOGVAL(PARSER_CTX(pctx), LY_VCODE_DUPSTMT, stmt->stmt);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parse */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(pctx, stmt, substmt->storage, NULL), cleanup);
+ break;
+
+ default:
+ LOGINT(PARSER_CTX(pctx));
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+cleanup:
+ return rc;
+}
diff --git a/src/parser_data.h b/src/parser_data.h
new file mode 100644
index 0000000..050ced3
--- /dev/null
+++ b/src/parser_data.h
@@ -0,0 +1,461 @@
+/**
+ * @file parser_data.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Data parsers for libyang
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PARSER_DATA_H_
+#define LY_PARSER_DATA_H_
+
+#include "tree_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_in;
+
+/**
+ * @page howtoDataParsers Parsing Data
+ *
+ * Data parser allows to read instances from a specific format. libyang supports the following data formats:
+ *
+ * - XML
+ *
+ * Original data format used in NETCONF protocol. XML mapping is part of the YANG specification
+ * ([RFC 6020](http://tools.ietf.org/html/rfc6020)).
+ *
+ * - JSON
+ *
+ * The alternative data format available in RESTCONF protocol. Specification of JSON encoding of data modeled by YANG
+ * can be found in [RFC 7951](http://tools.ietf.org/html/rfc7951). The specification does not cover RPCs, actions and
+ * Notifications, so the representation of these data trees is proprietary and corresponds to the representation of these
+ * trees in XML.
+ *
+ * While the parsers themselves process the input data only syntactically, all the parser functions actually incorporate
+ * the [common validator](@ref howtoDataValidation) checking the input data semantically. Therefore, the parser functions
+ * accepts two groups of options - @ref dataparseroptions and @ref datavalidationoptions.
+ *
+ * In contrast to the schema parser, data parser also accepts empty input data if such an empty data tree is valid
+ * according to the schemas in the libyang context (i.e. there are no top level mandatory nodes).
+ *
+ * There are individual functions to process different types of the data instances trees:
+ * - ::lyd_parse_data() is intended for standard configuration data trees. According to the given
+ * [parser options](@ref dataparseroptions), the caller can further specify which kind of data tree is expected:
+ * - *complete :running datastore*: this is the default case, possibly with the use of (some of) the
+ * ::LYD_PARSE_STRICT, ::LYD_PARSE_OPAQ or ::LYD_VALIDATE_PRESENT options.
+ * - *complete configuration-only datastore* (such as :startup): in this case it is necessary to except all state data
+ * using ::LYD_PARSE_NO_STATE option.
+ * - *incomplete datastore*: there are situation when the data tree is incomplete or invalid by specification. For
+ * example the *:operational* datastore is not necessarily valid and results of the NETCONF's \<get\> or \<get-config\>
+ * oprations used with filters will be incomplete (and thus invalid). This can be allowed using ::LYD_PARSE_ONLY,
+ * the ::LYD_PARSE_NO_STATE should be used for the data returned by \<get-config\> operation.
+ * - ::lyd_parse_ext_data() is used for parsing configuration data trees defined inside extension instances, such as
+ * instances of yang-data extension specified in [RFC 8040](http://tools.ietf.org/html/rfc8040).
+ * - ::lyd_parse_op() is used for parsing RPCs/actions, replies, and notifications. Even NETCONF rpc, rpc-reply, and
+ * notification messages are supported.
+ * - ::lyd_parse_ext_op() is used for parsing RPCs/actions, replies, and notifications defined inside extension instances.
+ *
+ * Further information regarding the processing input instance data can be found on the following pages.
+ * - @subpage howtoDataValidation
+ * - @subpage howtoDataWD
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_parse_data()
+ * - ::lyd_parse_data_mem()
+ * - ::lyd_parse_data_fd()
+ * - ::lyd_parse_data_path()
+ * - ::lyd_parse_ext_data()
+ * - ::lyd_parse_op()
+ * - ::lyd_parse_ext_op()
+ */
+
+/**
+ * @page howtoDataValidation Validating Data
+ *
+ * Data validation is performed implicitly to the input data processed by the [parser](@ref howtoDataParsers) and
+ * on demand via the lyd_validate_*() functions. The explicit validation process is supposed to be used when a (complex or
+ * simple) change is done on the data tree (via [data manipulation](@ref howtoDataManipulation) functions) and the data
+ * tree is expected to be valid (it doesn't make sense to validate modified result of filtered \<get\> operation).
+ *
+ * Similarly to the [data parser](@ref howtoDataParsers), there are individual functions to validate standard data tree
+ * (::lyd_validate_all()) and RPC, Action and Notification (::lyd_validate_op()). For the standard data trees, it is possible
+ * to modify the validation process by @ref datavalidationoptions. This way the state data can be prohibited
+ * (::LYD_VALIDATE_NO_STATE) and checking for mandatory nodes can be limited to the YANG modules with already present data
+ * instances (::LYD_VALIDATE_PRESENT). Validation of the standard data tree can be also limited with ::lyd_validate_module()
+ * function, which scopes only to a specified single YANG module.
+ *
+ * Since the operation data trees (RPCs, Actions or Notifications) can reference (leafref, instance-identifier, when/must
+ * expressions) data from a datastore tree, ::lyd_validate_op() may require additional data tree to be provided. This is a
+ * difference in contrast to the parsing process, when the data are loaded from an external source and invalid reference
+ * outside the operation tree is acceptable.
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_validate_all()
+ * - ::lyd_validate_module()
+ * - ::lyd_validate_op()
+ */
+
+/**
+ * @addtogroup datatree
+ * @{
+ */
+
+/**
+ * @ingroup datatree
+ * @defgroup dataparseroptions Data parser options
+ *
+ * Various options to change the data tree parsers behavior.
+ *
+ * Default parser behavior:
+ * - complete input file is always parsed. In case of XML, even not well-formed XML document (multiple top-level
+ * elements) is parsed in its entirety,
+ * - parser silently ignores data without matching schema node definition (for LYB format an error),
+ * - list instances are checked whether they have all the keys, error is raised if not.
+ *
+ * Default parser validation behavior:
+ * - the provided data are expected to provide complete datastore content (both the configuration and state data)
+ * and performs data validation according to all YANG rules, specifics follow,
+ * - list instances are expected to have all the keys (it is not checked),
+ * - instantiated (status) obsolete data print a warning,
+ * - all types are fully resolved (leafref/instance-identifier targets, unions) and must be valid (lists have
+ * all the keys, leaf(-lists) correct values),
+ * - when statements on existing nodes are evaluated, if not satisfied, a validation error is raised,
+ * - invalid multiple data instances/data from several cases cause a validation error,
+ * - implicit nodes (NP containers and default values) are added.
+ * @{
+ */
+/* note: keep the lower 16bits free for use by LYD_VALIDATE_ flags. They are not supposed to be combined together,
+ * but since they are used (as a separate parameter) together in some functions, we want to keep them in a separated
+ * range to be able detect that the caller put wrong flags into the parser/validate options parameter. */
+#define LYD_PARSE_ONLY 0x010000 /**< Data will be only parsed and no validation will be performed. When statements
+ are kept unevaluated, union types may not be fully resolved, and
+ default values are not added (only the ones parsed are present). */
+#define LYD_PARSE_STRICT 0x020000 /**< Instead of silently ignoring data without schema definition raise an error.
+ Do not combine with ::LYD_PARSE_OPAQ (except for ::LYD_LYB). */
+#define LYD_PARSE_OPAQ 0x040000 /**< Instead of silently ignoring data without definition, parse them into
+ an opaq node. Do not combine with ::LYD_PARSE_STRICT (except for ::LYD_LYB). */
+#define LYD_PARSE_NO_STATE 0x080000 /**< Forbid state data in the parsed data. Usually used with ::LYD_VALIDATE_NO_STATE. */
+
+#define LYD_PARSE_LYB_MOD_UPDATE 0x100000 /**< Only for LYB format, allow parsing data printed using a specific module
+ revision to be loaded even with a module with the same name but newer
+ revision. */
+#define LYD_PARSE_ORDERED 0x200000 /**< Do not search for the correct place of each node but instead expect
+ that the nodes are being parsed in the correct schema-based order,
+ which is always true if the data were printed by libyang and not
+ modified manually. If this flag is used incorrectly (for unordered data),
+ the behavior is undefined and most functions executed with these
+ data will not work correctly. */
+#define LYD_PARSE_SUBTREE 0x400000 /**< Parse only the current data subtree with any descendants, no siblings.
+ Also, a new return value ::LY_ENOT is returned if there is a sibling
+ subtree following in the input data. */
+#define LYD_PARSE_WHEN_TRUE 0x800000 /**< Mark all the parsed nodes dependend on a when condition with the flag
+ that means the condition was satisifed before. This allows for
+ auto-deletion of these nodes during validation. */
+#define LYD_PARSE_NO_NEW 0x1000000 /**< Do not set ::LYD_NEW (non-validated node flag) for any nodes. Use
+ when parsing validated data to skip some validation tasks and modify
+ some validation behavior (auto-deletion of cases). */
+
+#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */
+
+/** @} dataparseroptions */
+
+/**
+ * @ingroup datatree
+ * @defgroup datavalidationoptions Data validation options
+ *
+ * Various options to change data validation behaviour, both for the parser and separate validation.
+ *
+ * Default separate validation behavior:
+ * - the provided data are expected to provide complete datastore content (both the configuration and state data)
+ * and performs data validation according to all YANG rules, specifics follow,
+ * - instantiated (status) obsolete data print a warning,
+ * - all types are fully resolved (leafref/instance-identifier targets, unions) and must be valid (lists have
+ * all the keys, leaf(-lists) correct values),
+ * - when statements on existing nodes are evaluated. Depending on the previous when state (from previous validation
+ * or parsing), the node is silently auto-deleted if the state changed from true to false, otherwise a validation error
+ * is raised if it evaluates to false,
+ * - if-feature statements are evaluated,
+ * - data from several cases behave based on their previous state (from previous validation or parsing). If there existed
+ * already a case and another one was added, the previous one is silently auto-deleted. Otherwise (if data from 2 or
+ * more cases were created) a validation error is raised,
+ * - default values are added.
+ *
+ * @{
+ */
+#define LYD_VALIDATE_NO_STATE 0x0001 /**< Consider state data not allowed and raise an error if they are found.
+ Also, no implicit state data are added. */
+#define LYD_VALIDATE_PRESENT 0x0002 /**< Validate only modules whose data actually exist. */
+
+#define LYD_VALIDATE_OPTS_MASK 0x0000FFFF /**< Mask for all the LYD_VALIDATE_* options. */
+
+/** @} datavalidationoptions */
+
+/**
+ * @brief Parse (and validate) data from the input handler as a YANG data tree.
+ *
+ * @param[in] ctx Context to connect with the tree being built here.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] in The input handle to provide the dumped data in the specified @p format to parse (and validate).
+ * @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree. If @p parent is set, set to NULL.
+ * @return LY_SUCCESS in case of successful parsing (and validation).
+ * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
+
+/**
+ * @brief Parse (and validate) input data as a YANG data tree.
+ *
+ * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options.
+ *
+ * @param[in] ctx Context to connect with the tree being built here.
+ * @param[in] data The input data in the specified @p format to parse (and validate).
+ * @param[in] format Format of the input data to be parsed.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree
+ * @return LY_SUCCESS in case of successful parsing (and validation).
+ * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options, struct lyd_node **tree);
+
+/**
+ * @brief Parse (and validate) input data as a YANG data tree.
+ *
+ * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options.
+ *
+ * @param[in] ctx Context to connect with the tree being built here.
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the input data in the
+ * specified @p format to parse.
+ * @param[in] format Format of the input data to be parsed.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree
+ * @return LY_SUCCESS in case of successful parsing (and validation).
+ * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_data_fd(const struct ly_ctx *ctx, int fd, LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options, struct lyd_node **tree);
+
+/**
+ * @brief Parse (and validate) input data as a YANG data tree.
+ *
+ * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options.
+ *
+ * @param[in] ctx Context to connect with the tree being built here.
+ * @param[in] path Path to the file with the input data in the specified @p format to parse (and validate).
+ * @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from @p path extension.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree
+ * @return LY_SUCCESS in case of successful parsing (and validation).
+ * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT format,
+ uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
+
+/**
+ * @brief Parse (and validate) data from the input handler as an extension data tree following the schema tree of the given
+ * extension instance.
+ *
+ * Note that the data being parsed are limited only to the schema tree specified by the given extension, it does not allow
+ * to mix them with the standard data from any module.
+ *
+ * Directly applicable to data defined as [yang-data](@ref howtoDataYangdata).
+ *
+ * @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] in The input handle to provide the dumped data in the specified @p format to parse (and validate).
+ * @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree. If @p parent is set, set to NULL.
+ * @return LY_SUCCESS in case of successful parsing (and validation).
+ * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_ext_data(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in,
+ LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
+
+/**
+ * @ingroup datatree
+ * @defgroup datatype Data operation type
+ *
+ * Operation provided to ::lyd_validate_op() to validate.
+ *
+ * The operation cannot be determined automatically since RPC/action and a reply to it share the common top level node
+ * referencing the RPC/action schema node and may not have any input/output children to use for distinction.
+ *
+ * @{
+ */
+enum lyd_type {
+ LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
+ LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
+ including all parents in case of an action */
+ LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
+ LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
+ including all parents in case of an action */
+
+ LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
+ [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
+ LYD_TYPE_REPLY_NETCONF /* complete NETCONF RPC reply as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+};
+/** @} datatype */
+
+/**
+ * @brief Parse YANG data into an operation data tree.
+ *
+ * At least one of @p parent, @p tree, or @p op must always be set.
+ *
+ * Specific @p data_type values have different parameter meaning as follows:
+ * - ::LYD_TYPE_RPC_NETCONF:
+ * - @p parent - must be NULL, the whole RPC is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_NETCONF:
+ * - @p parent - must be NULL, the whole notification is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_NETCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, the reply is appended to the RPC;
+ * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] data_type Expected operation to parse (@ref datatype).
+ * @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL.
+ * @param[out] op Optional pointer to the operation (action/RPC) node.
+ * @return LY_ERR value.
+ * @return LY_ENOT if @p data_type is a NETCONF message and the root XML element is not the expected one.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op);
+
+/**
+ * @brief Parse extension data into an operation data tree following only the specification from the given extension instance.
+ *
+ * Directly applicable to data defined as [yang-data](@ref howtoDataYangdata).
+ *
+ * At least one of @p parent, @p tree, or @p op must always be set.
+ *
+ * Specific @p data_type values have different parameter meaning as follows:
+ * - ::LYD_TYPE_RPC_NETCONF:
+ * - @p parent - must be NULL, the whole RPC is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_NETCONF:
+ * - @p parent - must be NULL, the whole notification is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_NETCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, the reply is appended to the RPC;
+ * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ *
+ * @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] data_type Expected operation to parse (@ref datatype).
+ * @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL.
+ * @param[out] op Optional pointer to the operation (action/RPC) node.
+ * @return LY_ERR value.
+ * @return LY_ENOT if @p data_type is a NETCONF message and the root XML element is not the expected one.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_ext_op(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in,
+ LYD_FORMAT format, enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op);
+
+/**
+ * @brief Fully validate a data tree.
+ *
+ * The data tree is modified in-place. As a result of the validation, some data might be removed
+ * from the tree. In that case, the removed items are freed, not just unlinked.
+ *
+ * @param[in,out] tree Data tree to recursively validate. May be changed by validation, might become NULL.
+ * @param[in] ctx libyang context. Can be NULL if @p tree is set.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[out] diff Optional diff with any changes made by the validation.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_validate_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t val_opts,
+ struct lyd_node **diff);
+
+/**
+ * @brief Fully validate a data tree of a module.
+ *
+ * The data tree is modified in-place. As a result of the validation, some data might be removed
+ * from the tree. In that case, the removed items are freed, not just unlinked.
+ *
+ * @param[in,out] tree Data tree to recursively validate. May be changed by validation, might become NULL.
+ * @param[in] module Module whose data (and schema restrictions) to validate.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[out] diff Optional diff with any changes made by the validation.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, uint32_t val_opts,
+ struct lyd_node **diff);
+
+/**
+ * @brief Validate an RPC/action request, reply, or notification. Only the operation data tree (input/output/notif)
+ * is validate, any parents are ignored.
+ *
+ * @param[in,out] op_tree Operation tree with any parents. It can point to the operation itself or any of
+ * its parents, only the operation subtree is actually validated.
+ * @param[in] dep_tree Tree to be used for validating references from the operation subtree.
+ * @param[in] data_type Operation type to validate (only YANG operations are accepted, @ref datatype).
+ * @param[out] diff Optional diff with any changes made by the validation.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *dep_tree, enum lyd_type data_type,
+ struct lyd_node **diff);
+
+/** @} datatree */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PARSER_DATA_H_ */
diff --git a/src/parser_internal.h b/src/parser_internal.h
new file mode 100644
index 0000000..458d297
--- /dev/null
+++ b/src/parser_internal.h
@@ -0,0 +1,392 @@
+/**
+ * @file parser_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internal structures and functions for libyang parsers
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PARSER_INTERNAL_H_
+#define LY_PARSER_INTERNAL_H_
+
+#include "parser_data.h"
+#include "set.h"
+
+struct lyd_ctx;
+struct ly_in;
+struct lysp_ext_substmt;
+struct lysp_stmt;
+struct lysp_yang_ctx;
+struct lysp_yin_ctx;
+struct lysp_ctx;
+
+/**
+ * @brief Callback for ::lyd_ctx to free the structure
+ *
+ * @param[in] ctx Data parser context to free.
+ */
+typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx);
+
+/**
+ * @brief Internal data parser flags.
+ */
+#define LYD_INTOPT_RPC 0x01 /**< RPC request is being parsed. */
+#define LYD_INTOPT_ACTION 0x02 /**< Action request is being parsed. */
+#define LYD_INTOPT_REPLY 0x04 /**< RPC/action reply is being parsed. */
+#define LYD_INTOPT_NOTIF 0x08 /**< Notification is being parsed. */
+#define LYD_INTOPT_ANY 0x10 /**< Anydata/anyxml content is being parsed, there can be anything. */
+#define LYD_INTOPT_WITH_SIBLINGS 0x20 /**< Parse the whole input with any siblings. */
+#define LYD_INTOPT_NO_SIBLINGS 0x40 /**< If there are any siblings, return an error. */
+
+/**
+ * @brief Internal (common) context for YANG data parsers.
+ *
+ * Covers ::lyd_xml_ctx, ::lyd_json_ctx and ::lyd_lyb_ctx.
+ */
+struct lyd_ctx {
+ const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context of the data being parsed */
+ uint32_t parse_opts; /**< various @ref dataparseroptions. */
+ uint32_t val_opts; /**< various @ref datavalidationoptions. */
+ uint32_t int_opts; /**< internal parser options */
+ uint32_t path_len; /**< used bytes in the path buffer */
+
+#define LYD_PARSER_BUFSIZE 4078
+ char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
+ struct ly_set node_when; /**< set of nodes with "when" conditions */
+ struct ly_set node_types; /**< set of nodes validated with LY_EINCOMPLETE result */
+ struct ly_set meta_types; /**< set of metadata validated with LY_EINCOMPLETE result */
+ struct ly_set ext_node; /**< set of nodes with extension instances to validate */
+ struct ly_set ext_val; /**< set of nested subtrees parsed by extensions to validate */
+ struct lyd_node *op_node; /**< if an RPC/action/notification is being parsed, store the pointer to it */
+
+ /* callbacks */
+ lyd_ctx_free_clb free; /**< destructor */
+
+ struct {
+ const struct ly_ctx *ctx; /**< libyang context */
+ uint64_t line; /**< current line */
+ struct ly_in *in; /**< input structure */
+ } *data_ctx; /**< generic pointer supposed to map to and access (common part of) XML/JSON/... parser contexts */
+};
+
+/**
+ * @brief Internal context for XML data parser.
+ */
+struct lyd_xml_ctx {
+ const struct lysc_ext_instance *ext;
+ uint32_t parse_opts;
+ uint32_t val_opts;
+ uint32_t int_opts;
+ uint32_t path_len;
+ char path[LYD_PARSER_BUFSIZE];
+ struct ly_set node_when;
+ struct ly_set node_types;
+ struct ly_set meta_types;
+ struct ly_set ext_node;
+ struct ly_set ext_val;
+ struct lyd_node *op_node;
+
+ /* callbacks */
+ lyd_ctx_free_clb free;
+
+ struct lyxml_ctx *xmlctx; /**< XML context */
+};
+
+/**
+ * @brief Internal context for JSON data parser.
+ */
+struct lyd_json_ctx {
+ const struct lysc_ext_instance *ext;
+ uint32_t parse_opts;
+ uint32_t val_opts;
+ uint32_t int_opts;
+ uint32_t path_len;
+ char path[LYD_PARSER_BUFSIZE];
+ struct ly_set node_when;
+ struct ly_set node_types;
+ struct ly_set meta_types;
+ struct ly_set ext_node;
+ struct ly_set ext_val;
+ struct lyd_node *op_node;
+
+ /* callbacks */
+ lyd_ctx_free_clb free;
+
+ struct lyjson_ctx *jsonctx; /**< JSON context */
+};
+
+/**
+ * @brief Internal context for LYB data parser/printer.
+ */
+struct lyd_lyb_ctx {
+ const struct lysc_ext_instance *ext;
+
+ union {
+ struct {
+ uint32_t parse_opts;
+ uint32_t val_opts;
+ };
+ uint32_t print_options;
+ };
+ uint32_t int_opts;
+ uint32_t path_len;
+ char path[LYD_PARSER_BUFSIZE];
+ struct ly_set node_when;
+ struct ly_set node_types;
+ struct ly_set meta_types;
+ struct ly_set ext_node;
+ struct ly_set ext_val;
+ struct lyd_node *op_node;
+
+ /* callbacks */
+ lyd_ctx_free_clb free;
+
+ struct lylyb_ctx *lybctx; /* LYB context */
+};
+
+/**
+ * @brief Parsed extension instance data to validate.
+ */
+struct lyd_ctx_ext_val {
+ struct lysc_ext_instance *ext;
+ struct lyd_node *sibling;
+};
+
+/**
+ * @brief Parsed data node with extension instance to validate.
+ */
+struct lyd_ctx_ext_node {
+ struct lysc_ext_instance *ext;
+ struct lyd_node *node;
+};
+
+/**
+ * @brief Common part to supplement the specific ::lyd_ctx_free_clb callbacks.
+ */
+void lyd_ctx_free(struct lyd_ctx *ctx);
+
+/**
+ * @brief Parse submodule from YANG data.
+ * @param[in,out] context Parser context.
+ * @param[in] ly_ctx Context of YANG schemas.
+ * @param[in] main_ctx Parser context of main module.
+ * @param[in] in Input structure.
+ * @param[out] submod Pointer to the parsed submodule structure.
+ * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
+ */
+LY_ERR yang_parse_submodule(struct lysp_yang_ctx **context, struct ly_ctx *ly_ctx, struct lysp_ctx *main_ctx,
+ struct ly_in *in, struct lysp_submodule **submod);
+
+/**
+ * @brief Parse module from YANG data.
+ * @param[in] context Parser context.
+ * @param[in] in Input structure.
+ * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ * @return LY_ERR values.
+ */
+LY_ERR yang_parse_module(struct lysp_yang_ctx **context, struct ly_in *in, struct lys_module *mod);
+
+/**
+ * @brief Parse module from YIN data.
+ *
+ * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
+ * @param[in] in Input structure.
+ * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_module(struct lysp_yin_ctx **yin_ctx, struct ly_in *in, struct lys_module *mod);
+
+/**
+ * @brief Parse submodule from YIN data.
+ *
+ * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
+ * @param[in] ctx Libyang context.
+ * @param[in] main_ctx Parser context of main module.
+ * @param[in] in Input structure.
+ * @param[in,out] submod Submodule structure where the parsed information, will be filled in.
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_submodule(struct lysp_yin_ctx **yin_ctx, struct ly_ctx *ctx, struct lysp_ctx *main_ctx,
+ struct ly_in *in, struct lysp_submodule **submod);
+
+/**
+ * @brief Parse XML string as a YANG data tree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] int_opts Internal data parser options.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
+ struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
+
+/**
+ * @brief Parse XML string as a NETCONF message.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected NETCONF data type of the data.
+ * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
+
+/**
+ * @brief Parse JSON string as a YANG data tree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] int_opts Internal data parser options.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
+ struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
+
+/**
+ * @brief Parse binary LYB data as a YANG data tree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] int_opts Internal data parser options.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
+ struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
+
+/**
+ * @brief Search all the parents for an operation node, check validity based on internal parser flags.
+ *
+ * @param[in] parent Parent to connect the parsed nodes to.
+ * @param[in] int_opts Internal parser options.
+ * @param[out] op Found operation, if any.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op);
+
+/**
+ * @brief Check that a data node representing the @p snode is suitable based on options.
+ *
+ * @param[in] lydctx Common data parsers context.
+ * @param[in] snode Schema node to check.
+ * @return LY_SUCCESS or LY_EVALID
+ */
+LY_ERR lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode);
+
+/**
+ * @brief Wrapper around ::lyd_create_term() for data parsers.
+ *
+ * @param[in] lydctx Data parser context.
+ * @param[in] schema Schema node of the new data node.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints [Data parser's hints](@ref lydvalhints) for the value's type.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
+ ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node);
+
+/**
+ * @brief Wrapper around ::lyd_create_meta() for data parsers.
+ *
+ * @param[in] lydctx Data parser context.
+ * @param[in] parent Parent of the created meta. Must be set if @p meta is NULL.
+ * @param[in,out] meta First existing meta to connect to or empty pointer to set. Must be set if @p parent is NULL.
+ * @param[in] mod Module of the created metadata.
+ * @param[in] name Metadata name.
+ * @param[in] name_len Length of @p name.
+ * @param[in] value Metadata value.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] dynamic Whether the @p value is dynamically allocated, is adjusted once the value is assigned.
+ * @param[in] format Prefix format.
+ * @param[in] prefix_data Prefix format data (see ::ly_resolve_prefix()).
+ * @param[in] hints [Value hint](@ref lydvalhints) from the parser regarding the value type.
+ * @param[in] ctx_node Value context node.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta,
+ const struct lys_module *mod, const char *name, size_t name_len, const void *value, size_t value_len,
+ ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node);
+
+/**
+ * @brief Check that a list has all its keys.
+ *
+ * @param[in] node List to check.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT on a missing key.
+ */
+LY_ERR lyd_parse_check_keys(struct lyd_node *node);
+
+/**
+ * @brief Set data flags for a newly parsed node.
+ *
+ * @param[in] node Node to use.
+ * @param[in,out] meta Node metadata, may be removed from.
+ * @param[in] lydctx Data parsing context.
+ * @param[in] ext Extension instance if @p node was parsed for one.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
+ struct lysc_ext_instance *ext);
+
+/**
+ * @brief Parse an instance extension statement.
+ *
+ * @param[in] pctx Parse context.
+ * @param[in] substmt Parsed ext instance substatement info.
+ * @param[in] stmt Parsed generic statement to process.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_parse_ext_instance_stmt(struct lysp_ctx *pctx, struct lysp_ext_substmt *substmt, struct lysp_stmt *stmt);
+
+#endif /* LY_PARSER_INTERNAL_H_ */
diff --git a/src/parser_json.c b/src/parser_json.c
new file mode 100644
index 0000000..6219c6e
--- /dev/null
+++ b/src/parser_json.c
@@ -0,0 +1,1819 @@
+/**
+ * @file parser_json.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief JSON data parser for libyang
+ *
+ * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "in_internal.h"
+#include "json.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "plugins_exts.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "validation.h"
+
+/**
+ * @brief Free the JSON data parser context.
+ *
+ * JSON implementation of lyd_ctx_free_clb().
+ */
+static void
+lyd_json_ctx_free(struct lyd_ctx *lydctx)
+{
+ struct lyd_json_ctx *ctx = (struct lyd_json_ctx *)lydctx;
+
+ if (lydctx) {
+ lyd_ctx_free(lydctx);
+ lyjson_ctx_free(ctx->jsonctx);
+ free(ctx);
+ }
+}
+
+/**
+ * @brief Pass the responsibility for releasing the dynamic values to @p dst.
+ *
+ * @param[in] jsonctx JSON context which contains the dynamic value.
+ * @param[in,out] dst Pointer to which the responsibility will be submited.
+ * If the pointer is already pointing to some allocated memory, it is released beforehand.
+ */
+static void
+lyjson_ctx_give_dynamic_value(struct lyjson_ctx *jsonctx, char **dst)
+{
+ assert(jsonctx && dst);
+
+ if (!jsonctx->dynamic) {
+ return;
+ }
+
+ if (dst) {
+ free(*dst);
+ }
+ *dst = NULL;
+
+ /* give the dynamic value */
+ *dst = (char *)jsonctx->value;
+
+ /* responsibility for the release is now passed to dst */
+ jsonctx->dynamic = 0;
+}
+
+/**
+ * @brief Parse JSON member-name as [\@][prefix:][name]
+ *
+ * \@ - metadata flag, maps to 1 in @p is_meta_p
+ * prefix - name of the module of the data node
+ * name - name of the data node
+ *
+ * All the output parameter are mandatory. Function only parse the member-name, all the appropriate checks are up to the caller.
+ *
+ * @param[in] value String to parse
+ * @param[in] value_len Length of the @p str.
+ * @param[out] name_p Pointer to the beginning of the parsed name.
+ * @param[out] name_len_p Pointer to the length of the parsed name.
+ * @param[out] prefix_p Pointer to the beginning of the parsed prefix. If the member-name does not contain prefix, result is NULL.
+ * @param[out] prefix_len_p Pointer to the length of the parsed prefix. If the member-name does not contain prefix, result is 0.
+ * @param[out] is_meta_p Pointer to the metadata flag, set to 1 if the member-name contains \@, 0 otherwise.
+ */
+static void
+lydjson_parse_name(const char *value, size_t value_len, const char **name_p, size_t *name_len_p, const char **prefix_p,
+ size_t *prefix_len_p, ly_bool *is_meta_p)
+{
+ const char *name, *prefix = NULL;
+ size_t name_len, prefix_len = 0;
+ ly_bool is_meta = 0;
+
+ name = memchr(value, ':', value_len);
+ if (name != NULL) {
+ prefix = value;
+ if (*prefix == '@') {
+ is_meta = 1;
+ prefix++;
+ }
+ prefix_len = name - prefix;
+ name++;
+ name_len = value_len - (prefix_len + 1) - is_meta;
+ } else {
+ name = value;
+ if (name[0] == '@') {
+ is_meta = 1;
+ name++;
+ }
+ name_len = value_len - is_meta;
+ }
+
+ *name_p = name;
+ *name_len_p = name_len;
+ *prefix_p = prefix;
+ *prefix_len_p = prefix_len;
+ *is_meta_p = is_meta;
+}
+
+/**
+ * @brief Get correct prefix (module_name) inside the @p node.
+ *
+ * @param[in] node Data node to get inherited prefix.
+ * @param[in] local_prefix Local prefix to replace the inherited prefix.
+ * @param[in] local_prefix_len Length of the @p local_prefix string. In case of 0, the inherited prefix is taken.
+ * @param[out] prefix_p Pointer to the resulting prefix string, Note that the result can be NULL in case of no local prefix
+ * and no context @p node to get inherited prefix.
+ * @param[out] prefix_len_p Pointer to the length of the resulting @p prefix_p string. Note that the result can be 0 in case
+ * of no local prefix and no context @p node to get inherited prefix.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t local_prefix_len, const char **prefix_p,
+ size_t *prefix_len_p)
+{
+ struct lyd_node_opaq *onode;
+ const char *module_name = NULL;
+
+ assert(prefix_p && prefix_len_p);
+
+ if (local_prefix_len) {
+ *prefix_p = local_prefix;
+ *prefix_len_p = local_prefix_len;
+ return LY_SUCCESS;
+ }
+
+ *prefix_p = NULL;
+ while (node) {
+ if (node->schema) {
+ *prefix_p = node->schema->module->name;
+ break;
+ }
+ onode = (struct lyd_node_opaq *)node;
+ if (onode->name.module_name) {
+ *prefix_p = onode->name.module_name;
+ break;
+ } else if (onode->name.prefix) {
+ *prefix_p = onode->name.prefix;
+ break;
+ }
+ node = lyd_parent(node);
+ }
+ *prefix_len_p = ly_strlen(module_name);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Skip the current JSON object/array.
+ *
+ * @param[in] jsonctx JSON context with the input data to skip.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_data_skip(struct lyjson_ctx *jsonctx)
+{
+ enum LYJSON_PARSER_STATUS status, current;
+ uint32_t orig_depth;
+
+ status = lyjson_ctx_status(jsonctx, 0);
+ assert((status == LYJSON_OBJECT) || (status == LYJSON_ARRAY));
+ orig_depth = jsonctx->depth;
+
+ /* next */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+
+ if ((status == LYJSON_OBJECT) && (current != LYJSON_OBJECT) && (current != LYJSON_ARRAY)) {
+ /* no nested objects */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
+ return LY_SUCCESS;
+ }
+
+ /* skip after the content */
+ while ((jsonctx->depth > orig_depth) || (current != status + 1)) {
+ if (current == LYJSON_ARRAY) {
+ /* skip the array separately */
+ LY_CHECK_RET(lydjson_data_skip(jsonctx));
+ current = lyjson_ctx_status(jsonctx, 0);
+ } else {
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ }
+
+ if (current == LYJSON_END) {
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get schema node corresponding to the input parameters.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] is_attr Flag if the reference to the node is an attribute, for logging only.
+ * @param[in] prefix Requested node's prefix (module name).
+ * @param[in] prefix_len Length of the @p prefix.
+ * @param[in] name Requested node's name.
+ * @param[in] name_len Length of the @p name.
+ * @param[in] parent Parent of the node being processed, can be NULL in case of top-level.
+ * @param[out] snode Found schema node corresponding to the input parameters. If NULL, parse as an opaque node.
+ * @param[out] ext Extension instance that provided @p snode, if any.
+ * @return LY_SUCCES on success.
+ * @return LY_ENOT if the whole object was parsed (skipped or as an extension).
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name,
+ size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lys_module *mod = NULL;
+ uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
+
+ *snode = NULL;
+ *ext = NULL;
+
+ /* get the element module, prefer parent context because of extensions */
+ if (prefix_len) {
+ mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, prefix, prefix_len);
+ } else if (parent) {
+ if (parent->schema) {
+ mod = parent->schema->module;
+ }
+ } else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Top-level JSON object member \"%.*s\" must be namespace-qualified.",
+ (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (!mod) {
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
+ ret = r;
+ goto cleanup;
+ }
+
+ /* unknown module */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip element with children */
+ ret = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = LY_ENOT;
+ goto cleanup;
+ }
+ }
+
+ /* get the schema node */
+ if (mod && (!parent || parent->schema)) {
+ if (!parent && lydctx->ext) {
+ *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+ } else {
+ *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+ }
+ if (!*snode) {
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
+ ret = r;
+ goto cleanup;
+ }
+
+ /* unknown data node */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (parent) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
+ (int)name_len, name, LYD_NAME(parent));
+ } else if (lydctx->ext) {
+ if (lydctx->ext->argument) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE,
+ "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
+ (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
+ (int)name_len, name, lydctx->ext->def->name);
+ }
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+ (int)name_len, name, mod->name);
+ }
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip element with children */
+ ret = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = LY_ENOT;
+ goto cleanup;
+ }
+ } else {
+ /* check that schema node is valid and can be used */
+ ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode);
+ }
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Check that the input data are parseable as the @p list.
+ *
+ * Checks for all the list's keys. Function does not revert the context state.
+ *
+ * @param[in] jsonctx JSON 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)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx, 0);
+ struct ly_set key_set = {0};
+ const struct lysc_node *snode;
+ uint32_t i, status_count;
+
+ assert(list && (list->nodetype == LYS_LIST));
+
+ /* get all keys into a set (keys do not have if-features or anything) */
+ snode = NULL;
+ while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
+ ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ if (status == LYJSON_OBJECT) {
+ status_count = jsonctx->status.count;
+
+ while (key_set.count && (status != LYJSON_OBJECT_CLOSED)) {
+ const char *name, *prefix;
+ size_t name_len, prefix_len;
+ ly_bool is_attr;
+
+ /* match the key */
+ snode = NULL;
+ lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+
+ if (!is_attr && !prefix) {
+ for (i = 0; i < key_set.count; ++i) {
+ snode = (const struct lysc_node *)key_set.objs[i];
+ if (!ly_strncmp(snode->name, name, name_len)) {
+ break;
+ }
+ }
+ /* go into the item to a) process it as a key or b) start skipping it as another list child */
+ ret = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (snode) {
+ /* we have the key, validate the value */
+ if (status < LYJSON_NUMBER) {
+ /* not a terminal */
+ ret = LY_ENOT;
+ goto cleanup;
+ }
+
+ ret = lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* key with a valid value, remove from the set */
+ ly_set_rm_index(&key_set, i, NULL);
+ }
+ } else {
+ /* start skipping the member we are not interested in */
+ ret = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ /* move to the next child */
+ while (status_count < jsonctx->status.count) {
+ ret = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ }
+
+ if (key_set.count) {
+ /* some keys are missing/did not validate */
+ ret = LY_ENOT;
+ }
+
+cleanup:
+ ly_set_erase(&key_set, NULL);
+ return ret;
+}
+
+/**
+ * @brief Get the hint for the data type parsers according to the current JSON parser context.
+ *
+ * @param[in] lydctx JSON data parser context. The context is supposed to be on a value.
+ * @param[in,out] status Pointer to the current context status,
+ * in some circumstances the function manipulates with the context so the status is updated.
+ * @param[out] type_hint_p Pointer to the variable to store the result.
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EINVAL in case of invalid context status not referring to a value.
+ */
+static LY_ERR
+lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
+{
+ *type_hint_p = 0;
+
+ if (*status_p == LYJSON_ARRAY) {
+ /* only [null] */
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_p));
+ if (*status_p != LYJSON_NULL) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON,
+ "Expected JSON name/value or special name/[null], but input data contains name/[%s].",
+ lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, NULL));
+ if (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_ARRAY_CLOSED) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.",
+ lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ *type_hint_p = LYD_VALHINT_EMPTY;
+ } else if (*status_p == LYJSON_STRING) {
+ *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64;
+ } else if (*status_p == LYJSON_NUMBER) {
+ *type_hint_p = LYD_VALHINT_DECNUM;
+ } else if ((*status_p == LYJSON_FALSE) || (*status_p == LYJSON_TRUE)) {
+ *type_hint_p = LYD_VALHINT_BOOLEAN;
+ } else if (*status_p == LYJSON_NULL) {
+ *type_hint_p = 0;
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check in advance if the input data are parsable according to the provided @p snode.
+ *
+ * Note that the checks are done only in case the LYD_PARSE_OPAQ is allowed. Otherwise the same checking
+ * is naturally done when the data are really parsed.
+ *
+ * @param[in] lydctx JSON data parser context. When the function returns, the context is in the same state
+ * as before calling, despite it is necessary to process input data for checking.
+ * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[out] type_hint_p Pointer to a variable to store detected value type hint in case of leaf or leaf-list.
+ * @return LY_SUCCESS in case the data are ok for the @p snode or the LYD_PARSE_OPAQ is not enabled.
+ * @return LY_ENOT in case the input data are not sufficient to fully parse the list instance
+ * @return LY_EINVAL in case of invalid leaf JSON encoding
+ * and they are expected to be parsed as opaq nodes.
+ */
+static LY_ERR
+lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, uint32_t *type_hint_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyjson_ctx *jsonctx = lydctx->jsonctx;
+ enum LYJSON_PARSER_STATUS status;
+
+ assert(snode);
+
+ if (!(snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
+ /* can always be parsed as a data node if we have the schema node */
+ return LY_SUCCESS;
+ }
+
+ /* backup parser */
+ lyjson_ctx_backup(jsonctx);
+ status = lyjson_ctx_status(jsonctx, 0);
+
+ if (lydctx->parse_opts & LYD_PARSE_OPAQ) {
+ /* check if the node is parseable. if not, NULL the snode to announce that it is supposed to be parsed
+ * as an opaq node */
+ switch (snode->nodetype) {
+ case LYS_LEAFLIST:
+ case LYS_LEAF:
+ /* value may not be valid in which case we parse it as an opaque node */
+ ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+ if (ret) {
+ break;
+ }
+
+ if (lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL)) {
+ ret = LY_ENOT;
+ }
+ break;
+ case LYS_LIST:
+ /* lists may not have all its keys */
+ if (lydjson_check_list(jsonctx, snode)) {
+ /* invalid list, parse as opaque if it misses/has invalid some keys */
+ ret = LY_ENOT;
+ }
+ break;
+ }
+ } else if (snode->nodetype & LYD_NODE_TERM) {
+ status = lyjson_ctx_status(jsonctx, 0);
+ ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+ }
+
+ /* restore parser */
+ lyjson_ctx_restore(jsonctx);
+
+ return ret;
+}
+
+/**
+ * @brief Join the forward-referencing metadata with their target data nodes.
+ *
+ * Note that JSON encoding for YANG data allows forward-referencing metadata only for leafs/leaf-lists.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in,out] first_p Pointer to the first sibling node variable (top-level or in a particular parent node)
+ * as a starting point to search for the metadata's target data node
+ * @return LY_SUCCESS on success
+ * @return LY_EVALID in case there are some metadata with unresolved target data node instance
+ */
+static LY_ERR
+lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *node, *attr, *next, *meta_iter;
+ struct lysc_ext_instance *ext;
+ uint64_t instance = 0;
+ const char *prev = NULL;
+ uint32_t log_location_items = 0;
+
+ /* finish linking metadata */
+ LY_LIST_FOR_SAFE(*first_p, next, attr) {
+ struct lyd_node_opaq *meta_container = (struct lyd_node_opaq *)attr;
+ uint64_t match = 0;
+ ly_bool is_attr;
+ const char *name, *prefix;
+ size_t name_len, prefix_len;
+ const struct lysc_node *snode;
+
+ if (attr->schema || (meta_container->name.name[0] != '@')) {
+ /* not an opaq metadata node */
+ continue;
+ }
+
+ LOG_LOCSET(NULL, attr, NULL, NULL);
+ log_location_items++;
+
+ if (prev != meta_container->name.name) {
+ /* metas' names are stored in dictionary, so checking pointers must works */
+ lydict_remove(lydctx->jsonctx->ctx, prev);
+ LY_CHECK_GOTO(ret = lydict_insert(lydctx->jsonctx->ctx, meta_container->name.name, 0, &prev), cleanup);
+ instance = 1;
+ } else {
+ instance++;
+ }
+
+ /* find the corresponding data node */
+ LY_LIST_FOR(*first_p, node) {
+ if (!node->schema) {
+ /* opaq node - we are going to put into it just a generic attribute. */
+ if (strcmp(&meta_container->name.name[1], ((struct lyd_node_opaq *)node)->name.name)) {
+ continue;
+ }
+
+ if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Metadata container references a sibling list node %s.",
+ ((struct lyd_node_opaq *)node)->name.name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* match */
+ match++;
+ if (match != instance) {
+ continue;
+ }
+
+ LY_LIST_FOR(meta_container->child, meta_iter) {
+ /* convert opaq node to a attribute of the opaq node */
+ struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter;
+
+ ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, meta->name.name, strlen(meta->name.name),
+ meta->name.prefix, ly_strlen(meta->name.prefix), meta->name.module_name,
+ ly_strlen(meta->name.module_name), meta->value, ly_strlen(meta->value), NULL, LY_VALUE_JSON,
+ NULL, meta->hints);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* done */
+ break;
+ } else {
+ /* this is the second time we are resolving the schema node, so it must succeed,
+ * but remember that snode can be still NULL */
+ lydjson_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len,
+ &prefix, &prefix_len, &is_attr);
+ assert(is_attr);
+ lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext);
+
+ if (snode != node->schema) {
+ continue;
+ }
+
+ /* match */
+ match++;
+ if (match != instance) {
+ continue;
+ }
+
+ LY_LIST_FOR(meta_container->child, meta_iter) {
+ /* convert opaq node to a metadata of the node */
+ struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter;
+ struct lys_module *mod = NULL;
+
+ mod = ly_ctx_get_module_implemented(lydctx->jsonctx->ctx, meta->name.prefix);
+ if (mod) {
+ ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod,
+ meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value),
+ NULL, LY_VALUE_JSON, NULL, meta->hints, node->schema);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (meta->name.prefix) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE,
+ "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".",
+ meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "",
+ meta->name.name);
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".",
+ meta->name.name);
+ }
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* add/correct flags */
+ ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_GOTO(ret, cleanup);
+ break;
+ }
+ }
+
+ if (match != instance) {
+ /* there is no corresponding data node for the metadata */
+ if (instance > 1) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE,
+ "Missing JSON data instance #%" PRIu64 " to be coupled with %s metadata.",
+ instance, meta_container->name.name);
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance to be coupled with %s metadata.",
+ meta_container->name.name);
+ }
+ ret = LY_EVALID;
+ } else {
+ /* remove the opaq attr */
+ if (attr == (*first_p)) {
+ *first_p = attr->next;
+ }
+ lyd_free_tree(attr);
+ }
+
+ LOG_LOCBACK(0, log_location_items, 0, 0);
+ log_location_items = 0;
+ }
+
+cleanup:
+ lydict_remove(lydctx->jsonctx->ctx, prev);
+
+ LOG_LOCBACK(0, log_location_items, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Parse a metadata member.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] snode Schema node of the metadata parent.
+ * @param[in] node Parent node in case the metadata is not forward-referencing (only LYD_NODE_TERM)
+ * so the data node does not exists. In such a case the metadata is stored in the context for the later
+ * processing by lydjson_metadata_finish().
+ * @return LY_SUCCESS on success
+ * @return Various LY_ERR values in case of failure.
+ */
+static LY_ERR
+lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum LYJSON_PARSER_STATUS status;
+ const char *expected;
+ ly_bool in_parent = 0;
+ const char *name, *prefix = NULL;
+ char *dynamic_prefname = NULL;
+ size_t name_len, prefix_len = 0;
+ struct lys_module *mod;
+ const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
+ ly_bool is_attr = 0;
+ struct lyd_node *prev = node;
+ uint32_t instance = 0, val_hints;
+ uint16_t nodetype;
+
+ assert(snode || node);
+
+ nodetype = snode ? snode->nodetype : LYS_CONTAINER;
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+
+ /* move to the second item in the name/X pair */
+ ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* check attribute encoding */
+ switch (nodetype) {
+ case LYS_LEAFLIST:
+ expected = "@name/array of objects/nulls";
+
+ LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
+
+next_entry:
+ instance++;
+
+ /* move into array/next entry */
+ ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (status == LYJSON_ARRAY_CLOSED) {
+ /* no more metadata */
+ goto cleanup;
+ }
+ LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_NULL), representation_error);
+
+ if (!node || (node->schema != prev->schema)) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%u of %s:%s to be coupled with metadata.",
+ instance, prev->schema->module->name, prev->schema->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (status == LYJSON_NULL) {
+ /* continue with the next entry in the leaf-list array */
+ prev = node;
+ node = node->next;
+ goto next_entry;
+ }
+ break;
+ case LYS_LEAF:
+ case LYS_ANYXML:
+ expected = "@name/object";
+
+ LY_CHECK_GOTO(status != LYJSON_OBJECT, representation_error);
+ break;
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_ANYDATA:
+ case LYS_NOTIF:
+ case LYS_ACTION:
+ case LYS_RPC:
+ in_parent = 1;
+ expected = "@/object";
+ LY_CHECK_GOTO(status != LYJSON_OBJECT, representation_error);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+
+ /* process all the members inside a single metadata object */
+ assert(status == LYJSON_OBJECT);
+ while (status != LYJSON_OBJECT_CLOSED) {
+ LY_CHECK_GOTO(status != LYJSON_OBJECT, representation_error);
+
+ lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+ lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &dynamic_prefname);
+
+ if (!name_len) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (!prefix_len) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".",
+ (int)lydctx->jsonctx->value_len, lydctx->jsonctx->value);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (is_attr) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"",
+ (int)lydctx->jsonctx->value_len, lydctx->jsonctx->value);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* get the element module */
+ mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
+ if (!mod) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.",
+ (int)prefix_len, prefix, (int)name_len, name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (node->schema) {
+ /* skip element with children */
+ ret = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ /* end of the item */
+ continue;
+ }
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+ }
+
+ /* get the value */
+ ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get value hints */
+ ret = lydjson_value_type_hint(lydctx, &status, &val_hints);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (node->schema) {
+ /* create metadata */
+ ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value,
+ lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, val_hints, node->schema);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add/correct flags */
+ ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* create attribute */
+ const char *module_name;
+ size_t module_name_len;
+
+ lydjson_get_node_prefix(node, prefix, prefix_len, &module_name, &module_name_len);
+
+ /* attr2 is always changed to the created attribute */
+ ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name,
+ module_name_len, lydctx->jsonctx->value, lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic,
+ LY_VALUE_JSON, NULL, val_hints);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ /* next member */
+ ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_OBJECT_CLOSED), representation_error);
+ }
+
+ if (nodetype == LYS_LEAFLIST) {
+ /* continue by processing another metadata object for the following
+ * leaf-list instance since they are always instantiated in JSON array */
+ prev = node;
+ node = node->next;
+ goto next_entry;
+ }
+
+ /* success */
+ goto cleanup;
+
+representation_error:
+ LOGVAL(ctx, LYVE_SYNTAX_JSON,
+ "The attribute(s) of %s \"%s\" is expected to be represented as JSON %s, but input data contains @%s/%s.",
+ lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected, lyjson_token2str(status),
+ in_parent ? "" : "name");
+ ret = LY_EVALID;
+
+cleanup:
+ free(dynamic_prefname);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Eat the node pointed by @p node_p by inserting it into @p parent and maintain the @p first_p pointing
+ * to the first child node.
+ *
+ * @param[in] parent Parent node to insert to, can be NULL in case of top-level (or provided first_p).
+ * @param[in,out] first_p Pointer to the first sibling node in case of top-level.
+ * @param[in,out] node_p pointer to the new node to insert, after the insert is done, pointer is set to NULL.
+ * @param[in] last If set, always insert at the end.
+ * @param[in] ext Extension instance of @p node_p, if any.
+ */
+static void
+lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
+ struct lysc_ext_instance *ext)
+{
+ if (*node_p) {
+ /* insert, keep first pointer correct */
+ if (ext) {
+ lyplg_ext_insert(parent, *node_p);
+ } else {
+ lyd_insert_node(parent, first_p, *node_p, last);
+ }
+ if (first_p) {
+ if (parent) {
+ *first_p = lyd_child(parent);
+ } else {
+ while ((*first_p)->prev->next) {
+ *first_p = (*first_p)->prev;
+ }
+ }
+ }
+ *node_p = NULL;
+ }
+}
+
+/**
+ * @brief Wrapper for ::lyd_create_opaq().
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] name Name of the opaq node to create.
+ * @param[in] name_len Length of the @p name string.
+ * @param[in] prefix Prefix of the opaq node to create.
+ * @param[in] prefix_len Length of the @p prefx string.
+ * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
+ * but must be set if @p first is not.
+ * @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone
+ * context status of the array content. Otherwise, it is supposed to be the same as @p status_p.
+ * @param[out] node_p Pointer to the created opaq node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
+ struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_inner_p, struct lyd_node **node_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *value = NULL, *module_name;
+ size_t value_len = 0, module_name_len = 0;
+ ly_bool dynamic = 0;
+ uint32_t type_hint = 0;
+
+ if ((*status_inner_p != LYJSON_OBJECT) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
+ /* prepare for creating opaq node with a value */
+ value = lydctx->jsonctx->value;
+ value_len = lydctx->jsonctx->value_len;
+ dynamic = lydctx->jsonctx->dynamic;
+ lydctx->jsonctx->dynamic = 0;
+
+ LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
+ }
+
+ /* create node */
+ lydjson_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len);
+ ret = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value,
+ value_len, &dynamic, LY_VALUE_JSON, NULL, type_hint, node_p);
+ if (dynamic) {
+ free((char *)value);
+ }
+
+ return ret;
+}
+
+static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed);
+
+/**
+ * @brief Parse opaq node from the input.
+ *
+ * In case of processing array, the whole array is being processed and the resulting @p node_p is the last item of the array.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] name Name of the opaq node to create.
+ * @param[in] name_len Length of the @p name string.
+ * @param[in] prefix Prefix of the opaq node to create.
+ * @param[in] prefix_len Length of the @p prefx string.
+ * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
+ * but must be set if @p first is not.
+ * @param[in,out] status_p Pointer to the current status of the parser context,
+ * since the function manipulates with the context and process the input, the status can be updated.
+ * @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone
+ * context status of the array content. Otherwise, it is supposed to be the same as @p status_p.
+ * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not.
+ * @param[out] node_p Pointer to the created opaq node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
+ struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p,
+ struct lyd_node **first_p, struct lyd_node **node_p)
+{
+ LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+
+ if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) {
+ /* special array null value */
+ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY;
+
+ /* must be the only item */
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member.");
+ return LY_EVALID;
+ }
+
+ goto finish;
+ }
+
+ while ((*status_p == LYJSON_ARRAY) || (*status_p == LYJSON_ARRAY_EMPTY)) {
+ /* process another instance of the same node */
+
+ if ((*status_inner_p == LYJSON_OBJECT) || (*status_inner_p == LYJSON_OBJECT_EMPTY)) {
+ /* array with objects, list */
+ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST;
+
+ /* but first process children of the object in the array */
+ while ((*status_inner_p != LYJSON_OBJECT_CLOSED) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
+ LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
+ *status_inner_p = lyjson_ctx_status(lydctx->jsonctx, 0);
+ }
+ } else {
+ /* array with values, leaf-list */
+ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST;
+ }
+
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ if (*status_inner_p == LYJSON_ARRAY_CLOSED) {
+ goto finish;
+ }
+
+ /* continue with the next instance */
+ assert(node_p);
+ lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, NULL);
+ LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+ }
+
+ if ((*status_p == LYJSON_OBJECT) || (*status_p == LYJSON_OBJECT_EMPTY)) {
+ /* process children */
+ while (*status_p != LYJSON_OBJECT_CLOSED && *status_p != LYJSON_OBJECT_EMPTY) {
+ LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
+ *status_p = lyjson_ctx_status(lydctx->jsonctx, 0);
+ }
+ }
+
+finish:
+ /* finish linking metadata */
+ return lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+}
+
+/**
+ * @brief Move to the second item in the name/X pair and parse opaq node from the input.
+ *
+ * This function is basically the wrapper of the ::lydjson_parse_opaq().
+ * In addition, it calls the ::json_ctx_next() and prepares the status_inner_p parameter
+ * for ::lydjson_parse_opaq().
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] name Name of the opaq node to create.
+ * @param[in] name_len Length of the @p name string.
+ * @param[in] prefix Prefix of the opaq node to create.
+ * @param[in] prefix_len Length of the @p prefx string.
+ * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
+ * but must be set if @p first is not.
+ * @param[in,out] status_p Pointer to the current status of the parser context,
+ * since the function manipulates with the context and process the input, the status can be updated.
+ * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not.
+ * @param[out] node_p Pointer to the created opaq node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len,
+ const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p,
+ struct lyd_node **first_p, struct lyd_node **node_p)
+{
+ enum LYJSON_PARSER_STATUS status_inner = 0;
+
+ /* move to the second item in the name/X pair */
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_p));
+
+ if (*status_p == LYJSON_ARRAY) {
+ /* move into the array */
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, &status_inner));
+ } else {
+ /* just a flag to pass correct parameters into lydjson_parse_opaq() */
+ status_inner = LYJSON_ERROR;
+ }
+
+ if (status_inner == LYJSON_ERROR) {
+ status_inner = *status_p;
+ }
+
+ /* parse opaq node from the input */
+ LY_CHECK_RET(lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, &status_inner,
+ first_p, node_p));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Process the attribute container (starting by @)
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] attr_node The data node referenced by the attribute container, if already known.
+ * @param[in] snode The schema node of the data node referenced by the attribute container, if known.
+ * @param[in] name Name of the opaq node to create.
+ * @param[in] name_len Length of the @p name string.
+ * @param[in] prefix Prefix of the opaq node to create.
+ * @param[in] prefix_len Length of the @p prefx string.
+ * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
+ * but must be set if @p first is not.
+ * @param[in,out] status_p Pointer to the current status of the parser context,
+ * since the function manipulates with the context and process the input, the status can be updated.
+ * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not.
+ * @param[out] node_p Pointer to the created opaq node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, const struct lysc_node *snode,
+ const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent,
+ enum LYJSON_PARSER_STATUS *status_p, struct lyd_node **first_p, struct lyd_node **node_p)
+{
+ LY_ERR r;
+ const char *opaq_name, *mod_name;
+ size_t opaq_name_len;
+
+ if (!snode && !prefix) {
+ /* set the prefix */
+ if (parent) {
+ lydjson_get_node_prefix(parent, NULL, 0, &prefix, &prefix_len);
+ } else {
+ prefix = "";
+ prefix_len = 0;
+ }
+ }
+
+ /* parse as an attribute to a (opaque) node */
+ if (!attr_node) {
+ /* try to find the instance */
+ LY_LIST_FOR(*first_p, attr_node) {
+ if (snode) {
+ if (attr_node->schema) {
+ if (attr_node->schema == snode) {
+ break;
+ }
+ } else {
+ mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name;
+ if (!strcmp(LYD_NAME(attr_node), snode->name) && mod_name && !strcmp(mod_name, snode->module->name)) {
+ break;
+ }
+ }
+ } else {
+ if (attr_node->schema) {
+ if (!ly_strncmp(LYD_NAME(attr_node), name, name_len) &&
+ !ly_strncmp(attr_node->schema->module->name, prefix, prefix_len)) {
+ break;
+ }
+ } else {
+ mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name;
+ if (!ly_strncmp(LYD_NAME(attr_node), name, name_len) && mod_name &&
+ !ly_strncmp(mod_name, prefix, prefix_len)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!attr_node) {
+ /* parse just as an opaq node with the name beginning with @,
+ * later we have to check that it belongs to a standard node
+ * and it is supposed to be converted to a metadata */
+ uint32_t prev_opts;
+
+ /* backup parser options to parse unknown metadata as opaq nodes and try to resolve them later */
+ prev_opts = lydctx->parse_opts;
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ;
+
+ opaq_name = prefix ? prefix - 1 : name - 1;
+ opaq_name_len = prefix ? prefix_len + name_len + 2 : name_len + 1;
+ r = lydjson_ctx_next_parse_opaq(lydctx, opaq_name, opaq_name_len, NULL, 0, parent, status_p, first_p, node_p);
+
+ /* restore the parser options */
+ lydctx->parse_opts = prev_opts;
+ LY_CHECK_RET(r);
+ } else {
+ LY_CHECK_RET(lydjson_metadata(lydctx, snode, attr_node));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a single anydata/anyxml node.
+ *
+ * @param[in] lydctx JSON data parser context. When the function returns, the context is in the same state
+ * as before calling, despite it is necessary to process input data for checking.
+ * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[in,out] status JSON parser status, is updated.
+ * @param[out] node Parsed data (or opaque) node.
+ * @return LY_SUCCESS if a node was successfully parsed,
+ * @return LY_ENOT in case of invalid JSON encoding,
+ * @return LY_ERR on other errors.
+ */
+static LY_ERR
+lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
+ enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint32_t prev_parse_opts, prev_int_opts;
+ struct ly_in in_start;
+ char *val = NULL;
+ struct lyd_node *tree = NULL;
+
+ assert(snode->nodetype & LYD_NODE_ANY);
+
+ /* status check according to allowed JSON types */
+ if (snode->nodetype == LYS_ANYXML) {
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY) && (*status != LYJSON_ARRAY) &&
+ (*status != LYJSON_ARRAY_EMPTY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
+ (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL), LY_ENOT);
+ } else {
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
+ }
+
+ /* create any node */
+ switch (*status) {
+ case LYJSON_OBJECT:
+ /* parse any data tree with correct options, first backup the current options and then make the parser
+ * process data as opaq nodes */
+ prev_parse_opts = lydctx->parse_opts;
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
+ prev_int_opts = lydctx->int_opts;
+ lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+
+ /* process the anydata content */
+ while (*status != LYJSON_OBJECT_CLOSED) {
+ LY_CHECK_RET(lydjson_subtree_r(lydctx, NULL, &tree, NULL));
+ *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ }
+
+ /* restore parser options */
+ lydctx->parse_opts = prev_parse_opts;
+ lydctx->int_opts = prev_int_opts;
+
+ /* finish linking metadata */
+ LY_CHECK_RET(lydjson_metadata_finish(lydctx, &tree));
+
+ LY_CHECK_RET(lyd_create_any(snode, tree, LYD_ANYDATA_DATATREE, 1, node));
+ break;
+ case LYJSON_ARRAY_EMPTY:
+ /* store the empty array */
+ if (asprintf(&val, "[]") == -1) {
+ LOGMEM(lydctx->jsonctx->ctx);
+ return LY_EMEM;
+ }
+ LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ break;
+ case LYJSON_ARRAY:
+ /* skip until the array end */
+ in_start = *lydctx->jsonctx->in;
+ LY_CHECK_RET(lydjson_data_skip(lydctx->jsonctx));
+
+ /* make a copy of the whole array and store it */
+ if (asprintf(&val, "[%.*s", (int)(lydctx->jsonctx->in->current - in_start.current), in_start.current) == -1) {
+ LOGMEM(lydctx->jsonctx->ctx);
+ return LY_EMEM;
+ }
+ LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ break;
+ case LYJSON_STRING:
+ /* string value */
+ if (lydctx->jsonctx->dynamic) {
+ LY_CHECK_RET(lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node));
+ lydctx->jsonctx->dynamic = 0;
+ } else {
+ val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
+ LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+
+ LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node), val_err);
+ }
+ break;
+ case LYJSON_NUMBER:
+ case LYJSON_FALSE:
+ case LYJSON_TRUE:
+ /* JSON value */
+ assert(!lydctx->jsonctx->dynamic);
+ val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
+ LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+
+ LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ break;
+ case LYJSON_NULL:
+ /* no value */
+ LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node));
+ break;
+ case LYJSON_OBJECT_EMPTY:
+ /* empty object */
+ LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node));
+ break;
+ default:
+ LOGINT_RET(lydctx->jsonctx->ctx);
+ }
+
+ return LY_SUCCESS;
+
+val_err:
+ free(val);
+ return rc;
+}
+
+/**
+ * @brief Parse a single instance of an inner node.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[in,out] status JSON parser status, is updated.
+ * @param[out] node Parsed data (or opaque) node.
+ * @return LY_SUCCESS if a node was successfully parsed,
+ * @return LY_ENOT in case of invalid JSON encoding,
+ * @return LY_ERR on other errors.
+ */
+static LY_ERR
+lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
+ enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t prev_parse_opts = lydctx->parse_opts;
+
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
+
+ /* create inner node */
+ LY_CHECK_RET(lyd_create_inner(snode, node));
+
+ /* use it for logging */
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+
+ if (ext) {
+ /* only parse these extension data and validate afterwards */
+ lydctx->parse_opts |= LYD_PARSE_ONLY;
+ }
+
+ /* process children */
+ while ((*status != LYJSON_OBJECT_CLOSED) && (*status != LYJSON_OBJECT_EMPTY)) {
+ ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ }
+
+ /* finish linking metadata */
+ ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (snode->nodetype == LYS_LIST) {
+ /* check all keys exist */
+ ret = lyd_parse_check_keys(*node);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* new node validation, autodelete CANNOT occur, all nodes are new */
+ ret = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add any missing default children */
+ ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when,
+ &lydctx->node_types, &lydctx->ext_node,
+ (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ lydctx->parse_opts = prev_parse_opts;
+ LOG_LOCBACK(0, 1, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Parse a single instance of a node.
+ *
+ * @param[in] lydctx JSON data parser context. When the function returns, the context is in the same state
+ * as before calling, despite it is necessary to process input data for checking.
+ * @param[in] parent Data parent of the subtree, must be set if @p first is not.
+ * @param[in,out] first_p Pointer to the variable holding the first top-level sibling, must be set if @p parent is not.
+ * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[in] name Parsed JSON node name.
+ * @param[in] name_len Lenght of @p name.
+ * @param[in] prefix Parsed JSON node prefix.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in,out] status JSON parser status, is updated.
+ * @param[out] node Parsed data (or opaque) node.
+ * @return LY_SUCCESS if a node was successfully parsed,
+ * @return LY_ENOT in case of invalid JSON encoding,
+ * @return LY_ERR on other errors.
+ */
+static LY_ERR
+lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
+ const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len,
+ const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t type_hints = 0;
+
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+
+ ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
+ if (ret == LY_SUCCESS) {
+ assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
+ if (snode->nodetype & LYD_NODE_TERM) {
+ if ((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
+ (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL)) {
+ ret = LY_ENOT;
+ goto cleanup;
+ }
+
+ /* create terminal node */
+ LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
+ lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node), cleanup);
+
+ /* move JSON parser */
+ if (*status == LYJSON_ARRAY) {
+ /* only [null], 2 more moves are needed */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+ assert(*status == LYJSON_NULL);
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+ assert(*status == LYJSON_ARRAY_CLOSED);
+ }
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ /* create inner node */
+ LY_CHECK_GOTO(ret = lydjson_parse_instance_inner(lydctx, snode, ext, status, node), cleanup);
+ } else {
+ /* create any node */
+ LY_CHECK_GOTO(ret = lydjson_parse_any(lydctx, snode, ext, status, node), cleanup);
+ }
+
+ /* add/correct flags */
+ LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext), cleanup);
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_GOTO(ret = lyd_validate_node_ext(*node, &lydctx->ext_node), cleanup);
+ }
+ } else if (ret == LY_ENOT) {
+ /* parse it again as an opaq node */
+ LY_CHECK_GOTO(ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status,
+ first_p, node), cleanup);
+
+ if (snode->nodetype == LYS_LIST) {
+ ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST;
+ } else if (snode->nodetype == LYS_LEAFLIST) {
+ ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST;
+ }
+ } else {
+ /* error */
+ goto cleanup;
+ }
+
+cleanup:
+ LOG_LOCBACK(1, 0, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Parse JSON subtree. All leaf-list and list instances of a node are considered one subtree.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] parent Data parent of the subtree, must be set if @p first is not.
+ * @param[in,out] first_p Pointer to the variable holding the first top-level sibling, must be set if @p parent is not.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ const char *name, *prefix = NULL, *expected = NULL;
+ size_t name_len, prefix_len = 0;
+ ly_bool is_meta = 0, parse_subtree;
+ const struct lysc_node *snode = NULL;
+ struct lysc_ext_instance *ext;
+ struct lyd_node *node = NULL, *attr_node = NULL;
+ const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
+ char *value = NULL;
+
+ assert(parent || first_p);
+ assert(status == LYJSON_OBJECT);
+
+ parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
+ /* all descendants should be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+
+ /* process the node name */
+ lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_meta);
+ lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &value);
+
+ if (!is_meta || name_len || prefix_len) {
+ /* get the schema node */
+ r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext);
+ if (r == LY_ENOT) {
+ /* data parsed */
+ goto cleanup;
+ }
+ LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+
+ if (!snode) {
+ /* we will not be parsing it as metadata */
+ is_meta = 0;
+ }
+ }
+
+ if (is_meta) {
+ /* parse as metadata */
+ if (!name_len && !prefix_len) {
+ /* parent's metadata without a name - use the schema from the parent */
+ if (!parent) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON,
+ "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ attr_node = parent;
+ snode = attr_node->schema;
+ }
+ ret = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
+ first_p, &node);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else if (!snode) {
+ /* parse as an opaq node */
+ assert((lydctx->parse_opts & LYD_PARSE_OPAQ) || (lydctx->int_opts));
+
+ /* opaq node cannot have an empty string as the name. */
+ if (name_len == 0) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "A JSON object member name cannot be a zero-length string.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* move to the second item in the name/X pair and parse opaq */
+ ret = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* parse as a standard lyd_node but it can still turn out to be an opaque node */
+
+ /* move to the second item in the name/X pair */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+
+ /* set expected representation */
+ switch (snode->nodetype) {
+ case LYS_LEAFLIST:
+ expected = "name/array of values";
+ break;
+ case LYS_LIST:
+ expected = "name/array of objects";
+ break;
+ case LYS_LEAF:
+ if (status == LYJSON_ARRAY) {
+ expected = "name/[null]";
+ } else {
+ expected = "name/value";
+ }
+ break;
+ case LYS_CONTAINER:
+ case LYS_NOTIF:
+ case LYS_ACTION:
+ case LYS_RPC:
+ case LYS_ANYDATA:
+ expected = "name/object";
+ break;
+ case LYS_ANYXML:
+ if (status == LYJSON_ARRAY) {
+ expected = "name/array";
+ } else {
+ expected = "name/value";
+ }
+ break;
+ }
+
+ /* check the representation according to the nodetype and then continue with the content */
+ switch (snode->nodetype) {
+ case LYS_LEAFLIST:
+ case LYS_LIST:
+ if (status == LYJSON_ARRAY_EMPTY) {
+ /* no instances, skip */
+ break;
+ }
+ LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
+
+ /* move into array */
+ ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* process all the values/objects */
+ do {
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ &status, &node);
+ if (ret == LY_ENOT) {
+ goto representation_error;
+ } else if (ret) {
+ goto cleanup;
+ }
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
+
+ /* move after the item(s) */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ } while (status != LYJSON_ARRAY_CLOSED);
+
+ break;
+ case LYS_LEAF:
+ case LYS_CONTAINER:
+ case LYS_NOTIF:
+ case LYS_ACTION:
+ case LYS_RPC:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ /* process the value/object */
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ &status, &node);
+ if (ret == LY_ENOT) {
+ goto representation_error;
+ } else if (ret) {
+ goto cleanup;
+ }
+
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lydctx->op_node = node;
+ }
+ break;
+ }
+ }
+
+ /* finally connect the parsed node */
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
+
+ /* rememeber a successfully parsed node */
+ if (parsed && node) {
+ ly_set_add(parsed, node, 1, NULL);
+ }
+
+ if (!parse_subtree) {
+ /* move after the item(s) */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ }
+
+ /* success */
+ goto cleanup;
+
+representation_error:
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "The %s \"%s\" is expected to be represented as JSON %s, but input data contains name/%s.",
+ lys_nodetype2str(snode->nodetype), snode->name, expected, lyjson_token2str(status));
+ ret = LY_EVALID;
+
+cleanup:
+ free(value);
+ lyd_free_tree(node);
+ return ret;
+}
+
+/**
+ * @brief Common start of JSON parser processing different types of the input data.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @param[out] status Storage for the current context's status
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
+ struct lyd_json_ctx **lydctx_p, enum LYJSON_PARSER_STATUS *status)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_json_ctx *lydctx;
+ size_t i;
+ ly_bool subtree;
+
+ assert(lydctx_p);
+ assert(status);
+
+ /* init context */
+ lydctx = calloc(1, sizeof *lydctx);
+ LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
+ lydctx->parse_opts = parse_opts;
+ lydctx->val_opts = val_opts;
+ lydctx->free = lyd_json_ctx_free;
+
+ /* starting top-level */
+ for (i = 0; in->current[i] != '\0' && is_jsonws(in->current[i]); i++) {
+ if (in->current[i] == '\n') {
+ /* new line */
+ LY_IN_NEW_LINE(in);
+ }
+ }
+
+ subtree = (parse_opts & LYD_PARSE_SUBTREE) ? 1 : 0;
+ LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, subtree, &lydctx->jsonctx), free(lydctx), ret);
+ *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+
+ if ((*status == LYJSON_END) || (*status == LYJSON_OBJECT_EMPTY) || (*status == LYJSON_OBJECT)) {
+ *lydctx_p = lydctx;
+ return LY_SUCCESS;
+ } else {
+ /* expecting top-level object */
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(*status));
+ *lydctx_p = NULL;
+ lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+ return LY_EVALID;
+ }
+}
+
+LY_ERR
+lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
+ struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_json_ctx *lydctx = NULL;
+ enum LYJSON_PARSER_STATUS status;
+
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx, &status);
+ LY_CHECK_GOTO(rc || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
+
+ assert(status == LYJSON_OBJECT);
+
+ lydctx->int_opts = int_opts;
+ lydctx->ext = ext;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* read subtree(s) */
+ while (lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
+ rc = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ status = lyjson_ctx_status(lydctx->jsonctx, 0);
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
+ (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* finish linking metadata */
+ rc = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (parse_opts & LYD_PARSE_SUBTREE) {
+ /* check for a sibling object */
+ assert(subtree_sibling);
+ if (lydctx->jsonctx->in->current[0] == ',') {
+ *subtree_sibling = 1;
+
+ /* move to the next object */
+ ly_in_skip(lydctx->jsonctx->in, 1);
+ } else {
+ *subtree_sibling = 0;
+ }
+ }
+
+cleanup:
+ /* there should be no unresolved types stored */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
+ if (rc) {
+ lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyjson_ctx_free(lydctx->jsonctx);
+ lydctx->jsonctx = NULL;
+ }
+ return rc;
+}
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
new file mode 100644
index 0000000..f898085
--- /dev/null
+++ b/src/parser_lyb.c
@@ -0,0 +1,1792 @@
+/**
+ * @file parser_lyb.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief LYB data parser for libyang
+ *
+ * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "lyb.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "hash_table.h"
+#include "in.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "plugins_exts.h"
+#include "plugins_exts/metadata.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "validation.h"
+#include "xml.h"
+
+static LY_ERR lyb_parse_siblings(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed);
+
+void
+lylyb_ctx_free(struct lylyb_ctx *ctx)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FREE(ctx->siblings);
+ LY_ARRAY_FREE(ctx->models);
+
+ LY_ARRAY_FOR(ctx->sib_hts, u) {
+ lyht_free(ctx->sib_hts[u].ht);
+ }
+ LY_ARRAY_FREE(ctx->sib_hts);
+
+ free(ctx);
+}
+
+void
+lyd_lyb_ctx_free(struct lyd_ctx *lydctx)
+{
+ struct lyd_lyb_ctx *ctx = (struct lyd_lyb_ctx *)lydctx;
+
+ lyd_ctx_free(lydctx);
+ lylyb_ctx_free(ctx->lybctx);
+ free(ctx);
+}
+
+/**
+ * @brief Read metadata about siblings.
+ *
+ * @param[out] sib Structure in which the metadata will be stored.
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_read_sibling_meta(struct lyd_lyb_sibling *sib, struct lylyb_ctx *lybctx)
+{
+ uint8_t meta_buf[LYB_META_BYTES];
+ uint64_t num = 0;
+
+ ly_in_read(lybctx->in, meta_buf, LYB_META_BYTES);
+
+ memcpy(&num, meta_buf, LYB_SIZE_BYTES);
+ sib->written = le64toh(num);
+ memcpy(&num, meta_buf + LYB_SIZE_BYTES, LYB_INCHUNK_BYTES);
+ sib->inner_chunks = le64toh(num);
+
+ /* remember whether there is a following chunk or not */
+ sib->position = (sib->written == LYB_SIZE_MAX ? 1 : 0);
+}
+
+/**
+ * @brief Read YANG data from LYB input. Metadata are handled transparently and not returned.
+ *
+ * @param[in] buf Destination buffer.
+ * @param[in] count Number of bytes to read.
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_read(uint8_t *buf, size_t count, struct lylyb_ctx *lybctx)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_lyb_sibling *empty;
+ size_t to_read;
+
+ assert(lybctx);
+
+ while (1) {
+ /* check for fully-read (empty) data chunks */
+ to_read = count;
+ empty = NULL;
+ LY_ARRAY_FOR(lybctx->siblings, u) {
+ /* we want the innermost chunks resolved first, so replace previous empty chunks,
+ * also ignore chunks that are completely finished, there is nothing for us to do */
+ if ((lybctx->siblings[u].written <= to_read) && lybctx->siblings[u].position) {
+ /* empty chunk, do not read more */
+ to_read = lybctx->siblings[u].written;
+ empty = &lybctx->siblings[u];
+ }
+ }
+
+ if (!empty && !count) {
+ break;
+ }
+
+ /* we are actually reading some data, not just finishing another chunk */
+ if (to_read) {
+ if (buf) {
+ ly_in_read(lybctx->in, buf, to_read);
+ } else {
+ ly_in_skip(lybctx->in, to_read);
+ }
+
+ LY_ARRAY_FOR(lybctx->siblings, u) {
+ /* decrease all written counters */
+ lybctx->siblings[u].written -= to_read;
+ assert(lybctx->siblings[u].written <= LYB_SIZE_MAX);
+ }
+ /* decrease count/buf */
+ count -= to_read;
+ if (buf) {
+ buf += to_read;
+ }
+ }
+
+ if (empty) {
+ /* read the next chunk meta information */
+ lyb_read_sibling_meta(empty, lybctx);
+ }
+ }
+}
+
+/**
+ * @brief Read a number.
+ *
+ * @param[in] num Destination buffer.
+ * @param[in] num_size Size of @p num.
+ * @param[in] bytes Number of bytes to read.
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_read_number(void *num, size_t num_size, size_t bytes, struct lylyb_ctx *lybctx)
+{
+ uint64_t buf = 0;
+
+ lyb_read((uint8_t *)&buf, bytes, lybctx);
+
+ /* correct byte order */
+ buf = le64toh(buf);
+
+ switch (num_size) {
+ case sizeof(uint8_t):
+ *((uint8_t *)num) = buf;
+ break;
+ case sizeof(uint16_t):
+ *((uint16_t *)num) = buf;
+ break;
+ case sizeof(uint32_t):
+ *((uint32_t *)num) = buf;
+ break;
+ case sizeof(uint64_t):
+ *((uint64_t *)num) = buf;
+ break;
+ default:
+ LOGINT(lybctx->ctx);
+ }
+}
+
+/**
+ * @brief Read a string.
+ *
+ * @param[in] str Destination buffer, is allocated.
+ * @param[in] len_size Number of bytes on which the length of the string is written.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_string(char **str, uint8_t len_size, struct lylyb_ctx *lybctx)
+{
+ uint64_t len = 0;
+
+ assert((len_size == 1) || (len_size == 2) || (len_size == 4) || (len_size == 8));
+
+ *str = NULL;
+
+ lyb_read_number(&len, sizeof len, len_size, lybctx);
+
+ *str = malloc((len + 1) * sizeof **str);
+ LY_CHECK_ERR_RET(!*str, LOGMEM(lybctx->ctx), LY_EMEM);
+
+ lyb_read((uint8_t *)*str, len, lybctx);
+
+ (*str)[len] = '\0';
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Skip string.
+ *
+ * @param[in] len_size Number of bytes on which the length of the string is written.
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_skip_string(uint8_t len_size, struct lylyb_ctx *lybctx)
+{
+ size_t len = 0;
+
+ lyb_read_number(&len, sizeof len, len_size, lybctx);
+
+ lyb_read(NULL, len, lybctx);
+}
+
+/**
+ * @brief Read value of term node.
+ *
+ * @param[in] term Compiled term node.
+ * @param[out] term_value Set to term node value in dynamically
+ * allocated memory. The caller must release it.
+ * @param[out] term_value_len Value length in bytes. The zero byte is
+ * always included and is not counted.
+ * @param[in,out] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_term_value(const struct lysc_node_leaf *term, uint8_t **term_value, uint64_t *term_value_len,
+ struct lylyb_ctx *lybctx)
+{
+ uint32_t allocated_size;
+ int32_t lyb_data_len;
+ struct lysc_type_leafref *type_lf;
+
+ assert(term && term_value && term_value_len && lybctx);
+
+ /* Find out the size from @ref howtoDataLYB. */
+ if (term->type->basetype == LY_TYPE_LEAFREF) {
+ /* Leafref itself is ignored, the target is loaded directly. */
+ type_lf = (struct lysc_type_leafref *)term->type;
+ lyb_data_len = type_lf->realtype->plugin->lyb_data_len;
+ } else {
+ lyb_data_len = term->type->plugin->lyb_data_len;
+ }
+
+ if (lyb_data_len < 0) {
+ /* Parse value size. */
+ lyb_read_number(term_value_len, sizeof *term_value_len,
+ sizeof *term_value_len, lybctx);
+ } else {
+ /* Data size is fixed. */
+ *term_value_len = lyb_data_len;
+ }
+
+ /* Allocate memory. */
+ allocated_size = *term_value_len + 1;
+ *term_value = malloc(allocated_size * sizeof **term_value);
+ LY_CHECK_ERR_RET(!*term_value, LOGMEM(lybctx->ctx), LY_EMEM);
+
+ if (*term_value_len > 0) {
+ /* Parse value. */
+ lyb_read(*term_value, *term_value_len, lybctx);
+ }
+
+ /* Add extra zero byte regardless of whether it is string or not. */
+ (*term_value)[allocated_size - 1] = 0;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Stop the current "siblings" - change LYB context state.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_stop_siblings(struct lylyb_ctx *lybctx)
+{
+ if (LYB_LAST_SIBLING(lybctx).written) {
+ LOGINT_RET(lybctx->ctx);
+ }
+
+ LY_ARRAY_DECREMENT(lybctx->siblings);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Start a new "siblings" - change LYB context state but also read the expected metadata.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_start_siblings(struct lylyb_ctx *lybctx)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ u = LY_ARRAY_COUNT(lybctx->siblings);
+ if (u == lybctx->sibling_size) {
+ LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->siblings, u + LYB_SIBLING_STEP, LY_EMEM);
+ lybctx->sibling_size = u + LYB_SIBLING_STEP;
+ }
+
+ LY_ARRAY_INCREMENT(lybctx->siblings);
+ lyb_read_sibling_meta(&LYB_LAST_SIBLING(lybctx), lybctx);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Read YANG model info.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[out] mod_name Module name, if any.
+ * @param[out] mod_rev Module revision, "" if none.
+ * @param[in,out] feat_set Set to add the names of enabled features to. If not set, enabled features are not parsed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_model(struct lylyb_ctx *lybctx, char **mod_name, char mod_rev[], struct ly_set *feat_set)
+{
+ uint16_t i, rev, length;
+ char *str;
+
+ *mod_name = NULL;
+ mod_rev[0] = '\0';
+
+ lyb_read_number(&length, 2, 2, lybctx);
+ if (!length) {
+ return LY_SUCCESS;
+ }
+
+ /* module name */
+ *mod_name = malloc(length + 1);
+ LY_CHECK_ERR_RET(!*mod_name, LOGMEM(lybctx->ctx), LY_EMEM);
+ lyb_read(((uint8_t *)*mod_name), length, lybctx);
+ (*mod_name)[length] = '\0';
+
+ /* module revision */
+ lyb_read_number(&rev, sizeof rev, 2, lybctx);
+
+ if (rev) {
+ sprintf(mod_rev, "%04u-%02u-%02u", ((rev & LYB_REV_YEAR_MASK) >> LYB_REV_YEAR_SHIFT) + LYB_REV_YEAR_OFFSET,
+ (rev & LYB_REV_MONTH_MASK) >> LYB_REV_MONTH_SHIFT, rev & LYB_REV_DAY_MASK);
+ }
+
+ if (!feat_set) {
+ /* enabled features not printed */
+ return LY_SUCCESS;
+ }
+
+ /* enabled feature count */
+ lyb_read_number(&length, sizeof length, sizeof length, lybctx);
+ if (!length) {
+ return LY_SUCCESS;
+ }
+
+ /* enabled features */
+ for (i = 0; i < length; ++i) {
+ LY_CHECK_RET(lyb_read_string(&str, sizeof length, lybctx));
+ ly_set_add(feat_set, str, 1, NULL);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse YANG model info.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parse_options Flag with options for parsing.
+ * @param[in] with_features Whether the enabled features were also printed and should be read.
+ * @param[out] mod Parsed module.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_model(struct lylyb_ctx *lybctx, uint32_t parse_options, ly_bool with_features, const struct lys_module **mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lys_module *m = NULL;
+ char *mod_name = NULL, mod_rev[LY_REV_SIZE];
+ struct ly_set feat_set = {0};
+ struct lysp_feature *f = NULL;
+ uint32_t i, idx = 0;
+ ly_bool enabled;
+
+ /* read module info */
+ if ((ret = lyb_read_model(lybctx, &mod_name, mod_rev, with_features ? &feat_set : NULL))) {
+ goto cleanup;
+ }
+
+ /* get the module */
+ if (mod_rev[0]) {
+ m = ly_ctx_get_module(lybctx->ctx, mod_name, mod_rev);
+ if ((parse_options & LYD_PARSE_LYB_MOD_UPDATE) && !m) {
+ /* try to use an updated module */
+ m = ly_ctx_get_module_implemented(lybctx->ctx, mod_name);
+ if (m && (!m->revision || (strcmp(m->revision, mod_rev) < 0))) {
+ /* not an implemented module in a newer revision */
+ m = NULL;
+ }
+ }
+ } else {
+ m = ly_ctx_get_module_latest(lybctx->ctx, mod_name);
+ }
+
+ if (!m || !m->implemented) {
+ if (parse_options & LYD_PARSE_STRICT) {
+ if (!m) {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, missing module \"%s%s%s\".",
+ mod_name, mod_rev[0] ? "@" : "", mod_rev[0] ? mod_rev : "");
+ } else if (!m->implemented) {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, module \"%s%s%s\" not implemented.",
+ mod_name, mod_rev[0] ? "@" : "", mod_rev[0] ? mod_rev : "");
+ }
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+
+ goto cleanup;
+ }
+
+ if (with_features) {
+ /* check features */
+ while ((f = lysp_feature_next(f, m->parsed, &idx))) {
+ enabled = 0;
+ for (i = 0; i < feat_set.count; ++i) {
+ if (!strcmp(feat_set.objs[i], f->name)) {
+ enabled = 1;
+ break;
+ }
+ }
+
+ if (enabled && !(f->flags & LYS_FENABLED)) {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, module \"%s\" has \"%s\" feature disabled.",
+ mod_name, f->name);
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (!enabled && (f->flags & LYS_FENABLED)) {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, module \"%s\" has \"%s\" feature enabled.",
+ mod_name, f->name);
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* fill cached hashes, if not already */
+ lyb_cache_module_hash(m);
+
+cleanup:
+ *mod = m;
+ free(mod_name);
+ ly_set_erase(&feat_set, free);
+ return ret;
+}
+
+/**
+ * @brief Parse YANG node metadata.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] sparent Schema parent node of the metadata.
+ * @param[out] meta Parsed metadata.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_metadata(struct lyd_lyb_ctx *lybctx, const struct lysc_node *sparent, struct lyd_meta **meta)
+{
+ LY_ERR ret = LY_SUCCESS;
+ ly_bool dynamic;
+ uint8_t i, count = 0;
+ char *meta_name = NULL, *meta_value;
+ const struct lys_module *mod;
+
+ /* read number of attributes stored */
+ lyb_read(&count, 1, lybctx->lybctx);
+
+ /* read attributes */
+ for (i = 0; i < count; ++i) {
+ /* find model */
+ ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_opts, 0, &mod);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!mod) {
+ /* skip meta name */
+ lyb_skip_string(sizeof(uint16_t), lybctx->lybctx);
+
+ /* skip meta value */
+ lyb_skip_string(sizeof(uint16_t), lybctx->lybctx);
+ continue;
+ }
+
+ /* meta name */
+ ret = lyb_read_string(&meta_name, sizeof(uint16_t), lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* meta value */
+ ret = lyb_read_string(&meta_value, sizeof(uint64_t), lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ dynamic = 1;
+
+ /* create metadata */
+ ret = lyd_parser_create_meta((struct lyd_ctx *)lybctx, NULL, meta, mod, meta_name, strlen(meta_name), meta_value,
+ ly_strlen(meta_value), &dynamic, LY_VALUE_JSON, NULL, LYD_HINT_DATA, sparent);
+
+ /* free strings */
+ free(meta_name);
+ meta_name = NULL;
+ if (dynamic) {
+ free(meta_value);
+ dynamic = 0;
+ }
+
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ free(meta_name);
+ if (ret) {
+ lyd_free_meta_siblings(*meta);
+ *meta = NULL;
+ }
+ return ret;
+}
+
+/**
+ * @brief Parse format-specific prefix data.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] format Prefix data format.
+ * @param[out] prefix_data Parsed prefix data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_prefix_data(struct lylyb_ctx *lybctx, LY_VALUE_FORMAT format, void **prefix_data)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint8_t count, i;
+ struct ly_set *set = NULL;
+ struct lyxml_ns *ns = NULL;
+
+ switch (format) {
+ case LY_VALUE_XML:
+ /* read count */
+ lyb_read(&count, 1, lybctx);
+
+ /* read all NS elements */
+ LY_CHECK_GOTO(ret = ly_set_new(&set), cleanup);
+
+ for (i = 0; i < count; ++i) {
+ ns = calloc(1, sizeof *ns);
+
+ /* prefix */
+ LY_CHECK_GOTO(ret = lyb_read_string(&ns->prefix, sizeof(uint16_t), lybctx), cleanup);
+ if (!strlen(ns->prefix)) {
+ free(ns->prefix);
+ ns->prefix = NULL;
+ }
+
+ /* namespace */
+ LY_CHECK_GOTO(ret = lyb_read_string(&ns->uri, sizeof(uint16_t), lybctx), cleanup);
+
+ LY_CHECK_GOTO(ret = ly_set_add(set, ns, 1, NULL), cleanup);
+ ns = NULL;
+ }
+
+ *prefix_data = set;
+ break;
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ /* nothing stored */
+ break;
+ default:
+ LOGINT(lybctx->ctx);
+ ret = LY_EINT;
+ break;
+ }
+
+cleanup:
+ if (ret) {
+ ly_free_prefix_data(format, set);
+ if (ns) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ }
+ }
+ return ret;
+}
+
+/**
+ * @brief Parse opaque attributes.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[out] attr Parsed attributes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_attributes(struct lylyb_ctx *lybctx, struct lyd_attr **attr)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint8_t count, i;
+ struct lyd_attr *attr2 = NULL;
+ char *prefix = NULL, *module_name = NULL, *name = NULL, *value = NULL;
+ ly_bool dynamic = 0;
+ LY_VALUE_FORMAT format = 0;
+ void *val_prefix_data = NULL;
+
+ /* read count */
+ lyb_read(&count, 1, lybctx);
+
+ /* read attributes */
+ for (i = 0; i < count; ++i) {
+ /* prefix, may be empty */
+ ret = lyb_read_string(&prefix, sizeof(uint16_t), lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ if (!prefix[0]) {
+ free(prefix);
+ prefix = NULL;
+ }
+
+ /* namespace, may be empty */
+ ret = lyb_read_string(&module_name, sizeof(uint16_t), lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ if (!module_name[0]) {
+ free(module_name);
+ module_name = NULL;
+ }
+
+ /* name */
+ ret = lyb_read_string(&name, sizeof(uint16_t), lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* format */
+ lyb_read_number(&format, sizeof format, 1, lybctx);
+
+ /* value prefixes */
+ ret = lyb_parse_prefix_data(lybctx, format, &val_prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* value */
+ ret = lyb_read_string(&value, sizeof(uint64_t), lybctx);
+ LY_CHECK_ERR_GOTO(ret, ly_free_prefix_data(format, val_prefix_data), cleanup);
+ dynamic = 1;
+
+ /* attr2 is always changed to the created attribute */
+ ret = lyd_create_attr(NULL, &attr2, lybctx->ctx, name, strlen(name), prefix, ly_strlen(prefix), module_name,
+ ly_strlen(module_name), value, ly_strlen(value), &dynamic, format, val_prefix_data, LYD_HINT_DATA);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ free(prefix);
+ prefix = NULL;
+ free(module_name);
+ module_name = NULL;
+ free(name);
+ name = NULL;
+ assert(!dynamic);
+ value = NULL;
+
+ if (!*attr) {
+ *attr = attr2;
+ }
+
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ free(prefix);
+ free(module_name);
+ free(name);
+ if (dynamic) {
+ free(value);
+ }
+ if (ret) {
+ lyd_free_attr_siblings(lybctx->ctx, *attr);
+ *attr = NULL;
+ }
+ return ret;
+}
+
+/**
+ * @brief Fill @p hash with hash values.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in,out] hash Pointer to the array in which the hash values are to be written.
+ * @param[out] hash_count Number of hashes in @p hash.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_hashes(struct lylyb_ctx *lybctx, LYB_HASH *hash, uint8_t *hash_count)
+{
+ uint8_t i = 0, j;
+
+ /* read the first hash */
+ lyb_read(&hash[0], sizeof *hash, lybctx);
+
+ if (!hash[0]) {
+ *hash_count = i + 1;
+ return LY_SUCCESS;
+ }
+
+ /* based on the first hash read all the other ones, if any */
+ for (i = 0; !(hash[0] & (LYB_HASH_COLLISION_ID >> i)); ++i) {
+ if (i > LYB_HASH_BITS) {
+ LOGINT_RET(lybctx->ctx);
+ }
+ }
+
+ /* move the first hash on its accurate position */
+ hash[i] = hash[0];
+
+ /* read the rest of hashes */
+ for (j = i; j; --j) {
+ lyb_read(&hash[j - 1], sizeof *hash, lybctx);
+
+ /* correct collision ID */
+ assert(hash[j - 1] & (LYB_HASH_COLLISION_ID >> (j - 1)));
+ /* preceded with zeros */
+ assert(!(hash[j - 1] & (LYB_HASH_MASK << (LYB_HASH_BITS - (j - 1)))));
+ }
+
+ *hash_count = i + 1;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check whether a schema node matches a hash(es).
+ *
+ * @param[in] sibling Schema node to check.
+ * @param[in] hash Hash array to check.
+ * @param[in] hash_count Number of hashes in @p hash.
+ * @return non-zero if matches,
+ * @return 0 if not.
+ */
+static int
+lyb_is_schema_hash_match(struct lysc_node *sibling, LYB_HASH *hash, uint8_t hash_count)
+{
+ LYB_HASH sibling_hash;
+ uint8_t i;
+
+ /* compare all the hashes starting from collision ID 0 */
+ for (i = 0; i < hash_count; ++i) {
+ sibling_hash = lyb_get_hash(sibling, i);
+ if (sibling_hash != hash[i]) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Parse schema node hash.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] sparent Schema parent, must be set if @p mod is not.
+ * @param[in] mod Module of the top-level node, must be set if @p sparent is not.
+ * @param[out] snode Parsed found schema node, may be NULL if opaque.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_schema_hash(struct lyd_lyb_ctx *lybctx, const struct lysc_node *sparent, const struct lys_module *mod,
+ const struct lysc_node **snode)
+{
+ LY_ERR ret;
+ const struct lysc_node *sibling;
+ LYB_HASH hash[LYB_HASH_BITS - 1];
+ uint32_t getnext_opts;
+ uint8_t hash_count;
+
+ *snode = NULL;
+
+ ret = lyb_read_hashes(lybctx->lybctx, hash, &hash_count);
+ LY_CHECK_RET(ret);
+
+ if (!hash[0]) {
+ /* opaque node */
+ return LY_SUCCESS;
+ }
+
+ getnext_opts = lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
+
+ /* find our node with matching hashes */
+ sibling = NULL;
+ while (1) {
+ if (!sparent && lybctx->ext) {
+ sibling = lys_getnext_ext(sibling, sparent, lybctx->ext, getnext_opts);
+ } else {
+ sibling = lys_getnext(sibling, sparent, mod ? mod->compiled : NULL, getnext_opts);
+ }
+ if (!sibling) {
+ break;
+ }
+ /* skip schema nodes from models not present during printing */
+ if (((sibling->module->ctx != lybctx->lybctx->ctx) || lyb_has_schema_model(sibling, lybctx->lybctx->models)) &&
+ lyb_is_schema_hash_match((struct lysc_node *)sibling, hash, hash_count)) {
+ /* match found */
+ break;
+ }
+ }
+
+ if (!sibling && (lybctx->parse_opts & LYD_PARSE_STRICT)) {
+ if (lybctx->ext) {
+ LOGVAL(lybctx->lybctx->ctx, LYVE_REFERENCE, "Failed to find matching hash for a node from \"%s\" extension instance node.",
+ lybctx->ext->def->name);
+ } else if (mod) {
+ LOGVAL(lybctx->lybctx->ctx, LYVE_REFERENCE, "Failed to find matching hash for a top-level node"
+ " from \"%s\".", mod->name);
+ } else {
+ LOGVAL(lybctx->lybctx->ctx, LYVE_REFERENCE, "Failed to find matching hash for a child node"
+ " of \"%s\".", sparent->name);
+ }
+ return LY_EVALID;
+ } else if (sibling && (ret = lyd_parser_check_schema((struct lyd_ctx *)lybctx, sibling))) {
+ return ret;
+ }
+
+ *snode = sibling;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse schema node name of a nested extension data node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent.
+ * @param[in] mod_name Module name of the node.
+ * @param[out] snode Parsed found schema node of a nested extension.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_schema_nested_ext(struct lyd_lyb_ctx *lybctx, const struct lyd_node *parent, const char *mod_name,
+ const struct lysc_node **snode)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ char *name = NULL;
+ struct lysc_ext_instance *ext;
+
+ assert(parent);
+
+ /* read schema node name */
+ LY_CHECK_GOTO(rc = lyb_read_string(&name, sizeof(uint16_t), lybctx->lybctx), cleanup);
+
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, mod_name, mod_name ? strlen(mod_name) : 0, LY_VALUE_JSON, NULL, name,
+ strlen(name), snode, &ext);
+ if (r == LY_ENOT) {
+ /* failed to parse */
+ LOGERR(lybctx->lybctx->ctx, LY_EINVAL, "Failed to parse node \"%s\" as nested extension instance data.", name);
+ rc = LY_EINVAL;
+ goto cleanup;
+ } else if (r) {
+ /* error */
+ rc = r;
+ goto cleanup;
+ }
+
+ /* fill cached hashes in the module, it may be from a different context */
+ lyb_cache_module_hash((*snode)->module);
+
+cleanup:
+ free(name);
+ return rc;
+}
+
+/**
+ * @brief Read until the end of the current siblings.
+ *
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_skip_siblings(struct lylyb_ctx *lybctx)
+{
+ do {
+ /* first skip any meta information inside */
+ ly_in_skip(lybctx->in, LYB_LAST_SIBLING(lybctx).inner_chunks * LYB_META_BYTES);
+
+ /* then read data */
+ lyb_read(NULL, LYB_LAST_SIBLING(lybctx).written, lybctx);
+ } while (LYB_LAST_SIBLING(lybctx).written);
+}
+
+/**
+ * @brief Insert new node to @p parsed set.
+ *
+ * Also if needed, correct @p first_p.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first_p is not.
+ * @param[in,out] node Parsed node to insertion.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static void
+lyb_insert_node(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node *node, struct lyd_node **first_p,
+ struct ly_set *parsed)
+{
+ /* insert, keep first pointer correct */
+ if (parent && (LYD_CTX(parent) != LYD_CTX(node))) {
+ lyplg_ext_insert(parent, node);
+ } else {
+ lyd_insert_node(parent, first_p, node, lybctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+ }
+ while (!parent && (*first_p)->prev->next) {
+ *first_p = (*first_p)->prev;
+ }
+
+ /* rememeber a successfully parsed node */
+ if (parsed) {
+ ly_set_add(parsed, node, 1, NULL);
+ }
+}
+
+/**
+ * @brief Finish parsing the opaq node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first_p is not.
+ * @param[in] flags Node flags to set.
+ * @param[in,out] attr Attributes to be attached. Finally set to NULL.
+ * @param[in,out] node Parsed opaq node to finish.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static void
+lyb_finish_opaq(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, uint32_t flags, struct lyd_attr **attr,
+ struct lyd_node **node, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ struct lyd_attr *iter;
+
+ /* set flags */
+ (*node)->flags = flags;
+
+ /* add attributes */
+ assert(!(*node)->schema);
+ LY_LIST_FOR(*attr, iter) {
+ iter->parent = (struct lyd_node_opaq *)*node;
+ }
+ ((struct lyd_node_opaq *)*node)->attr = *attr;
+ *attr = NULL;
+
+ lyb_insert_node(lybctx, parent, *node, first_p, parsed);
+ *node = NULL;
+}
+
+/**
+ * @brief Finish parsing the node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first_p is not.
+ * @param[in] flags Node flags to set.
+ * @param[in,out] meta Metadata to be attached. Finally set to NULL.
+ * @param[in,out] node Parsed node to finish.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static void
+lyb_finish_node(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, uint32_t flags, struct lyd_meta **meta,
+ struct lyd_node **node, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ struct lyd_meta *m;
+
+ /* set flags */
+ (*node)->flags = flags;
+
+ /* add metadata */
+ LY_LIST_FOR(*meta, m) {
+ m->parent = *node;
+ }
+ (*node)->meta = *meta;
+ *meta = NULL;
+
+ /* insert into parent */
+ lyb_insert_node(lybctx, parent, *node, first_p, parsed);
+
+ if (!(lybctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* store for ext instance node validation, if needed */
+ (void)lyd_validate_node_ext(*node, &lybctx->ext_node);
+ }
+
+ *node = NULL;
+}
+
+/**
+ * @brief Parse header for non-opaq node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] sparent Schema parent node of the metadata.
+ * @param[out] flags Parsed node flags.
+ * @param[out] meta Parsed metadata of the node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_header(struct lyd_lyb_ctx *lybctx, const struct lysc_node *sparent, uint32_t *flags, struct lyd_meta **meta)
+{
+ LY_ERR ret;
+
+ /* create and read metadata */
+ ret = lyb_parse_metadata(lybctx, sparent, meta);
+ LY_CHECK_RET(ret);
+
+ /* read flags */
+ lyb_read_number(flags, sizeof *flags, sizeof *flags, lybctx->lybctx);
+
+ return ret;
+}
+
+/**
+ * @brief Create term node and fill it with value.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] snode Schema of the term node.
+ * @param[out] node Created term node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_create_term(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snode, struct lyd_node **node)
+{
+ LY_ERR ret;
+ ly_bool dynamic;
+ uint8_t *term_value;
+ uint64_t term_value_len;
+
+ ret = lyb_read_term_value((struct lysc_node_leaf *)snode, &term_value, &term_value_len, lybctx->lybctx);
+ LY_CHECK_RET(ret);
+
+ dynamic = 1;
+ /* create node */
+ ret = lyd_parser_create_term((struct lyd_ctx *)lybctx, snode,
+ term_value, term_value_len, &dynamic, LY_VALUE_LYB,
+ NULL, LYD_HINT_DATA, node);
+ if (dynamic) {
+ free(term_value);
+ }
+ if (ret) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Validate inner node, autodelete default values nad create implicit nodes.
+ *
+ * @param[in,out] lybctx LYB context.
+ * @param[in] snode Schema of the inner node.
+ * @param[in] node Parsed inner node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_validate_node_inner(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snode, struct lyd_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t impl_opts;
+
+ if (!(lybctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* new node validation, autodelete CANNOT occur, all nodes are new */
+ ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
+ LY_CHECK_RET(ret);
+
+ /* add any missing default children */
+ impl_opts = (lybctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0;
+ ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lybctx->node_when, &lybctx->node_types,
+ &lybctx->ext_node, impl_opts, NULL);
+ LY_CHECK_RET(ret);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Parse opaq node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling.
+ * @param[in,out] first_p First top-level sibling.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_opaq(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret;
+ struct lyd_node *node = NULL;
+ struct lyd_attr *attr = NULL;
+ char *value = NULL, *name = NULL, *prefix = NULL, *module_key = NULL;
+ ly_bool dynamic = 0;
+ LY_VALUE_FORMAT format = 0;
+ void *val_prefix_data = NULL;
+ const struct ly_ctx *ctx = lybctx->lybctx->ctx;
+ uint32_t flags;
+
+ /* parse opaq node attributes */
+ ret = lyb_parse_attributes(lybctx->lybctx, &attr);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* read flags */
+ lyb_read_number(&flags, sizeof flags, sizeof flags, lybctx->lybctx);
+
+ /* parse prefix */
+ ret = lyb_read_string(&prefix, sizeof(uint16_t), lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse module key */
+ ret = lyb_read_string(&module_key, sizeof(uint16_t), lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse name */
+ ret = lyb_read_string(&name, sizeof(uint16_t), lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse value */
+ ret = lyb_read_string(&value, sizeof(uint64_t), lybctx->lybctx);
+ LY_CHECK_ERR_GOTO(ret, ly_free_prefix_data(format, val_prefix_data), cleanup);
+ dynamic = 1;
+
+ /* parse format */
+ lyb_read_number(&format, sizeof format, 1, lybctx->lybctx);
+
+ /* parse value prefixes */
+ ret = lyb_parse_prefix_data(lybctx->lybctx, format, &val_prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!(lybctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip children */
+ ret = lyb_read_start_siblings(lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ lyb_skip_siblings(lybctx->lybctx);
+ ret = lyb_read_stop_siblings(lybctx->lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ goto cleanup;
+ }
+
+ /* create node */
+ ret = lyd_create_opaq(ctx, name, strlen(name), prefix, ly_strlen(prefix), module_key, ly_strlen(module_key),
+ value, strlen(value), &dynamic, format, val_prefix_data, 0, &node);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* process children */
+ ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* register parsed opaq node */
+ lyb_finish_opaq(lybctx, parent, flags, &attr, &node, first_p, parsed);
+ assert(!attr && !node);
+
+cleanup:
+ free(prefix);
+ free(module_key);
+ free(name);
+ if (dynamic) {
+ free(value);
+ }
+ lyd_free_attr_siblings(ctx, attr);
+ lyd_free_tree(node);
+
+ return ret;
+}
+
+/**
+ * @brief Parse anydata or anyxml node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling.
+ * @param[in] snode Schema of the node to be parsed.
+ * @param[in,out] first_p First top-level sibling.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_any(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret;
+ struct lyd_node *node = NULL, *tree = NULL;
+ struct lyd_meta *meta = NULL;
+ LYD_ANYDATA_VALUETYPE value_type;
+ struct ly_in *in;
+ struct lyd_ctx *lydctx = NULL;
+ char *value = NULL;
+ uint32_t flags;
+ const struct ly_ctx *ctx = lybctx->lybctx->ctx;
+
+ /* read necessary basic data */
+ ret = lyb_parse_node_header(lybctx, snode, &flags, &meta);
+ LY_CHECK_GOTO(ret, error);
+
+ /* parse value type */
+ lyb_read_number(&value_type, sizeof value_type, sizeof value_type, lybctx->lybctx);
+ if ((value_type == LYD_ANYDATA_DATATREE) || ((snode->nodetype == LYS_ANYDATA) && (value_type != LYD_ANYDATA_LYB))) {
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto error;
+ }
+
+ /* read anydata content */
+ ret = lyb_read_string(&value, sizeof(uint64_t), lybctx->lybctx);
+ LY_CHECK_GOTO(ret, error);
+
+ if (value_type == LYD_ANYDATA_LYB) {
+ /* parse LYB into a data tree */
+ LY_CHECK_RET(ly_in_new_memory(value, &in));
+ ret = lyd_parse_lyb(ctx, NULL, NULL, &tree, in, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0,
+ LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS, NULL, NULL, &lydctx);
+ ly_in_free(in, 0);
+ if (lydctx) {
+ lydctx->free(lydctx);
+ }
+ LY_CHECK_ERR_GOTO(ret, lyd_free_siblings(tree), error);
+
+ /* use the parsed tree as the value */
+ free(value);
+ value = (char *)tree;
+ value_type = LYD_ANYDATA_DATATREE;
+ }
+
+ /* create the node */
+ switch (value_type) {
+ case LYD_ANYDATA_DATATREE:
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ /* use the value directly */
+ ret = lyd_create_any(snode, value, value_type, 1, &node);
+ LY_CHECK_GOTO(ret, error);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto error;
+ }
+
+ /* register parsed anydata node */
+ lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+
+ return LY_SUCCESS;
+
+error:
+ free(value);
+ lyd_free_meta_siblings(meta);
+ lyd_free_tree(node);
+ return ret;
+}
+
+/**
+ * @brief Parse inner node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first is not.
+ * @param[in] snode Schema of the node to be parsed.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *node = NULL;
+ struct lyd_meta *meta = NULL;
+ uint32_t flags;
+
+ /* read necessary basic data */
+ ret = lyb_parse_node_header(lybctx, snode, &flags, &meta);
+ LY_CHECK_GOTO(ret, error);
+
+ /* create node */
+ ret = lyd_create_inner(snode, &node);
+ LY_CHECK_GOTO(ret, error);
+
+ /* process children */
+ ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
+ LY_CHECK_GOTO(ret, error);
+
+ /* additional procedure for inner node */
+ ret = lyb_validate_node_inner(lybctx, snode, node);
+ LY_CHECK_GOTO(ret, error);
+
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lybctx->op_node = node;
+ }
+
+ /* register parsed node */
+ lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+
+ return LY_SUCCESS;
+
+error:
+ lyd_free_meta_siblings(meta);
+ lyd_free_tree(node);
+ return ret;
+}
+
+/**
+ * @brief Parse leaf node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling.
+ * @param[in] snode Schema of the node to be parsed.
+ * @param[in,out] first_p First top-level sibling.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_leaf(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret;
+ struct lyd_node *node = NULL;
+ struct lyd_meta *meta = NULL;
+ uint32_t flags;
+
+ /* read necessary basic data */
+ ret = lyb_parse_node_header(lybctx, snode, &flags, &meta);
+ LY_CHECK_GOTO(ret, error);
+
+ /* read value of term node and create it */
+ ret = lyb_create_term(lybctx, snode, &node);
+ LY_CHECK_GOTO(ret, error);
+
+ lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+
+ return LY_SUCCESS;
+
+error:
+ lyd_free_meta_siblings(meta);
+ lyd_free_tree(node);
+ return ret;
+}
+
+/**
+ * @brief Parse all leaflist nodes which belong to same schema.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling.
+ * @param[in] snode Schema of the nodes to be parsed.
+ * @param[in,out] first_p First top-level sibling.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_leaflist(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret;
+
+ /* register a new sibling */
+ ret = lyb_read_start_siblings(lybctx->lybctx);
+ LY_CHECK_RET(ret);
+
+ /* process all siblings */
+ while (LYB_LAST_SIBLING(lybctx->lybctx).written) {
+ ret = lyb_parse_node_leaf(lybctx, parent, snode, first_p, parsed);
+ LY_CHECK_RET(ret);
+ }
+
+ /* end the sibling */
+ ret = lyb_read_stop_siblings(lybctx->lybctx);
+ LY_CHECK_RET(ret);
+
+ return ret;
+}
+
+/**
+ * @brief Parse all list nodes which belong to same schema.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling.
+ * @param[in] snode Schema of the nodes to be parsed.
+ * @param[in,out] first_p First top-level sibling.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret;
+ struct lyd_node *node = NULL;
+ struct lyd_meta *meta = NULL;
+ uint32_t flags;
+
+ /* register a new sibling */
+ ret = lyb_read_start_siblings(lybctx->lybctx);
+ LY_CHECK_RET(ret);
+
+ while (LYB_LAST_SIBLING(lybctx->lybctx).written) {
+ /* read necessary basic data */
+ ret = lyb_parse_node_header(lybctx, snode, &flags, &meta);
+ LY_CHECK_GOTO(ret, error);
+
+ /* create list node */
+ ret = lyd_create_inner(snode, &node);
+ LY_CHECK_GOTO(ret, error);
+
+ /* process children */
+ ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
+ LY_CHECK_GOTO(ret, error);
+
+ /* additional procedure for inner node */
+ ret = lyb_validate_node_inner(lybctx, snode, node);
+ LY_CHECK_GOTO(ret, error);
+
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lybctx->op_node = node;
+ }
+
+ /* register parsed list node */
+ lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ }
+
+ /* end the sibling */
+ ret = lyb_read_stop_siblings(lybctx->lybctx);
+ LY_CHECK_RET(ret);
+
+ return LY_SUCCESS;
+
+error:
+ lyd_free_meta_siblings(meta);
+ lyd_free_tree(node);
+ return ret;
+}
+
+/**
+ * @brief Parse a node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first_p is not.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[in,out] parsed Set of all successfully parsed nodes to add to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_node(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed)
+{
+ LY_ERR ret;
+ const struct lysc_node *snode;
+ const struct lys_module *mod;
+ enum lylyb_node_type lyb_type;
+ char *mod_name = NULL, mod_rev[LY_REV_SIZE];
+
+ /* read node type */
+ lyb_read_number(&lyb_type, sizeof lyb_type, 1, lybctx->lybctx);
+
+ switch (lyb_type) {
+ case LYB_NODE_TOP:
+ /* top-level, read module name */
+ LY_CHECK_GOTO(ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_opts, 0, &mod), cleanup);
+
+ /* read hash, find the schema node starting from mod */
+ LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, NULL, mod, &snode), cleanup);
+ break;
+ case LYB_NODE_CHILD:
+ case LYB_NODE_OPAQ:
+ /* read hash, find the schema node starting from parent schema, if any */
+ LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, parent ? parent->schema : NULL, NULL, &snode), cleanup);
+ break;
+ case LYB_NODE_EXT:
+ /* ext, read module name */
+ LY_CHECK_GOTO(ret = lyb_read_model(lybctx->lybctx, &mod_name, mod_rev, NULL), cleanup);
+
+ /* read schema node name, find the nexted ext schema node */
+ LY_CHECK_GOTO(ret = lyb_parse_schema_nested_ext(lybctx, parent, mod_name, &snode), cleanup);
+ break;
+ }
+
+ if (!snode) {
+ ret = lyb_parse_node_opaq(lybctx, parent, first_p, parsed);
+ } else if (snode->nodetype & LYS_LEAFLIST) {
+ ret = lyb_parse_node_leaflist(lybctx, parent, snode, first_p, parsed);
+ } else if (snode->nodetype == LYS_LIST) {
+ ret = lyb_parse_node_list(lybctx, parent, snode, first_p, parsed);
+ } else if (snode->nodetype & LYD_NODE_ANY) {
+ ret = lyb_parse_node_any(lybctx, parent, snode, first_p, parsed);
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ ret = lyb_parse_node_inner(lybctx, parent, snode, first_p, parsed);
+ } else {
+ ret = lyb_parse_node_leaf(lybctx, parent, snode, first_p, parsed);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ free(mod_name);
+ return ret;
+}
+
+/**
+ * @brief Parse siblings (@ref lyb_print_siblings()).
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first_p is not.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[out] parsed Set of all successfully parsed nodes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_siblings(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed)
+{
+ ly_bool top_level;
+
+ top_level = !LY_ARRAY_COUNT(lybctx->lybctx->siblings);
+
+ /* register a new siblings */
+ LY_CHECK_RET(lyb_read_start_siblings(lybctx->lybctx));
+
+ while (LYB_LAST_SIBLING(lybctx->lybctx).written) {
+ LY_CHECK_RET(lyb_parse_node(lybctx, parent, first_p, parsed));
+
+ if (top_level && !(lybctx->int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+
+ /* end the siblings */
+ LY_CHECK_RET(lyb_read_stop_siblings(lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse used YANG data models.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parse_options Flag with options for parsing.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_data_models(struct lylyb_ctx *lybctx, uint32_t parse_options)
+{
+ LY_ERR ret;
+ uint32_t count;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* read model count */
+ lyb_read_number(&count, sizeof count, 2, lybctx);
+
+ if (count) {
+ LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->models, count, LY_EMEM);
+
+ /* read modules */
+ for (u = 0; u < count; ++u) {
+ ret = lyb_parse_model(lybctx, parse_options, 1, &lybctx->models[u]);
+ LY_CHECK_RET(ret);
+ LY_ARRAY_INCREMENT(lybctx->models);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse LYB magic number.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_magic_number(struct lylyb_ctx *lybctx)
+{
+ char magic_byte = 0;
+
+ lyb_read((uint8_t *)&magic_byte, 1, lybctx);
+ if (magic_byte != 'l') {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid first magic number byte \"0x%02x\".", magic_byte);
+ return LY_EINVAL;
+ }
+
+ lyb_read((uint8_t *)&magic_byte, 1, lybctx);
+ if (magic_byte != 'y') {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid second magic number byte \"0x%02x\".", magic_byte);
+ return LY_EINVAL;
+ }
+
+ lyb_read((uint8_t *)&magic_byte, 1, lybctx);
+ if (magic_byte != 'b') {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid third magic number byte \"0x%02x\".", magic_byte);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse LYB header.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_header(struct lylyb_ctx *lybctx)
+{
+ uint8_t byte = 0;
+
+ /* version, future flags */
+ lyb_read((uint8_t *)&byte, sizeof byte, lybctx);
+
+ if ((byte & LYB_VERSION_MASK) != LYB_VERSION_NUM) {
+ LOGERR(lybctx->ctx, LY_EINVAL, "Invalid LYB format version \"0x%02x\", expected \"0x%02x\".",
+ byte & LYB_VERSION_MASK, LYB_VERSION_NUM);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parse_lyb(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
+ struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_lyb_ctx *lybctx;
+
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+ LY_CHECK_ARG_RET(ctx, !(parse_opts & LYD_PARSE_SUBTREE), LY_EINVAL);
+
+ if (subtree_sibling) {
+ *subtree_sibling = 0;
+ }
+
+ lybctx = calloc(1, sizeof *lybctx);
+ LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+ lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
+ LY_CHECK_ERR_GOTO(!lybctx->lybctx, LOGMEM(ctx); rc = LY_EMEM, cleanup);
+
+ lybctx->lybctx->in = in;
+ lybctx->lybctx->ctx = ctx;
+ lybctx->parse_opts = parse_opts;
+ lybctx->val_opts = val_opts;
+ lybctx->int_opts = int_opts;
+ lybctx->free = lyd_lyb_ctx_free;
+ lybctx->ext = ext;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lybctx->op_node), cleanup);
+
+ /* read magic number */
+ rc = lyb_parse_magic_number(lybctx->lybctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* read header */
+ rc = lyb_parse_header(lybctx->lybctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* read used models */
+ rc = lyb_parse_data_models(lybctx->lybctx, lybctx->parse_opts);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* read sibling(s) */
+ rc = lyb_parse_siblings(lybctx, parent, first_p, parsed);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lybctx->lybctx->in->current[0]) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lybctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* read the last zero, parsing finished */
+ ly_in_skip(lybctx->lybctx->in, 1);
+
+cleanup:
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lybctx->node_types.count && !lybctx->meta_types.count &&
+ !lybctx->node_when.count));
+
+ if (rc) {
+ lyd_lyb_ctx_free((struct lyd_ctx *)lybctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lybctx;
+ }
+ return rc;
+}
+
+LIBYANG_API_DEF int
+lyd_lyb_data_length(const char *data)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lylyb_ctx *lybctx;
+ uint32_t count, feat_count, len = 0, i, j;
+ uint8_t buf[LYB_SIZE_MAX];
+ uint8_t zero[LYB_SIZE_BYTES] = {0};
+
+ if (!data) {
+ return -1;
+ }
+
+ lybctx = calloc(1, sizeof *lybctx);
+ LY_CHECK_ERR_RET(!lybctx, LOGMEM(NULL), LY_EMEM);
+ ret = ly_in_new_memory(data, &lybctx->in);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* read magic number */
+ ret = lyb_parse_magic_number(lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* read header */
+ ret = lyb_parse_header(lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* read model count */
+ lyb_read_number(&count, sizeof count, 2, lybctx);
+
+ /* read all models */
+ for (i = 0; i < count; ++i) {
+ /* module name length */
+ lyb_read_number(&len, sizeof len, 2, lybctx);
+
+ /* model name */
+ lyb_read(buf, len, lybctx);
+
+ /* revision */
+ lyb_read(buf, 2, lybctx);
+
+ /* enabled feature count */
+ lyb_read_number(&feat_count, sizeof feat_count, 2, lybctx);
+
+ /* enabled features */
+ for (j = 0; j < feat_count; ++j) {
+ /* feature name length */
+ lyb_read_number(&len, sizeof len, 2, lybctx);
+
+ /* feature name */
+ lyb_read(buf, len, lybctx);
+ }
+ }
+
+ if (memcmp(zero, lybctx->in->current, LYB_SIZE_BYTES)) {
+ /* register a new sibling */
+ ret = lyb_read_start_siblings(lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* skip it */
+ lyb_skip_siblings(lybctx);
+
+ /* sibling finished */
+ ret = lyb_read_stop_siblings(lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ lyb_read(NULL, LYB_SIZE_BYTES, lybctx);
+ }
+
+ /* read the last zero, parsing finished */
+ ly_in_skip(lybctx->in, 1);
+
+cleanup:
+ count = lybctx->in->current - lybctx->in->start;
+
+ ly_in_free(lybctx->in, 0);
+ lylyb_ctx_free(lybctx);
+
+ return ret ? -1 : (int)count;
+}
diff --git a/src/parser_schema.h b/src/parser_schema.h
new file mode 100644
index 0000000..f8df16d
--- /dev/null
+++ b/src/parser_schema.h
@@ -0,0 +1,179 @@
+/**
+ * @file parser_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema parsers for libyang
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PARSER_SCHEMA_H_
+#define LY_PARSER_SCHEMA_H_
+
+#include "log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_in;
+struct lys_module;
+
+/**
+ * @page howtoSchemaParsers Parsing YANG Modules
+ *
+ * YANG module parsers allow to read YANG module from a specific format. libyang supports the following module formats:
+ *
+ * - YANG
+ *
+ * Basic YANG schemas format described in [RFC 6020](http://tools.ietf.org/html/rfc6020) and
+ * [RFC 7951](http://tools.ietf.org/html/rfc7951) (so both YANG 1.0 and YANG 1.1 versions are supported).
+ *
+ * - YIN
+ *
+ * Alternative XML-based format to YANG - YANG Independent Notation. The details can be found in
+ * [RFC 6020](http://tools.ietf.org/html/rfc6020#section-11) and
+ * [RFC 7951](http://tools.ietf.org/html/rfc7951#section-13).
+ *
+ * When the [context](@ref howtoContext) is created, it already contains the following YANG modules, which
+ * are implemented internally by libyang:
+ * - ietf-yang-metadata@2016-08-05
+ * - yang@2020-06-17
+ * - ietf-inet-types@2013-07-15
+ * - ietf-yang-types@2013-07-15
+ * - ietf-datastores@2018-02-14
+ * - ietf-yang-library@2019-01-04
+ *
+ * The `yang` module is the libyang's internal module to provide namespace and definitions of for various YANG
+ * attributes described in [RFC 7951](https://tools.ietf.org/html/rfc6243) (such as `insert` attribute for
+ * edit-config's data).
+ *
+ * Other modules can be added to the context manually with the functions listed below. Besides them,
+ * it is also possible to use ::ly_ctx_load_module() which tries to find the required module automatically - using
+ * ::ly_module_imp_clb or automatic search in working directory and in the context's search directories. For details, see
+ * [how the context works](@ref howtoContext).
+ *
+ * YANG modules are loaded in two steps. First, the input YANG/YIN data are parsed into \b lysp_* structures that reflect
+ * the structure of the input module and submodule(s). Mostly just syntax checks are done, no reference or type checking is
+ * performed in this step. If the module is supposed to be implemented, not just imported by another module, the second step
+ * is to compile it. The compiled tree may significantly differ from the source (parsed) tree structure. All the references
+ * are resolved, groupings are instantiated, types are resolved (and compiled by joining all the relevant restrictions
+ * when derived from another types) and many other syntactical checks are done.
+ *
+ * There is the main parsing function ::lys_parse() working with the libyang [input handler](@ref howtoInput). However,
+ * to simplify some of the use-cases, it is also possible to use other functions accepting input data from various sources.
+ *
+ * Functions List
+ * --------------
+ * - ::lys_parse()
+ * - ::lys_parse_mem()
+ * - ::lys_parse_fd()
+ * - ::lys_parse_path()
+ *
+ * - ::lys_search_localfile()
+ * - ::ly_ctx_set_module_imp_clb()
+ * - ::ly_ctx_load_module()
+ */
+
+/**
+ * @addtogroup schematree
+ * @{
+ */
+
+/**
+ * @brief Schema input formats accepted by libyang [parser functions](@ref howtoSchemaParsers).
+ */
+typedef enum {
+ LYS_IN_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
+ LYS_IN_YANG = 1, /**< YANG schema input format */
+ LYS_IN_YIN = 3 /**< YIN schema input format */
+} LYS_INFORMAT;
+
+/**
+ * @brief Load a schema into the specified context.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] in The input handle to provide the dumped data model in the specified format.
+ * @param[in] format Format of the schema to parse. Can be 0 to try to detect format from the input handler.
+ * @param[in] features Array of features to enable ended with NULL. If NULL, no features are enabled.
+ * @param[out] module Optional parsed module.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features,
+ struct lys_module **module);
+
+/**
+ * @brief Load a schema into the specified context.
+ *
+ * This function is considered for a simple use, if you have a complex use-case,
+ * consider use of ::lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] data The string containing the dumped data model in the specified format.
+ * @param[in] format Format of the schema to parse.
+ * @param[out] module Optional parsed module.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct lys_module **module);
+
+/**
+ * @brief Read a schema from file descriptor into the specified context.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * This function is considered for a simple use, if you have a complex use-case,
+ * consider use of ::lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
+ * in the specified format.
+ * @param[in] format Format of the schema to parse.
+ * @param[out] module Optional parsed module.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_module **module);
+
+/**
+ * @brief Load a schema into the specified context from a file.
+ *
+ * This function is considered for a simple use, if you have a complex use-case,
+ * consider use of ::lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] path Path to the file with the model in the specified format.
+ * @param[in] format Format of the schema to parse.
+ * @param[out] module Optional parsed module.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, struct lys_module **module);
+
+/**
+ * @brief Search for the schema file in the specified searchpaths.
+ *
+ * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
+ * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
+ * result of the ::ly_ctx_get_searchdirs().
+ * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
+ * @param[in] name Name of the schema to find.
+ * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
+ * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
+ * complying the provided restriction is found, NULL is set.
+ * @param[out] format Optional output variable containing expected format of the schema document according to the
+ * file suffix.
+ * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
+ */
+LIBYANG_API_DECL LY_ERR lys_search_localfile(const char * const *searchpaths, ly_bool cwd, const char *name, const char *revision,
+ char **localfile, LYS_INFORMAT *format);
+
+/** @} schematree */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PARSER_SCHEMA_H_ */
diff --git a/src/parser_xml.c b/src/parser_xml.c
new file mode 100644
index 0000000..5a929da
--- /dev/null
+++ b/src/parser_xml.c
@@ -0,0 +1,1816 @@
+/**
+ * @file parser_xml.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief XML data parser for libyang
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "plugins_exts.h"
+#include "plugins_internal.h"
+#include "schema_compile_node.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "validation.h"
+#include "xml.h"
+
+void
+lyd_xml_ctx_free(struct lyd_ctx *lydctx)
+{
+ struct lyd_xml_ctx *ctx = (struct lyd_xml_ctx *)lydctx;
+
+ lyd_ctx_free(lydctx);
+ lyxml_ctx_free(ctx->xmlctx);
+ free(ctx);
+}
+
+/**
+ * @brief Parse and create XML metadata.
+ *
+ * @param[in] lydctx XML data parser context.
+ * @param[in] sparent Schema node of the parent.
+ * @param[out] meta List of created metadata instances.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_metadata(struct lyd_xml_ctx *lydctx, const struct lysc_node *sparent, struct lyd_meta **meta)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lyxml_ns *ns;
+ struct lys_module *mod;
+ const char *name;
+ size_t name_len;
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool filter_attrs = 0;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+
+ *meta = NULL;
+
+ /* check for NETCONF filter unqualified attributes */
+ if (!strcmp(sparent->module->name, "notifications")) {
+ /* ancient module that does not even use the extension */
+ filter_attrs = 1;
+ } else {
+ LY_ARRAY_FOR(sparent->exts, u) {
+ if (!strcmp(sparent->exts[u].def->name, "get-filter-element-attributes") &&
+ !strcmp(sparent->exts[u].def->module->name, "ietf-netconf")) {
+ filter_attrs = 1;
+ break;
+ }
+ }
+ }
+
+ while (xmlctx->status == LYXML_ATTRIBUTE) {
+ if (!xmlctx->prefix_len) {
+ /* in XML all attributes must be prefixed except NETCONF filter ones marked by an extension */
+ if (filter_attrs && (!ly_strncmp("type", xmlctx->name, xmlctx->name_len) ||
+ !ly_strncmp("select", xmlctx->name, xmlctx->name_len))) {
+ mod = ly_ctx_get_module_implemented(xmlctx->ctx, "ietf-netconf");
+ if (!mod) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
+ "Missing (or not implemented) YANG module \"ietf-netconf\" for special filter attributes.");
+ ret = LY_ENOTFOUND;
+ goto cleanup;
+ }
+ goto create_meta;
+ }
+
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
+ (int)xmlctx->name_len, xmlctx->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* skip attr */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ assert(xmlctx->status == LYXML_ATTR_CONTENT);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ continue;
+ }
+
+ /* get namespace of the attribute to find its annotation definition */
+ ns = lyxml_ns_get(&xmlctx->ns, xmlctx->prefix, xmlctx->prefix_len);
+ if (!ns) {
+ /* unknown namespace, XML error */
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)xmlctx->prefix_len, xmlctx->prefix);
+ ret = LY_ENOTFOUND;
+ goto cleanup;
+ }
+
+ /* get the module with metadata definition */
+ mod = ly_ctx_get_module_implemented_ns(xmlctx->ctx, ns->uri);
+ if (!mod) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
+ "Unknown (or not implemented) YANG module with namespace \"%s\" for metadata \"%.*s%s%.*s\".",
+ ns->uri, (int)xmlctx->prefix_len, xmlctx->prefix, xmlctx->prefix_len ? ":" : "",
+ (int)xmlctx->name_len, xmlctx->name);
+ ret = LY_ENOTFOUND;
+ goto cleanup;
+ }
+
+ /* skip attr */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ assert(xmlctx->status == LYXML_ATTR_CONTENT);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ continue;
+ }
+
+create_meta:
+ /* remember meta name and get its content */
+ name = xmlctx->name;
+ name_len = xmlctx->name_len;
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ assert(xmlctx->status == LYXML_ATTR_CONTENT);
+
+ /* create metadata */
+ ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, NULL, meta, mod, name, name_len, xmlctx->value,
+ xmlctx->value_len, &xmlctx->dynamic, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, sparent);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* next attribute */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ }
+
+cleanup:
+ if (ret) {
+ lyd_free_meta_siblings(*meta);
+ *meta = NULL;
+ }
+ return ret;
+}
+
+static LY_ERR
+lydxml_attrs(struct lyxml_ctx *xmlctx, struct lyd_attr **attr)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lyxml_ns *ns;
+ void *val_prefix_data;
+ LY_VALUE_FORMAT format;
+ struct lyd_attr *attr2;
+ const char *name, *prefix;
+ size_t name_len, prefix_len;
+
+ assert(attr);
+ *attr = NULL;
+
+ while (xmlctx->status == LYXML_ATTRIBUTE) {
+ if (*attr) {
+ attr2 = *attr;
+ } else {
+ attr2 = NULL;
+ }
+
+ /* remember attr prefix, name, and get its content */
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ name = xmlctx->name;
+ name_len = xmlctx->name_len;
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ assert(xmlctx->status == LYXML_ATTR_CONTENT);
+
+ /* handle special "xml" attribute prefix */
+ if ((prefix_len == 3) && !strncmp(prefix, "xml", 3)) {
+ name = prefix;
+ name_len += 1 + prefix_len;
+ prefix = NULL;
+ prefix_len = 0;
+ }
+
+ /* find namespace of the attribute, if any */
+ ns = NULL;
+ if (prefix_len) {
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ if (!ns) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)prefix_len, prefix);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* get value prefixes */
+ val_prefix_data = NULL;
+ LY_CHECK_GOTO(ret = ly_store_prefix_data(xmlctx->ctx, xmlctx->value, xmlctx->value_len, LY_VALUE_XML,
+ &xmlctx->ns, &format, &val_prefix_data), cleanup);
+
+ /* attr2 is always changed to the created attribute */
+ ret = lyd_create_attr(NULL, &attr2, xmlctx->ctx, name, name_len, prefix, prefix_len, ns ? ns->uri : NULL,
+ ns ? strlen(ns->uri) : 0, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, format, val_prefix_data,
+ LYD_HINT_DATA);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!*attr) {
+ *attr = attr2;
+ }
+
+ /* next attribute */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ }
+
+cleanup:
+ if (ret) {
+ lyd_free_attr_siblings(xmlctx->ctx, *attr);
+ *attr = NULL;
+ }
+ return ret;
+}
+
+static LY_ERR
+lydxml_check_list(struct lyxml_ctx *xmlctx, const struct lysc_node *list)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ enum LYXML_PARSER_STATUS next;
+ struct ly_set key_set = {0};
+ const struct lysc_node *snode;
+ uint32_t i, parents_count;
+
+ assert(list && (list->nodetype == LYS_LIST));
+
+ /* get all keys into a set (keys do not have if-features or anything) */
+ snode = NULL;
+ while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
+ ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* remember parent count */
+ parents_count = xmlctx->elements.count;
+
+ while (xmlctx->status == LYXML_ELEMENT) {
+ /* find key definition */
+ for (i = 0; i < key_set.count; ++i) {
+ snode = (const struct lysc_node *)key_set.objs[i];
+ if (!ly_strncmp(snode->name, xmlctx->name, xmlctx->name_len)) {
+ break;
+ }
+ }
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* skip attributes */
+ while (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ assert(xmlctx->status == LYXML_ATTR_CONTENT);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ }
+
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (i < key_set.count) {
+ /* validate the value */
+ r = lys_value_validate(NULL, snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns);
+ if (!r) {
+ /* key with a valid value, remove from the set */
+ ly_set_rm_index(&key_set, i, NULL);
+ }
+ }
+
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* skip any children, resursively */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ while (parents_count < xmlctx->elements.count) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ }
+ assert(xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ }
+
+ /* parser next, but do not parse closing element of the list because it would remove its namespaces */
+ assert(xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_GOTO(ret = lyxml_ctx_peek(xmlctx, &next), cleanup);
+ if (next != LYXML_ELEM_CLOSE) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ }
+ }
+
+ if (key_set.count) {
+ /* some keys are missing/did not validate */
+ ret = LY_ENOT;
+ }
+
+cleanup:
+ ly_set_erase(&key_set, NULL);
+ return ret;
+}
+
+/**
+ * @brief Skip an element with all its descendants.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_data_skip(struct lyxml_ctx *xmlctx)
+{
+ uint32_t parents_count;
+
+ /* remember current number of parents */
+ parents_count = xmlctx->elements.count;
+ assert(parents_count);
+
+ /* skip after the content */
+ while (xmlctx->status != LYXML_ELEM_CONTENT) {
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+ }
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ /* skip all children elements, recursively, if any */
+ while (parents_count <= xmlctx->elements.count) {
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+ }
+
+ /* close element */
+ assert(xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that the current element can be parsed as a data node.
+ *
+ * @param[in] lydctx XML data parser context.
+ * @param[in,out] snode Found schema node, set to NULL if data node cannot be created.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx, pxmlctx;
+
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* only checks specific to opaque nodes */
+ return LY_SUCCESS;
+ }
+
+ if (!((*snode)->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER))) {
+ /* nothing to check */
+ return LY_SUCCESS;
+ }
+
+ assert(xmlctx->elements.count);
+
+ /* backup parser */
+ LY_CHECK_RET(lyxml_ctx_backup(xmlctx, &pxmlctx));
+
+ /* skip attributes */
+ while (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
+ }
+
+ if ((*snode)->nodetype & LYD_NODE_TERM) {
+ /* value may not be valid in which case we parse it as an opaque node */
+ if (lys_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns)) {
+ LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, xmlctx->value_len,
+ xmlctx->value);
+ *snode = NULL;
+ }
+ } else if ((*snode)->nodetype == LYS_LIST) {
+ /* skip content */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
+
+ if (lydxml_check_list(xmlctx, *snode)) {
+ /* invalid list, parse as opaque if it missing/has invalid some keys */
+ LOGVRB("Parsing opaque list node \"%s\" with missing/invalid keys.", (*snode)->name);
+ *snode = NULL;
+ }
+ } else {
+ /* if there is a non-WS value, it cannot be parsed as an inner node */
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (!xmlctx->ws_only) {
+ *snode = NULL;
+ }
+ }
+
+restore:
+ /* restore parser */
+ lyxml_ctx_restore(xmlctx, &pxmlctx);
+ return ret;
+}
+
+/**
+ * @brief Get sensible data hints for an opaque node.
+ *
+ * @param[in] name Node name.
+ * @param[in] name_len Length of @p name.
+ * @param[in] value Node value.
+ * @param[in] value_len Length of @p value.
+ * @param[in] first Node first sibling.
+ * @param[in] ns Node module namespace, NULL for no namespace.
+ * @param[out] hints Data hints to use.
+ * @param[out] anchor Anchor to insert after in case of a list.
+ */
+static void
+lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size_t value_len, struct lyd_node *first,
+ const char *ns, uint32_t *hints, struct lyd_node **anchor)
+{
+ struct lyd_node_opaq *opaq;
+ char *ptr;
+ long num;
+
+ *hints = 0;
+ *anchor = NULL;
+
+ if (!value_len) {
+ /* no value */
+ *hints |= LYD_VALHINT_EMPTY;
+ } else if (!strncmp(value, "true", value_len) || !strncmp(value, "false", value_len)) {
+ /* boolean value */
+ *hints |= LYD_VALHINT_BOOLEAN;
+ } else {
+ num = strtol(value, &ptr, 10);
+ if ((unsigned)(ptr - value) == value_len) {
+ /* number value */
+ *hints |= LYD_VALHINT_DECNUM;
+ if ((num < INT32_MIN) || (num > UINT32_MAX)) {
+ /* large number */
+ *hints |= LYD_VALHINT_NUM64;
+ }
+ } else {
+ /* string value */
+ *hints |= LYD_VALHINT_STRING;
+ }
+ }
+
+ if (!first) {
+ return;
+ }
+
+ /* search backwards to find the last instance */
+ do {
+ first = first->prev;
+ if (first->schema) {
+ continue;
+ }
+
+ opaq = (struct lyd_node_opaq *)first;
+ assert(opaq->format == LY_VALUE_XML);
+ if (!ly_strncmp(opaq->name.name, name, name_len) &&
+ ((ns && !strcmp(opaq->name.module_ns, ns)) || (!ns && !opaq->name.module_ns))) {
+ if (opaq->value && opaq->value[0]) {
+ /* leaf-list nodes */
+ opaq->hints |= LYD_NODEHINT_LEAFLIST;
+ *hints |= LYD_NODEHINT_LEAFLIST;
+ } else {
+ /* list nodes */
+ opaq->hints |= LYD_NODEHINT_LIST;
+ *hints |= LYD_NODEHINT_LIST;
+ }
+ *anchor = first;
+ break;
+ }
+ } while (first->prev->next);
+}
+
+/**
+ * @brief Get schema node for the current element.
+ *
+ * @param[in] lydctx XML data parser context.
+ * @param[in] parent Parsed parent data node, if any.
+ * @param[in] prefix Element prefix, if any.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] snode Found schema node, NULL if no suitable was found.
+ * @param[out] ext Extension instance that provided @p snode, if any.
+ * @return LY_SUCCESS on success;
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lydxml_subtree_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
+ const char *name, size_t name_len, const struct lysc_node **snode, struct lysc_ext_instance **ext)
+{
+ LY_ERR r;
+ struct lyxml_ctx *xmlctx;
+ const struct ly_ctx *ctx;
+ const struct lyxml_ns *ns;
+ struct lys_module *mod;
+ uint32_t getnext_opts;
+
+ xmlctx = lydctx->xmlctx;
+ ctx = xmlctx->ctx;
+ getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
+
+ *snode = NULL;
+ *ext = NULL;
+
+ /* get current namespace */
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ if (!ns) {
+ if (lydctx->int_opts & LYD_INTOPT_ANY) {
+ goto unknown_module;
+ }
+
+ if (prefix_len) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)prefix_len, prefix);
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Missing XML namespace.");
+ }
+ return LY_EVALID;
+ }
+
+ /* get the element module, use parent context if possible because of extensions */
+ mod = ly_ctx_get_module_implemented_ns(parent ? LYD_CTX(parent) : ctx, ns->uri);
+ if (!mod) {
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name, name_len,
+ snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
+ return r;
+ }
+
+unknown_module:
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+ }
+
+ /* get the schema node */
+ if (mod) {
+ if (!parent && lydctx->ext) {
+ *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+ } else {
+ *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+ }
+ if (!*snode) {
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
+ name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
+ return r;
+ }
+
+ /* unknown data node */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (parent) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
+ (int)name_len, name, LYD_NAME(parent));
+ } else if (lydctx->ext) {
+ if (lydctx->ext->argument) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
+ (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
+ (int)name_len, name, lydctx->ext->def->name);
+ }
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+ (int)name_len, name, mod->name);
+ }
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+ } else {
+ /* check that schema node is valid and can be used */
+ LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
+ LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse XML subtree.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
+ * @param[in,out] first_p Pointer to the first (@p parent or top-level) child. In case there were already some siblings,
+ * this may point to a previously existing node.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *prefix, *name, *ns_uri;
+ size_t prefix_len, name_len;
+ struct lyxml_ctx *xmlctx;
+ const struct ly_ctx *ctx;
+ const struct lyxml_ns *ns;
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lysc_node *snode;
+ struct lysc_ext_instance *ext;
+ uint32_t prev_parse_opts, orig_parse_opts, prev_int_opts, hints;
+ struct lyd_node *node = NULL, *anchor, *insert_anchor = NULL;
+ void *val_prefix_data = NULL;
+ LY_VALUE_FORMAT format;
+ ly_bool parse_subtree;
+ char *val;
+
+ assert(parent || first_p);
+
+ xmlctx = lydctx->xmlctx;
+ ctx = xmlctx->ctx;
+
+ parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
+ /* all descendants should be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+ orig_parse_opts = lydctx->parse_opts;
+
+ assert(xmlctx->status == LYXML_ELEMENT);
+
+ /* remember element prefix and name */
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ name = xmlctx->name;
+ name_len = xmlctx->name_len;
+
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+
+ /* get the schema node */
+ LY_CHECK_GOTO(ret = lydxml_subtree_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext), error);
+
+ if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ LOGVRB("Skipping parsing of unknown node \"%.*s\".", name_len, name);
+
+ /* skip element with children */
+ LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
+ return LY_SUCCESS;
+ }
+
+ /* create metadata/attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ if (snode) {
+ ret = lydxml_metadata(lydctx, snode, &meta);
+ LY_CHECK_GOTO(ret, error);
+ } else {
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+ ret = lydxml_attrs(xmlctx, &attr);
+ LY_CHECK_GOTO(ret, error);
+ }
+ }
+
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (!snode) {
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+
+ if (xmlctx->ws_only) {
+ /* ignore WS-only value */
+ if (xmlctx->dynamic) {
+ free((char *)xmlctx->value);
+ }
+ xmlctx->dynamic = 0;
+ xmlctx->value = "";
+ xmlctx->value_len = 0;
+ format = LY_VALUE_XML;
+ } else {
+ /* get value prefixes */
+ ret = ly_store_prefix_data(xmlctx->ctx, xmlctx->value, xmlctx->value_len, LY_VALUE_XML,
+ &xmlctx->ns, &format, &val_prefix_data);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ /* get NS again, it may have been backed up and restored */
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ ns_uri = ns ? ns->uri : NULL;
+
+ /* get best-effort node hints */
+ lydxml_get_hints_opaq(name, name_len, xmlctx->value, xmlctx->value_len, parent ? lyd_child(parent) : *first_p,
+ ns_uri, &hints, &insert_anchor);
+
+ /* create node */
+ ret = lyd_create_opaq(ctx, name, name_len, prefix, prefix_len, ns_uri, ns_uri ? strlen(ns_uri) : 0,
+ xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, format, val_prefix_data, hints, &node);
+ LY_CHECK_GOTO(ret, error);
+
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+
+ /* process children */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
+ LY_CHECK_GOTO(ret, error);
+ }
+ } else if (snode->nodetype & LYD_NODE_TERM) {
+ /* create node */
+ LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len,
+ &xmlctx->dynamic, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, &node), error);
+ LOG_LOCSET(snode, node, NULL, NULL);
+
+ if (parent && (node->schema->flags & LYS_KEY)) {
+ /* check the key order, the anchor must never be a key */
+ anchor = lyd_insert_get_next_anchor(lyd_child(parent), node);
+ if (anchor && anchor->schema && (anchor->schema->flags & LYS_KEY)) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", node->schema->name);
+ ret = LY_EVALID;
+ goto error;
+ } else {
+ LOGWRN(ctx, "Invalid position of the key \"%s\" in a list.", node->schema->name);
+ }
+ }
+ }
+
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+
+ /* no children expected */
+ if (xmlctx->status == LYXML_ELEMENT) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
+ (int)xmlctx->name_len, xmlctx->name, snode->name);
+ ret = LY_EVALID;
+ goto error;
+ }
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ if (!xmlctx->ws_only) {
+ /* value in inner node */
+ LOGVAL(ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
+ (int)xmlctx->value_len, xmlctx->value, snode->name);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ /* create node */
+ ret = lyd_create_inner(snode, &node);
+ LY_CHECK_GOTO(ret, error);
+
+ LOG_LOCSET(snode, node, NULL, NULL);
+
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+
+ prev_parse_opts = lydctx->parse_opts;
+ if (ext) {
+ /* only parse these extension data and validate afterwards */
+ lydctx->parse_opts |= LYD_PARSE_ONLY;
+ }
+
+ /* process children */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ /* restore options */
+ lydctx->parse_opts = prev_parse_opts;
+
+ if (snode->nodetype == LYS_LIST) {
+ /* check all keys exist */
+ LY_CHECK_GOTO(ret = lyd_parse_check_keys(node), error);
+ }
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* new node validation, autodelete CANNOT occur, all nodes are new */
+ ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
+ LY_CHECK_GOTO(ret, error);
+
+ /* add any missing default children */
+ ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
+ &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lydctx->op_node = node;
+ }
+ } else if (snode->nodetype & LYD_NODE_ANY) {
+ if ((snode->nodetype == LYS_ANYDATA) && !xmlctx->ws_only) {
+ /* value in anydata node, we expect a tree */
+ LOGVAL(ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an anydata node \"%s\" found.",
+ (int)xmlctx->value_len < 20 ? xmlctx->value_len : 20, xmlctx->value, snode->name);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ if (!xmlctx->ws_only) {
+ /* use an arbitrary text value for anyxml */
+ val = strndup(xmlctx->value, xmlctx->value_len);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(xmlctx->ctx); ret = LY_EMEM, error);
+
+ /* parser next */
+ LY_CHECK_ERR_GOTO(ret = lyxml_ctx_next(xmlctx), free(val), error);
+
+ /* create node */
+ ret = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, &node);
+ LY_CHECK_ERR_GOTO(ret, free(val), error);
+ } else {
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+
+ /* update options so that generic data can be parsed */
+ prev_parse_opts = lydctx->parse_opts;
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
+ prev_int_opts = lydctx->int_opts;
+ lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+
+ /* parse any data tree */
+ anchor = NULL;
+ while (xmlctx->status == LYXML_ELEMENT) {
+ ret = lydxml_subtree_r(lydctx, NULL, &anchor, NULL);
+ if (ret) {
+ lyd_free_siblings(anchor);
+ break;
+ }
+ }
+
+ /* restore options */
+ lydctx->parse_opts = prev_parse_opts;
+ lydctx->int_opts = prev_int_opts;
+
+ LY_CHECK_GOTO(ret, error);
+
+ /* create node */
+ ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, 1, &node);
+ LY_CHECK_GOTO(ret, error);
+ }
+ }
+ assert(node);
+
+ if (snode) {
+ /* add/correct flags */
+ LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext), error);
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_GOTO(ret = lyd_validate_node_ext(node, &lydctx->ext_node), error);
+ }
+ }
+
+ /* parser next */
+ assert(xmlctx->status == LYXML_ELEM_CLOSE);
+ if (!parse_subtree) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ }
+
+ /* add metadata/attributes */
+ if (snode) {
+ lyd_insert_meta(node, meta, 0);
+ } else {
+ lyd_insert_attr(node, attr);
+ }
+
+ /* insert, keep first pointer correct */
+ if (insert_anchor) {
+ lyd_insert_after(insert_anchor, node);
+ } else if (ext) {
+ LY_CHECK_GOTO(ret = lyplg_ext_insert(parent, node), error);
+ } else {
+ lyd_insert_node(parent, first_p, node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+ }
+ while (!parent && (*first_p)->prev->next) {
+ *first_p = (*first_p)->prev;
+ }
+
+ /* rememeber a successfully parsed node */
+ if (parsed) {
+ ly_set_add(parsed, node, 1, NULL);
+ }
+
+ lydctx->parse_opts = orig_parse_opts;
+ LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
+ return LY_SUCCESS;
+
+error:
+ lydctx->parse_opts = orig_parse_opts;
+ LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
+ lyd_free_meta_siblings(meta);
+ lyd_free_attr_siblings(ctx, attr);
+ lyd_free_tree(node);
+ return ret;
+}
+
+/**
+ * @brief Parse a specific XML element into an opaque node.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[in] name Name of the element.
+ * @param[in] uri URI of the element.
+ * @param[in] value Whether a value is expected in the element.
+ * @param[out] evnp Parsed envelope (opaque node).
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the specified element did not match.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, ly_bool value, struct lyd_node **envp)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const struct lyxml_ns *ns;
+ struct lyd_attr *attr = NULL;
+ const char *prefix;
+ size_t prefix_len;
+
+ assert(xmlctx->status == LYXML_ELEMENT);
+ if (ly_strncmp(name, xmlctx->name, xmlctx->name_len)) {
+ /* not the expected element */
+ return LY_ENOT;
+ }
+
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ if (!ns) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)prefix_len, prefix);
+ return LY_EVALID;
+ } else if (strcmp(ns->uri, uri)) {
+ /* different namespace */
+ return LY_ENOT;
+ }
+
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ /* create attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
+ }
+
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (!value && !xmlctx->ws_only) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected value \"%.*s\" in the \"%s\" element.",
+ (int)xmlctx->value_len, xmlctx->value, name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* create node */
+ rc = lyd_create_opaq(xmlctx->ctx, name, strlen(name), prefix, prefix_len, uri, strlen(uri), xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, 0, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* assign atributes */
+ ((struct lyd_node_opaq *)(*envp))->attr = attr;
+ attr = NULL;
+
+ /* parser next element */
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+cleanup:
+ lyd_free_attr_siblings(xmlctx->ctx, attr);
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+LY_ERR
+lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
+ struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_xml_ctx *lydctx;
+ ly_bool parsed_data_nodes = 0;
+ enum LYXML_PARSER_STATUS status;
+
+ assert(ctx && in && lydctx_p);
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+ /* init context */
+ lydctx = calloc(1, sizeof *lydctx);
+ LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_GOTO(rc = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
+ lydctx->parse_opts = parse_opts;
+ lydctx->val_opts = val_opts;
+ lydctx->int_opts = int_opts;
+ lydctx->free = lyd_xml_ctx_free;
+ lydctx->ext = ext;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* parse XML data */
+ while (lydctx->xmlctx->status == LYXML_ELEMENT) {
+ LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, parent, first_p, parsed), cleanup);
+ parsed_data_nodes = 1;
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+
+ /* check final state */
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (!parsed_data_nodes) {
+ /* no data nodes were parsed */
+ lydctx->op_node = NULL;
+ }
+
+ if (parse_opts & LYD_PARSE_SUBTREE) {
+ /* check for a sibling element */
+ assert(subtree_sibling);
+ if (!lyxml_ctx_peek(lydctx->xmlctx, &status) && (status == LYXML_ELEMENT)) {
+ *subtree_sibling = 1;
+ } else {
+ *subtree_sibling = 0;
+ }
+ }
+
+cleanup:
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
+ if (rc) {
+ lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the XML context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyxml_ctx_free(lydctx->xmlctx);
+ lydctx->xmlctx = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of a NETCONF rpc message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_rpc(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child;
+
+ assert(envp && !*envp);
+
+ if (xmlctx->status != LYXML_ELEMENT) {
+ /* nothing to parse */
+ assert(xmlctx->status == LYXML_END);
+ goto cleanup;
+ }
+
+ /* parse "rpc" */
+ r = lydxml_envelope(xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* parse "action", if any */
+ r = lydxml_envelope(xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", 0, &child);
+ if (r == LY_SUCCESS) {
+ /* insert */
+ lyd_insert_node(*envp, NULL, child, 0);
+
+ /* NETCONF action */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_ACTION;
+ *close_elem = 2;
+ } else if (r == LY_ENOT) {
+ /* NETCONF RPC */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_RPC;
+ *close_elem = 1;
+ } else {
+ rc = r;
+ goto cleanup;
+ }
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Validate eventTime date-and-time value.
+ *
+ * @param[in] node Opaque eventTime node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_eventtime_validate(const struct lyd_node *node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
+ struct lysc_ctx cctx;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_err_item *err = NULL;
+ struct lysp_type *type_p = NULL;
+ struct lysc_pattern **patterns = NULL;
+ const char *value;
+
+ LYSC_CTX_INIT_CTX(cctx, ctx);
+
+ /* get date-and-time parsed type */
+ mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
+ assert(mod);
+ LY_ARRAY_FOR(mod->parsed->typedefs, u) {
+ if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
+ type_p = &mod->parsed->typedefs[u].type;
+ break;
+ }
+ }
+ assert(type_p);
+
+ /* compile patterns */
+ assert(type_p->patterns);
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
+
+ /* validate */
+ value = lyd_get_value(node);
+ rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
+
+cleanup:
+ FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
+ if (rc && err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of a NETCONF notification message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_notif(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child;
+
+ assert(envp && !*envp);
+
+ if (xmlctx->status != LYXML_ELEMENT) {
+ /* nothing to parse */
+ assert(xmlctx->status == LYXML_END);
+ goto cleanup;
+ }
+
+ /* parse "notification" */
+ r = lydxml_envelope(xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* parse "eventTime" */
+ r = lydxml_envelope(xmlctx, "eventTime", "urn:ietf:params:xml:ns:netconf:notification:1.0", 1, &child);
+ if (r == LY_ENOT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"eventTime\".",
+ (int)xmlctx->name_len, xmlctx->name);
+ r = LY_EVALID;
+ }
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* insert */
+ lyd_insert_node(*envp, NULL, child, 0);
+
+ /* validate value */
+ r = lydxml_env_netconf_eventtime_validate(child);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* finish child parsing */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"eventTime\".",
+ (int)xmlctx->name_len, xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* NETCONF notification */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
+ *close_elem = 1;
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse an XML element as an opaque node subtree.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @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 *child = NULL;
+ const char *name, *prefix;
+ size_t name_len, prefix_len;
+
+ 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) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)prefix_len, prefix);
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ /* create attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
+ }
+
+ /* create node */
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, 0, &child);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* assign atributes */
+ ((struct lyd_node_opaq *)child)->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, child);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ /* insert */
+ lyd_insert_node(parent, NULL, child, 1);
+
+cleanup:
+ lyd_free_attr_siblings(xmlctx->ctx, attr);
+ if (rc) {
+ lyd_free_tree(child);
+ }
+ 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, 1);
+ }
+
+ 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, 1);
+ }
+
+ 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[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child = NULL;
+ const char *parsed_elem = NULL;
+
+ assert(envp && !*envp);
+
+ if (xmlctx->status != LYXML_ELEMENT) {
+ /* nothing to parse */
+ assert(xmlctx->status == LYXML_END);
+ goto cleanup;
+ }
+
+ /* parse "rpc-reply" */
+ r = lydxml_envelope(xmlctx, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* there must be some child */
+ if (xmlctx->status == LYXML_ELEM_CLOSE) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"rpc-reply\".");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* try to parse "ok" */
+ r = lydxml_envelope(xmlctx, "ok", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
+ if (r == LY_SUCCESS) {
+ /* insert */
+ lyd_insert_node(*envp, NULL, child, 1);
+
+ /* finish child parsing */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"ok\".",
+ (int)xmlctx->name_len, xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* success */
+ parsed_elem = "ok";
+ goto finish;
+ } else if (r != LY_ENOT) {
+ rc = r;
+ goto cleanup;
+ }
+
+ /* 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, 1);
+
+ /* 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);
+
+ parsed_elem = "rpc-error";
+ }
+
+finish:
+ if (parsed_elem) {
+ /* NETCONF rpc-reply with no data */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected sibling element \"%.*s\" of \"%s\".",
+ (int)xmlctx->name_len, xmlctx->name, parsed_elem);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* NETCONF rpc-reply */
+ *int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ *close_elem = 1;
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+LY_ERR
+lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_xml_ctx *lydctx;
+ uint32_t i, int_opts = 0, close_elem = 0;
+ ly_bool parsed_data_nodes = 0;
+
+ assert(ctx && in && lydctx_p);
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+ assert((data_type == LYD_TYPE_RPC_NETCONF) || (data_type == LYD_TYPE_NOTIF_NETCONF) ||
+ (data_type == LYD_TYPE_REPLY_NETCONF));
+ assert(!(parse_opts & LYD_PARSE_SUBTREE));
+
+ /* init context */
+ lydctx = calloc(1, sizeof *lydctx);
+ LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_GOTO(rc = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
+ lydctx->parse_opts = parse_opts;
+ lydctx->val_opts = val_opts;
+ lydctx->free = lyd_xml_ctx_free;
+ lydctx->ext = ext;
+
+ switch (data_type) {
+ case LYD_TYPE_RPC_NETCONF:
+ assert(!parent);
+ rc = lydxml_env_netconf_rpc(lydctx->xmlctx, envp, &int_opts, &close_elem);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <rpc> envelope or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ break;
+ case LYD_TYPE_NOTIF_NETCONF:
+ assert(!parent);
+ rc = lydxml_env_netconf_notif(lydctx->xmlctx, envp, &int_opts, &close_elem);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <notification> envelope or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ assert(parent);
+ rc = lydxml_env_netconf_reply(lydctx->xmlctx, envp, &int_opts, &close_elem);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <rpc-reply> envelope or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ break;
+ default:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ lydctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* parse XML data */
+ while (lydctx->xmlctx->status == LYXML_ELEMENT) {
+ LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, parent, first_p, parsed), cleanup);
+ parsed_data_nodes = 1;
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+
+ /* close all opened elements */
+ for (i = 0; i < close_elem; ++i) {
+ if (lydctx->xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(lydctx->xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(lydctx->xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\".",
+ (int)lydctx->xmlctx->name_len, lydctx->xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(lydctx->xmlctx), cleanup);
+ }
+
+ /* check final state */
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (!parsed_data_nodes) {
+ /* no data nodes were parsed */
+ lydctx->op_node = NULL;
+ }
+
+cleanup:
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
+ if (rc) {
+ lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the XML context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyxml_ctx_free(lydctx->xmlctx);
+ lydctx->xmlctx = NULL;
+ }
+ return rc;
+}
diff --git a/src/parser_yang.c b/src/parser_yang.c
new file mode 100644
index 0000000..dd84480
--- /dev/null
+++ b/src/parser_yang.c
@@ -0,0 +1,4827 @@
+/**
+ * @file parser_yang.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG parser
+ *
+ * Copyright (c) 2018 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#include "parser_internal.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "context.h"
+#include "dict.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_schema.h"
+#include "path.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_free.h"
+#include "tree_schema_internal.h"
+
+struct lys_glob_unres;
+
+/**
+ * @brief Insert WORD into the libyang context's dictionary and store as TARGET.
+ *
+ * @param[in] CTX yang parser context to access libyang context.
+ * @param[in] BUF buffer in case the word is not a constant and can be inserted directly (zero-copy)
+ * @param[out] TARGET variable where to store the pointer to the inserted value.
+ * @param[in] WORD string to store.
+ * @param[in] LEN length of the string in WORD to store.
+ */
+#define INSERT_WORD_GOTO(CTX, BUF, TARGET, WORD, LEN, RET, LABEL) \
+ if (BUF) {LY_CHECK_GOTO(RET = lydict_insert_zc(PARSER_CTX(CTX), WORD, &(TARGET)), LABEL);}\
+ else {LY_CHECK_GOTO(RET = lydict_insert(PARSER_CTX(CTX), LEN ? WORD : "", LEN, &(TARGET)), LABEL);}
+
+/**
+ * @brief Read from the IN structure COUNT items. Also updates the indent value in yang parser context
+ *
+ * @param[in] CTX yang parser context to update its indent value.
+ * @param[in] COUNT number of items for which the DATA pointer is supposed to move on.
+ */
+#define MOVE_INPUT(CTX, COUNT) ly_in_skip((CTX)->in, COUNT);(CTX)->indent+=COUNT
+
+/**
+ * @brief Loop through all substatements. Starts a for loop and ::YANG_READ_SUBSTMT_NEXT_ITER must be used at its end.
+ *
+ * @param[in] CTX yang parser context.
+ * @param[out] KW YANG keyword read.
+ * @param[out] WORD Pointer to the keyword itself.
+ * @param[out] WORD_LEN Length of the keyword.
+ * @param[out] RET Variable for error storing.
+ * @param[in] ERR_LABEL Label to go to on error.
+ */
+#define YANG_READ_SUBSTMT_FOR_GOTO(CTX, KW, WORD, WORD_LEN, RET, ERR_LABEL) \
+ ly_bool __loop_end = 0; \
+ if ((RET = get_keyword(CTX, &KW, &WORD, &WORD_LEN))) { \
+ goto ERR_LABEL; \
+ } \
+ if (KW == LY_STMT_SYNTAX_SEMICOLON) { \
+ __loop_end = 1; \
+ } else if (KW != LY_STMT_SYNTAX_LEFT_BRACE) { \
+ LOGVAL_PARSER(CTX, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", expected \";\" or \"{\".", lyplg_ext_stmt2str(KW)); \
+ RET = LY_EVALID; \
+ goto ERR_LABEL; \
+ } else { \
+ YANG_READ_SUBSTMT_NEXT_ITER(CTX, KW, WORD, WORD_LEN, NULL, RET, ERR_LABEL); \
+ } \
+ while (!__loop_end)
+
+/**
+ * @brief Next iteration of ::YANG_READ_SUBSTMT_FOR_GOTO loop.
+ *
+ * @param[in] CTX yang parser context.
+ * @param[out] KW YANG keyword read.
+ * @param[out] WORD Pointer to the keyword itself.
+ * @param[out] WORD_LEN Length of the keyword.
+ * @param[in] EXTS Final extension instance array to store.
+ * @param[out] RET Variable for error storing.
+ * @param[in] ERR_LABEL Label to go to on error.
+ */
+#define YANG_READ_SUBSTMT_NEXT_ITER(CTX, KW, WORD, WORD_LEN, EXTS, RET, ERR_LABEL) \
+ if ((RET = get_keyword(CTX, &KW, &WORD, &WORD_LEN))) { \
+ goto ERR_LABEL; \
+ } \
+ if (KW == LY_STMT_SYNTAX_RIGHT_BRACE) { \
+ if (EXTS && (RET = ly_set_add(&(CTX)->main_ctx->ext_inst, (EXTS), 1, NULL))) { \
+ goto ERR_LABEL; \
+ } \
+ __loop_end = 1; \
+ }
+
+LY_ERR parse_container(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_uses(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_choice(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_case(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_list(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_grouping(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_grp **groupings);
+
+/**
+ * @brief Add another character to dynamic buffer, a low-level function.
+ *
+ * Enlarge if needed. Updates \p input as well as \p buf_used.
+ *
+ * @param[in] ctx libyang context for logging.
+ * @param[in,out] in Input structure.
+ * @param[in] len Number of bytes to get from the input string and copy into the buffer.
+ * @param[in,out] buf Buffer to use, can be moved by realloc().
+ * @param[in,out] buf_len Current size of the buffer.
+ * @param[in,out] buf_used Currently used characters of the buffer.
+ * @return LY_ERR values.
+ */
+LY_ERR
+buf_add_char(struct ly_ctx *ctx, struct ly_in *in, size_t len, char **buf, size_t *buf_len, size_t *buf_used)
+{
+#define BUF_STEP 16;
+ if (*buf_len <= (*buf_used) + len) {
+ *buf_len += BUF_STEP;
+ *buf = ly_realloc(*buf, *buf_len);
+ LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
+ }
+ if (*buf_used) {
+ ly_in_read(in, &(*buf)[*buf_used], len);
+ } else {
+ ly_in_read(in, *buf, len);
+ }
+
+ (*buf_used) += len;
+ return LY_SUCCESS;
+#undef BUF_STEP
+}
+
+/**
+ * @brief Store a single UTF8 character. It depends whether in a dynamically-allocated buffer or just as a pointer to the data.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] arg Type of the input string to select method of checking character validity.
+ * @param[in,out] word_p Word pointer. If buffer (\p word_b) was not yet needed, it is just a pointer to the first
+ * stored character. If buffer was needed (\p word_b is non-NULL or \p need_buf is set), it is pointing to the buffer.
+ * @param[in,out] word_len Current length of the word pointed to by \p word_p.
+ * @param[in,out] word_b Word buffer. Is kept NULL as long as it is not requested (word is a substring of the data).
+ * @param[in,out] buf_len Current length of \p word_b.
+ * @param[in] need_buf Flag if the dynamically allocated buffer is required.
+ * @param[in,out] prefix Storage for internally used flag in case of possible prefixed identifiers:
+ * 0 - colon not yet found (no prefix)
+ * 1 - \p c is the colon character
+ * 2 - prefix already processed, now processing the identifier
+ * @return LY_ERR values.
+ */
+LY_ERR
+buf_store_char(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, size_t *word_len,
+ char **word_b, size_t *buf_len, ly_bool need_buf, uint8_t *prefix)
+{
+ uint32_t c;
+ size_t len;
+
+ /* check valid combination of input paremeters - if need_buf specified, word_b must be provided */
+ assert(!need_buf || (need_buf && word_b));
+
+ /* get UTF8 code point (and number of bytes coding the character) */
+ LY_CHECK_ERR_RET(ly_getutf8(&ctx->in->current, &c, &len),
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[-len]), LY_EVALID);
+ ctx->in->current -= len;
+ if (c == '\n') {
+ ctx->indent = 0;
+ LY_IN_NEW_LINE(ctx->in);
+ } else {
+ /* note - even the multibyte character is count as 1 */
+ ++ctx->indent;
+ }
+
+ /* check character validity */
+ switch (arg) {
+ case Y_IDENTIF_ARG:
+ LY_CHECK_RET(lysp_check_identifierchar((struct lysp_ctx *)ctx, c, !(*word_len), NULL));
+ break;
+ case Y_PREF_IDENTIF_ARG:
+ LY_CHECK_RET(lysp_check_identifierchar((struct lysp_ctx *)ctx, c, !(*word_len), prefix));
+ break;
+ case Y_STR_ARG:
+ case Y_MAYBE_STR_ARG:
+ LY_CHECK_RET(lysp_check_stringchar((struct lysp_ctx *)ctx, c));
+ break;
+ }
+
+ if (word_b && *word_b) {
+ /* add another character into buffer */
+ if (buf_add_char(PARSER_CTX(ctx), ctx->in, len, word_b, buf_len, word_len)) {
+ return LY_EMEM;
+ }
+
+ /* in case of realloc */
+ *word_p = *word_b;
+ } else if (word_b && need_buf) {
+ /* first time we need a buffer, copy everything read up to now */
+ if (*word_len) {
+ *word_b = malloc(*word_len);
+ LY_CHECK_ERR_RET(!*word_b, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+ *buf_len = *word_len;
+ memcpy(*word_b, *word_p, *word_len);
+ }
+
+ /* add this new character into buffer */
+ if (buf_add_char(PARSER_CTX(ctx), ctx->in, len, word_b, buf_len, word_len)) {
+ return LY_EMEM;
+ }
+
+ /* in case of realloc */
+ *word_p = *word_b;
+ } else {
+ /* just remember the first character pointer */
+ if (!*word_p) {
+ *word_p = (char *)ctx->in->current;
+ }
+ /* ... and update the word's length */
+ (*word_len) += len;
+ ly_in_skip(ctx->in, len);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Skip YANG comment in data.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] comment Type of the comment to process:
+ * 1 for a one-line comment,
+ * 2 for a block comment.
+ * @return LY_ERR values.
+ */
+LY_ERR
+skip_comment(struct lysp_yang_ctx *ctx, uint8_t comment)
+{
+ /* internal statuses: */
+#define COMMENT_NO 0 /* comment ended */
+#define COMMENT_LINE 1 /* in line comment */
+#define COMMENT_BLOCK 2 /* in block comment */
+#define COMMENT_BLOCK_END 3 /* in block comment with last read character '*' */
+
+ while (ctx->in->current[0] && comment) {
+ switch (comment) {
+ case COMMENT_LINE:
+ if (ctx->in->current[0] == '\n') {
+ comment = COMMENT_NO;
+ LY_IN_NEW_LINE(ctx->in);
+ }
+ break;
+ case COMMENT_BLOCK:
+ if (ctx->in->current[0] == '*') {
+ comment = COMMENT_BLOCK_END;
+ } else if (ctx->in->current[0] == '\n') {
+ LY_IN_NEW_LINE(ctx->in);
+ }
+ break;
+ case COMMENT_BLOCK_END:
+ if (ctx->in->current[0] == '/') {
+ comment = COMMENT_NO;
+ } else if (ctx->in->current[0] != '*') {
+ if (ctx->in->current[0] == '\n') {
+ LY_IN_NEW_LINE(ctx->in);
+ }
+ comment = COMMENT_BLOCK;
+ }
+ break;
+ default:
+ LOGINT_RET(PARSER_CTX(ctx));
+ }
+
+ if (ctx->in->current[0] == '\n') {
+ ctx->indent = 0;
+ } else {
+ ++ctx->indent;
+ }
+ ++ctx->in->current;
+ }
+
+ if (!ctx->in->current[0] && (comment >= COMMENT_BLOCK)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Unexpected end-of-input, non-terminated comment.");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+
+#undef COMMENT_NO
+#undef COMMENT_LINE
+#undef COMMENT_BLOCK
+#undef COMMENT_BLOCK_END
+}
+
+/**
+ * @brief Read a quoted string from data.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] arg Type of YANG keyword argument expected.
+ * @param[out] word_p Pointer to the read quoted string.
+ * @param[out] word_b Pointer to a dynamically-allocated buffer holding the read quoted string. If not needed,
+ * set to NULL. Otherwise equal to \p word_p.
+ * @param[out] word_len Length of the read quoted string.
+ * @param[out] buf_len Length of the dynamically-allocated buffer \p word_b.
+ * @param[in] indent Current indent (number of YANG spaces). Needed for correct multi-line string
+ * indenation in the final quoted string.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+read_qstring(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, char **word_b,
+ size_t *word_len, size_t *buf_len)
+{
+ /* string parsing status: */
+#define STRING_ENDED 0 /* string ended */
+#define STRING_SINGLE_QUOTED 1 /* string with ' */
+#define STRING_DOUBLE_QUOTED 2 /* string with " */
+#define STRING_DOUBLE_QUOTED_ESCAPED 3 /* string with " with last character \ */
+#define STRING_PAUSED_NEXTSTRING 4 /* string finished, now skipping whitespaces looking for + */
+#define STRING_PAUSED_CONTINUE 5 /* string continues after +, skipping whitespaces */
+
+ uint8_t string;
+ uint64_t block_indent = 0, current_indent = 0;
+ ly_bool need_buf = 0;
+ uint8_t prefix = 0;
+ const char *c;
+ uint64_t trailing_ws = 0; /* current number of stored trailing whitespace characters */
+
+ if (ctx->in->current[0] == '\"') {
+ string = STRING_DOUBLE_QUOTED;
+ current_indent = block_indent = ctx->indent + 1;
+ } else {
+ assert(ctx->in->current[0] == '\'');
+ string = STRING_SINGLE_QUOTED;
+ }
+ MOVE_INPUT(ctx, 1);
+
+ while (ctx->in->current[0] && string) {
+ switch (string) {
+ case STRING_SINGLE_QUOTED:
+ if (ctx->in->current[0] == '\'') {
+ /* string may be finished, but check for + */
+ string = STRING_PAUSED_NEXTSTRING;
+ MOVE_INPUT(ctx, 1);
+ } else {
+ /* check and store character */
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+ }
+ break;
+ case STRING_DOUBLE_QUOTED:
+ switch (ctx->in->current[0]) {
+ case '\"':
+ /* string may be finished, but check for + */
+ string = STRING_PAUSED_NEXTSTRING;
+ MOVE_INPUT(ctx, 1);
+ trailing_ws = 0;
+ break;
+ case '\\':
+ /* special character following */
+ string = STRING_DOUBLE_QUOTED_ESCAPED;
+
+ /* the backslash sequence is substituted, so we will need a buffer to store the result */
+ need_buf = 1;
+
+ /* move forward to the escaped character */
+ ++ctx->in->current;
+
+ /* note that the trailing whitespaces are supposed to be trimmed before substitution of
+ * backslash-escaped characters (RFC 7950, 6.1.3), so we have to zero the trailing whitespaces counter */
+ trailing_ws = 0;
+
+ /* since the backslash-escaped character is handled as first non-whitespace character, stop eating indentation */
+ current_indent = block_indent;
+ break;
+ case ' ':
+ if (current_indent < block_indent) {
+ ++current_indent;
+ MOVE_INPUT(ctx, 1);
+ } else {
+ /* check and store whitespace character */
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+ trailing_ws++;
+ }
+ break;
+ case '\t':
+ if (current_indent < block_indent) {
+ assert(need_buf);
+ current_indent += Y_TAB_SPACES;
+ ctx->indent += Y_TAB_SPACES;
+ for ( ; current_indent > block_indent; --current_indent, --ctx->indent) {
+ /* store leftover spaces from the tab */
+ c = ctx->in->current;
+ ctx->in->current = " ";
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+ ctx->in->current = c;
+ trailing_ws++;
+ }
+ ++ctx->in->current;
+ } else {
+ /* check and store whitespace character */
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+ trailing_ws++;
+ /* additional characters for indentation - only 1 was count in buf_store_char */
+ ctx->indent += Y_TAB_SPACES - 1;
+ }
+ break;
+ case '\r':
+ if (ctx->in->current[1] != '\n') {
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
+ return LY_EVALID;
+ }
+ /* fallthrough */
+ case '\n':
+ if (block_indent) {
+ /* we will be removing the indents so we need our own buffer */
+ need_buf = 1;
+
+ /* remove trailing tabs and spaces */
+ (*word_len) = *word_len - trailing_ws;
+
+ /* restart indentation */
+ current_indent = 0;
+ }
+
+ /* check and store character */
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+
+ /* reset context indentation counter for possible string after this one */
+ ctx->indent = 0;
+ trailing_ws = 0;
+ break;
+ default:
+ /* first non-whitespace character, stop eating indentation */
+ current_indent = block_indent;
+
+ /* check and store character */
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+ trailing_ws = 0;
+ break;
+ }
+ break;
+ case STRING_DOUBLE_QUOTED_ESCAPED:
+ /* string encoded characters */
+ c = ctx->in->current;
+ switch (ctx->in->current[0]) {
+ case 'n':
+ ctx->in->current = "\n";
+ /* fix false newline count in buf_store_char() */
+ ctx->in->line--;
+ break;
+ case 't':
+ ctx->in->current = "\t";
+ break;
+ case '\"':
+ case '\\':
+ /* ok as is */
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Double-quoted string unknown special character '\\%c'.",
+ ctx->in->current[0]);
+ return LY_EVALID;
+ }
+
+ /* check and store character */
+ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
+
+ string = STRING_DOUBLE_QUOTED;
+ ctx->in->current = c + 1;
+ break;
+ case STRING_PAUSED_NEXTSTRING:
+ switch (ctx->in->current[0]) {
+ case '+':
+ /* string continues */
+ string = STRING_PAUSED_CONTINUE;
+ need_buf = 1;
+ break;
+ case '\r':
+ if (ctx->in->current[1] != '\n') {
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
+ return LY_EVALID;
+ }
+ MOVE_INPUT(ctx, 1);
+ /* fallthrough */
+ case '\n':
+ LY_IN_NEW_LINE(ctx->in);
+ /* fall through */
+ case ' ':
+ case '\t':
+ /* just skip */
+ break;
+ default:
+ /* string is finished */
+ goto string_end;
+ }
+ MOVE_INPUT(ctx, 1);
+ break;
+ case STRING_PAUSED_CONTINUE:
+ switch (ctx->in->current[0]) {
+ case '\r':
+ if (ctx->in->current[1] != '\n') {
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
+ return LY_EVALID;
+ }
+ MOVE_INPUT(ctx, 1);
+ /* fallthrough */
+ case '\n':
+ LY_IN_NEW_LINE(ctx->in);
+ /* fall through */
+ case ' ':
+ case '\t':
+ /* skip */
+ break;
+ case '\'':
+ string = STRING_SINGLE_QUOTED;
+ break;
+ case '\"':
+ string = STRING_DOUBLE_QUOTED;
+ break;
+ default:
+ /* it must be quoted again */
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Both string parts divided by '+' must be quoted.");
+ return LY_EVALID;
+ }
+ MOVE_INPUT(ctx, 1);
+ break;
+ default:
+ return LY_EINT;
+ }
+ }
+
+string_end:
+ if ((arg <= Y_PREF_IDENTIF_ARG) && !(*word_len)) {
+ /* empty identifier */
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Statement argument is required.");
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+
+#undef STRING_ENDED
+#undef STRING_SINGLE_QUOTED
+#undef STRING_DOUBLE_QUOTED
+#undef STRING_DOUBLE_QUOTED_ESCAPED
+#undef STRING_PAUSED_NEXTSTRING
+#undef STRING_PAUSED_CONTINUE
+}
+
+/**
+ * @brief Get another YANG string from the raw data.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] arg Type of YANG keyword argument expected.
+ * @param[out] flags optional output argument to get flag of the argument's quoting (LYS_*QUOTED - see
+ * [schema node flags](@ref snodeflags))
+ * @param[out] word_p Pointer to the read string. Can return NULL if \p arg is #Y_MAYBE_STR_ARG.
+ * @param[out] word_b Pointer to a dynamically-allocated buffer holding the read string. If not needed,
+ * set to NULL. Otherwise equal to \p word_p.
+ * @param[out] word_len Length of the read string.
+ * @return LY_ERR values.
+ */
+LY_ERR
+get_argument(struct lysp_yang_ctx *ctx, enum yang_arg arg, uint16_t *flags, char **word_p,
+ char **word_b, size_t *word_len)
+{
+ LY_ERR ret;
+ size_t buf_len = 0;
+ uint8_t prefix = 0;
+
+ /* word buffer - dynamically allocated */
+ *word_b = NULL;
+
+ /* word pointer - just a pointer to data */
+ *word_p = NULL;
+
+ *word_len = 0;
+ while (ctx->in->current[0]) {
+ switch (ctx->in->current[0]) {
+ case '\'':
+ case '\"':
+ if (*word_len) {
+ /* invalid - quotes cannot be in unquoted string and only optsep, ; or { can follow it */
+ LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, 1, ctx->in->current,
+ "unquoted string character, optsep, semicolon or opening brace");
+ ret = LY_EVALID;
+ goto error;
+ }
+ if (flags) {
+ (*flags) |= ctx->in->current[0] == '\'' ? LYS_SINGLEQUOTED : LYS_DOUBLEQUOTED;
+ }
+ LY_CHECK_GOTO(ret = read_qstring(ctx, arg, word_p, word_b, word_len, &buf_len), error);
+ if (!*word_p) {
+ /* do not return NULL word */
+ *word_p = "";
+ }
+ goto str_end;
+ case '/':
+ if (ctx->in->current[1] == '/') {
+ /* one-line comment */
+ MOVE_INPUT(ctx, 2);
+ LY_CHECK_GOTO(ret = skip_comment(ctx, 1), error);
+ } else if (ctx->in->current[1] == '*') {
+ /* block comment */
+ MOVE_INPUT(ctx, 2);
+ LY_CHECK_GOTO(ret = skip_comment(ctx, 2), error);
+ } else {
+ /* not a comment after all */
+ LY_CHECK_GOTO(ret = buf_store_char(ctx, arg, word_p, word_len, word_b, &buf_len, 0, &prefix), error);
+ }
+ break;
+ case ' ':
+ if (*word_len) {
+ /* word is finished */
+ goto str_end;
+ }
+ MOVE_INPUT(ctx, 1);
+ break;
+ case '\t':
+ if (*word_len) {
+ /* word is finished */
+ goto str_end;
+ }
+ /* tabs count for 8 spaces */
+ ctx->indent += Y_TAB_SPACES;
+
+ ++ctx->in->current;
+ break;
+ case '\r':
+ if (ctx->in->current[1] != '\n') {
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
+ ret = LY_EVALID;
+ goto error;
+ }
+ MOVE_INPUT(ctx, 1);
+ /* fallthrough */
+ case '\n':
+ if (*word_len) {
+ /* word is finished */
+ goto str_end;
+ }
+ LY_IN_NEW_LINE(ctx->in);
+ MOVE_INPUT(ctx, 1);
+
+ /* reset indent */
+ ctx->indent = 0;
+ break;
+ case ';':
+ case '{':
+ if (*word_len || (arg == Y_MAYBE_STR_ARG)) {
+ /* word is finished */
+ goto str_end;
+ }
+
+ LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, 1, ctx->in->current, "an argument");
+ ret = LY_EVALID;
+ goto error;
+ case '}':
+ /* invalid - braces cannot be in unquoted string (opening braces terminates the string and can follow it) */
+ LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, 1, ctx->in->current,
+ "unquoted string character, optsep, semicolon or opening brace");
+ ret = LY_EVALID;
+ goto error;
+ default:
+ LY_CHECK_GOTO(ret = buf_store_char(ctx, arg, word_p, word_len, word_b, &buf_len, 0, &prefix), error);
+ break;
+ }
+ }
+
+ /* unexpected end of loop */
+ LOGVAL_PARSER(ctx, LY_VCODE_EOF);
+ ret = LY_EVALID;
+ goto error;
+
+str_end:
+ /* terminating NULL byte for buf */
+ if (*word_b) {
+ (*word_b) = ly_realloc(*word_b, (*word_len) + 1);
+ LY_CHECK_ERR_RET(!(*word_b), LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+ (*word_b)[*word_len] = '\0';
+ *word_p = *word_b;
+ }
+
+ return LY_SUCCESS;
+
+error:
+ free(*word_b);
+ *word_b = NULL;
+ return ret;
+}
+
+/**
+ * @brief Get another YANG keyword from the raw data.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[out] kw YANG keyword read.
+ * @param[out] word_p Pointer to the keyword in the data. Useful for extension instances.
+ * @param[out] word_len Length of the keyword in the data. Useful for extension instances.
+ * @return LY_ERR values.
+ */
+LY_ERR
+get_keyword(struct lysp_yang_ctx *ctx, enum ly_stmt *kw, char **word_p, size_t *word_len)
+{
+ uint8_t prefix;
+ const char *word_start;
+ size_t len;
+
+ if (word_p) {
+ *word_p = NULL;
+ *word_len = 0;
+ }
+
+ /* first skip "optsep", comments */
+ while (ctx->in->current[0]) {
+ switch (ctx->in->current[0]) {
+ case '/':
+ if (ctx->in->current[1] == '/') {
+ /* one-line comment */
+ MOVE_INPUT(ctx, 2);
+ LY_CHECK_RET(skip_comment(ctx, 1));
+ } else if (ctx->in->current[1] == '*') {
+ /* block comment */
+ MOVE_INPUT(ctx, 2);
+ LY_CHECK_RET(skip_comment(ctx, 2));
+ } else {
+ /* error - not a comment after all, keyword cannot start with slash */
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '/'.");
+ return LY_EVALID;
+ }
+ continue;
+ case '\n':
+ /* skip whitespaces (optsep) */
+ LY_IN_NEW_LINE(ctx->in);
+ ctx->indent = 0;
+ break;
+ case ' ':
+ /* skip whitespaces (optsep) */
+ ++ctx->indent;
+ break;
+ case '\t':
+ /* skip whitespaces (optsep) */
+ ctx->indent += Y_TAB_SPACES;
+ break;
+ case '\r':
+ /* possible CRLF endline */
+ if (ctx->in->current[1] == '\n') {
+ break;
+ }
+ /* fallthrough */
+ default:
+ /* either a keyword start or an invalid character */
+ goto keyword_start;
+ }
+
+ ly_in_skip(ctx->in, 1);
+ }
+
+keyword_start:
+ word_start = ctx->in->current;
+ *kw = lysp_match_kw(ctx->in, &ctx->indent);
+
+ if (*kw == LY_STMT_SYNTAX_SEMICOLON) {
+ goto success;
+ } else if (*kw == LY_STMT_SYNTAX_LEFT_BRACE) {
+ ctx->depth++;
+ if (ctx->depth > LY_MAX_BLOCK_DEPTH) {
+ LOGERR(PARSER_CTX(ctx), LY_EINVAL, "The maximum number of block nestings has been exceeded.");
+ return LY_EINVAL;
+ }
+ goto success;
+ } else if (*kw == LY_STMT_SYNTAX_RIGHT_BRACE) {
+ ctx->depth--;
+ goto success;
+ }
+
+ if (*kw != LY_STMT_NONE) {
+ /* make sure we have the whole keyword */
+ switch (ctx->in->current[0]) {
+ case '\r':
+ if (ctx->in->current[1] != '\n') {
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
+ return LY_EVALID;
+ }
+ MOVE_INPUT(ctx, 1);
+ /* fallthrough */
+ case '\n':
+ case '\t':
+ case ' ':
+ /* mandatory "sep" is just checked, not eaten so nothing in the context is updated */
+ break;
+ case ':':
+ /* keyword is not actually a keyword, but prefix of an extension.
+ * To avoid repeated check of the prefix syntax, move to the point where the colon was read
+ * and we will be checking the keyword (extension instance) itself */
+ prefix = 1;
+ MOVE_INPUT(ctx, 1);
+ goto extension;
+ case '{':
+ /* allowed only for input and output statements which can be without arguments */
+ if ((*kw == LY_STMT_INPUT) || (*kw == LY_STMT_OUTPUT)) {
+ break;
+ }
+ /* fall through */
+ default:
+ MOVE_INPUT(ctx, 1);
+ LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, (int)(ctx->in->current - word_start), word_start,
+ "a keyword followed by a separator");
+ return LY_EVALID;
+ }
+ } else {
+ /* still can be an extension */
+ prefix = 0;
+
+extension:
+ while (ctx->in->current[0] && (ctx->in->current[0] != ' ') && (ctx->in->current[0] != '\t') &&
+ (ctx->in->current[0] != '\n') && (ctx->in->current[0] != '\r') && (ctx->in->current[0] != '{') &&
+ (ctx->in->current[0] != ';')) {
+ uint32_t c = 0;
+
+ LY_CHECK_ERR_RET(ly_getutf8(&ctx->in->current, &c, &len),
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[-len]), LY_EVALID);
+ ++ctx->indent;
+ /* check character validity */
+ LY_CHECK_RET(lysp_check_identifierchar((struct lysp_ctx *)ctx, c,
+ ctx->in->current - len == word_start ? 1 : 0, &prefix));
+ }
+ if (!ctx->in->current[0]) {
+ LOGVAL_PARSER(ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+ }
+
+ /* prefix is mandatory for extension instances */
+ if (prefix != 2) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, (int)(ctx->in->current - word_start), word_start, "a keyword");
+ return LY_EVALID;
+ }
+
+ *kw = LY_STMT_EXTENSION_INSTANCE;
+ }
+
+success:
+ if (word_p) {
+ *word_p = (char *)word_start;
+ *word_len = ctx->in->current - word_start;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse extension instance substatements.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] kw Statement keyword value matching @p word value.
+ * @param[in] word Extension instance substatement name (keyword).
+ * @param[in] word_len Extension instance substatement name length.
+ * @param[in,out] child Children of this extension instance to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_ext_substmt(struct lysp_yang_ctx *ctx, enum ly_stmt kw, char *word, size_t word_len,
+ struct lysp_stmt **child)
+{
+ char *buf;
+ LY_ERR ret = LY_SUCCESS;
+ enum ly_stmt child_kw;
+ struct lysp_stmt *stmt, *par_child;
+
+ stmt = calloc(1, sizeof *stmt);
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(NULL), LY_EMEM);
+
+ /* insert into parent statements */
+ if (!*child) {
+ *child = stmt;
+ } else {
+ for (par_child = *child; par_child->next; par_child = par_child->next) {}
+ par_child->next = stmt;
+ }
+
+ /* statement */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), word, word_len, &stmt->stmt));
+
+ /* get optional argument */
+ LY_CHECK_RET(get_argument(ctx, Y_MAYBE_STR_ARG, &stmt->flags, &word, &buf, &word_len));
+ if (word) {
+ INSERT_WORD_GOTO(ctx, buf, stmt->arg, word, word_len, ret, cleanup);
+ }
+
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = PARSER_CUR_PMOD(ctx);
+ stmt->kw = kw;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, child_kw, word, word_len, ret, cleanup) {
+ LY_CHECK_GOTO(ret = parse_ext_substmt(ctx, child_kw, word, word_len, &stmt->child), cleanup)
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, child_kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse extension instance.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] ext_name Extension instance substatement name (keyword).
+ * @param[in] ext_name_len Extension instance substatement name length.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[in] parent_stmt_index In case of several @p parent_stmt, index of the relevant @p parent statement.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_ext(struct lysp_yang_ctx *ctx, const char *ext_name, size_t ext_name_len, const void *parent,
+ enum ly_stmt parent_stmt, LY_ARRAY_COUNT_TYPE parent_stmt_index, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ struct lysp_ext_instance *e;
+ enum ly_stmt kw;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *exts, e, LY_EMEM);
+
+ if (!ly_strnchr(ext_name, ':', ext_name_len)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%*.s\" without the mandatory prefix.", ext_name_len, ext_name);
+ return LY_EVALID;
+ }
+
+ /* store name */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), ext_name, ext_name_len, &e->name));
+
+ /* get optional argument */
+ LY_CHECK_RET(get_argument(ctx, Y_MAYBE_STR_ARG, NULL, &word, &buf, &word_len));
+ if (word) {
+ INSERT_WORD_GOTO(ctx, buf, e->argument, word, word_len, ret, cleanup);
+ }
+
+ /* store the rest of information */
+ e->format = LY_VALUE_SCHEMA;
+ e->parsed = NULL;
+ e->prefix_data = PARSER_CUR_PMOD(ctx);
+ e->parent = (void *)parent;
+ e->parent_stmt = parent_stmt;
+ e->parent_stmt_index = parent_stmt_index;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ LY_CHECK_GOTO(ret = parse_ext_substmt(ctx, kw, word, word_len, &e->child), cleanup)
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse a generic text field without specific constraints. Those are contact, organization,
+ * description, etc...
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of statement in @p value.
+ * @param[in] parent_stmt_index In case of several @p parent_stmt, index of the relevant @p parent statement.
+ * @param[in,out] value Place to store the parsed value.
+ * @param[in] arg Type of the YANG keyword argument (of the value).
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_text_field(struct lysp_yang_ctx *ctx, const void *parent, enum ly_stmt parent_stmt, uint32_t parent_stmt_index,
+ const char **value, enum yang_arg arg, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (*value) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, arg, NULL, &word, &buf, &word_len));
+
+ /* store value and spend buf if allocated */
+ INSERT_WORD_GOTO(ctx, buf, *value, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, parent, parent_stmt, parent_stmt_index, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the yang-version statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] mod Module to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_yangversion(struct lysp_yang_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (mod->version) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "yang-version");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ if ((word_len == 1) && !strncmp(word, "1", word_len)) {
+ mod->version = LYS_VERSION_1_0;
+ } else if ((word_len == ly_strlen_const("1.1")) && !strncmp(word, "1.1", word_len)) {
+ mod->version = LYS_VERSION_1_1;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yang-version");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, mod, LY_STMT_YANG_VERSION, 0, &mod->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "yang-version");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the belongs-to statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] submod Submodule to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_belongsto(struct lysp_yang_ctx *ctx, struct lysp_submodule *submod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (submod->prefix) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "belongs-to");
+ return LY_EVALID;
+ }
+
+ /* get value, it must match the main module */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ if (ly_strncmp(PARSER_CUR_PMOD(ctx)->mod->name, word, word_len)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Submodule \"belongs-to\" value \"%.*s\" does not match its module name \"%s\".",
+ (int)word_len, word, PARSER_CUR_PMOD(ctx)->mod->name);
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_PREFIX:
+ LY_CHECK_RET(parse_text_field(ctx, submod->prefix, LY_STMT_PREFIX, 0, &submod->prefix, Y_IDENTIF_ARG, &submod->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, submod, LY_STMT_BELONGS_TO, 0, &submod->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "belongs-to");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!submod->prefix) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "prefix", "belongs-to");
+ return LY_EVALID;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the revision-date statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] rev Buffer to store the parsed value in.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_revisiondate(struct lysp_yang_ctx *ctx, char *rev, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (rev[0]) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "revision-date");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ /* check value */
+ if (lysp_check_date((struct lysp_ctx *)ctx, word, word_len, "revision-date")) {
+ free(buf);
+ return LY_EVALID;
+ }
+
+ /* store value and spend buf if allocated */
+ strncpy(rev, word, word_len);
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, rev, LY_STMT_REVISION_DATE, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "revision-date");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the include statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] module_name Name of the module to check name collisions.
+ * @param[in,out] includes Parsed includes to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_include(struct lysp_yang_ctx *ctx, const char *module_name, struct lysp_include **includes)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_include *inc;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *includes, inc, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+
+ INSERT_WORD_GOTO(ctx, buf, inc->name, word, word_len, ret, cleanup);
+
+ /* submodules share the namespace with the module names, so there must not be
+ * a module of the same name in the context, no need for revision matching */
+ if (!strcmp(module_name, inc->name) || ly_ctx_get_module_latest(PARSER_CTX(ctx), inc->name)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_NAME2_COL, "module", "submodule", inc->name);
+ return LY_EVALID;
+ }
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "description", "include");
+ LY_CHECK_RET(parse_text_field(ctx, inc->dsc, LY_STMT_DESCRIPTION, 0, &inc->dsc, Y_STR_ARG, &inc->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "reference", "include");
+ LY_CHECK_RET(parse_text_field(ctx, inc->ref, LY_STMT_REFERENCE, 0, &inc->ref, Y_STR_ARG, &inc->exts));
+ break;
+ case LY_STMT_REVISION_DATE:
+ LY_CHECK_RET(parse_revisiondate(ctx, inc->rev, &inc->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, inc, LY_STMT_INCLUDE, 0, &inc->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "include");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, inc->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the import statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] module_prefix Prefix of the module to check prefix collisions.
+ * @param[in,out] imports Parsed imports to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_import(struct lysp_yang_ctx *ctx, const char *module_prefix, struct lysp_import **imports)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_import *imp;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *imports, imp, LY_EVALID);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, imp->name, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_PREFIX:
+ LY_CHECK_RET(parse_text_field(ctx, imp->prefix, LY_STMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts));
+ LY_CHECK_RET(lysp_check_prefix((struct lysp_ctx *)ctx, *imports, module_prefix, &imp->prefix), LY_EVALID);
+ break;
+ case LY_STMT_DESCRIPTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "description", "import");
+ LY_CHECK_RET(parse_text_field(ctx, imp->dsc, LY_STMT_DESCRIPTION, 0, &imp->dsc, Y_STR_ARG, &imp->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "reference", "import");
+ LY_CHECK_RET(parse_text_field(ctx, imp->ref, LY_STMT_REFERENCE, 0, &imp->ref, Y_STR_ARG, &imp->exts));
+ break;
+ case LY_STMT_REVISION_DATE:
+ LY_CHECK_RET(parse_revisiondate(ctx, imp->rev, &imp->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, imp, LY_STMT_IMPORT, 0, &imp->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "import");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, imp->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ LY_CHECK_ERR_RET(!imp->prefix, LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "prefix", "import"), LY_EVALID);
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the revision statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] revs Parsed revisions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_revision(struct lysp_yang_ctx *ctx, struct lysp_revision **revs)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_revision *rev;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *revs, rev, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ /* check value */
+ if (lysp_check_date((struct lysp_ctx *)ctx, word, word_len, "revision")) {
+ free(buf);
+ return LY_EVALID;
+ }
+
+ strncpy(rev->date, word, word_len);
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, rev->dsc, LY_STMT_DESCRIPTION, 0, &rev->dsc, Y_STR_ARG, &rev->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, rev->ref, LY_STMT_REFERENCE, 0, &rev->ref, Y_STR_ARG, &rev->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, rev, LY_STMT_REVISION, 0, &rev->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "revision");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, rev->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse a generic text field that can have more instances such as base.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[in,out] texts Parsed values to add to.
+ * @param[in] arg Type of the expected argument.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_text_fields(struct lysp_yang_ctx *ctx, enum ly_stmt parent_stmt, const char ***texts, enum yang_arg arg,
+ struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ const char **item;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ /* allocate new pointer */
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *texts, item, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, arg, NULL, &word, &buf, &word_len));
+
+ INSERT_WORD_GOTO(ctx, buf, *item, word, word_len, ret, cleanup);
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, *texts, parent_stmt, LY_ARRAY_COUNT(*texts) - 1, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse a generic text field that can have more instances such as base.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] parent_stmt Type of statement stored in @p qnames.
+ * @param[in,out] qnames Parsed qnames to add to.
+ * @param[in] arg Type of the expected argument.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_qnames(struct lysp_yang_ctx *ctx, enum ly_stmt parent_stmt, struct lysp_qname **qnames, enum yang_arg arg,
+ struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ struct lysp_qname *item;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ /* allocate new pointer */
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *qnames, item, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, arg, NULL, &word, &buf, &word_len));
+
+ INSERT_WORD_GOTO(ctx, buf, item->str, word, word_len, ret, cleanup);
+ item->mod = PARSER_CUR_PMOD(ctx);
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, *qnames, parent_stmt, LY_ARRAY_COUNT(*qnames) - 1, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the config statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_config(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (*flags & LYS_CONFIG_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "config");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ if ((word_len == ly_strlen_const("true")) && !strncmp(word, "true", word_len)) {
+ *flags |= LYS_CONFIG_W;
+ } else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
+ *flags |= LYS_CONFIG_R;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "config");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, flags, LY_STMT_CONFIG, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "config");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the mandatory statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_mandatory(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (*flags & LYS_MAND_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "mandatory");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ if ((word_len == ly_strlen_const("true")) && !strncmp(word, "true", word_len)) {
+ *flags |= LYS_MAND_TRUE;
+ } else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
+ *flags |= LYS_MAND_FALSE;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "mandatory");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, flags, LY_STMT_MANDATORY, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "mandatory");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse a restriction such as range or length.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] restr_kw Type of this particular restriction.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_restr(struct lysp_yang_ctx *ctx, enum ly_stmt restr_kw, struct lysp_restr *restr)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ CHECK_NONEMPTY(ctx, word_len, lyplg_ext_stmt2str(restr_kw));
+ INSERT_WORD_GOTO(ctx, buf, restr->arg.str, word, word_len, ret, cleanup);
+ restr->arg.mod = PARSER_CUR_PMOD(ctx);
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, restr->dsc, LY_STMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, restr->ref, LY_STMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_APP_TAG:
+ LY_CHECK_RET(parse_text_field(ctx, restr, LY_STMT_ERROR_APP_TAG, 0, &restr->eapptag, Y_STR_ARG,
+ &restr->exts));
+ break;
+ case LY_STMT_ERROR_MESSAGE:
+ LY_CHECK_RET(parse_text_field(ctx, restr, LY_STMT_ERROR_MESSAGE, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, restr, restr_kw, 0, &restr->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(restr_kw));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, restr->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse a restriction that can have more instances such as must.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] restr_kw Type of this particular restriction.
+ * @param[in,out] restrs Restrictions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_restrs(struct lysp_yang_ctx *ctx, enum ly_stmt restr_kw, struct lysp_restr **restrs)
+{
+ struct lysp_restr *restr;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *restrs, restr, LY_EMEM);
+ return parse_restr(ctx, restr_kw, restr);
+}
+
+/**
+ * @brief Parse the status statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_status(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (*flags & LYS_STATUS_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "status");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ if ((word_len == ly_strlen_const("current")) && !strncmp(word, "current", word_len)) {
+ *flags |= LYS_STATUS_CURR;
+ } else if ((word_len == ly_strlen_const("deprecated")) && !strncmp(word, "deprecated", word_len)) {
+ *flags |= LYS_STATUS_DEPRC;
+ } else if ((word_len == ly_strlen_const("obsolete")) && !strncmp(word, "obsolete", word_len)) {
+ *flags |= LYS_STATUS_OBSLT;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "status");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, flags, LY_STMT_STATUS, 0, exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "status");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the when statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] when_p When pointer to parse to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_when(struct lysp_yang_ctx *ctx, struct lysp_when **when_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_when *when;
+ struct lysf_ctx fctx = {.ctx = PARSER_CTX(ctx)};
+
+ if (*when_p) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "when");
+ return LY_EVALID;
+ }
+
+ when = calloc(1, sizeof *when);
+ LY_CHECK_ERR_GOTO(!when, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+ CHECK_NONEMPTY(ctx, word_len, "when");
+ INSERT_WORD_GOTO(ctx, buf, when->cond, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_GOTO(ret = parse_text_field(ctx, when->dsc, LY_STMT_DESCRIPTION, 0, &when->dsc, Y_STR_ARG, &when->exts),
+ cleanup);
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_GOTO(ret = parse_text_field(ctx, when->ref, LY_STMT_REFERENCE, 0, &when->ref, Y_STR_ARG, &when->exts),
+ cleanup);
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, *when_p, LY_STMT_WHEN, 0, &when->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "when");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, when->exts, ret, cleanup);
+ }
+
+cleanup:
+ if (ret) {
+ lysp_when_free(&fctx, when);
+ free(when);
+ } else {
+ *when_p = when;
+ }
+ return ret;
+}
+
+/**
+ * @brief Parse the anydata or anyxml statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] any_kw Type of this particular keyword.
+ * @param[in] parent Node parent.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_any(struct lysp_yang_ctx *ctx, enum ly_stmt any_kw, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ struct lysp_node_anydata *any;
+ enum ly_stmt kw;
+
+ /* create new structure and insert into siblings */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, any, next, LY_EMEM);
+
+ any->nodetype = any_kw == LY_STMT_ANYDATA ? LYS_ANYDATA : LYS_ANYXML;
+ any->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, any->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &any->flags, &any->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, any->dsc, LY_STMT_DESCRIPTION, 0, &any->dsc, Y_STR_ARG, &any->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &any->iffeatures, Y_STR_ARG, &any->exts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(parse_mandatory(ctx, &any->flags, &any->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(parse_restrs(ctx, kw, &any->musts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, any->ref, LY_STMT_REFERENCE, 0, &any->ref, Y_STR_ARG, &any->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &any->flags, &any->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &any->when));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, any, any_kw, 0, &any->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(any_kw));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, any->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the value or position statement. Substatement of type enum statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] val_kw Type of this particular keyword.
+ * @param[in,out] enm Structure to fill.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct lysp_type_enum *enm)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word, *ptr;
+ size_t word_len;
+ long long num = 0;
+ unsigned long long unum = 0;
+ enum ly_stmt kw;
+
+ if (enm->flags & LYS_SET_VALUE) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ enm->flags |= LYS_SET_VALUE;
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == LY_STMT_POSITION) && !strncmp(word, "-0", 2))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ errno = 0;
+ if (val_kw == LY_STMT_VALUE) {
+ num = strtoll(word, &ptr, LY_BASE_DEC);
+ if ((num < INT64_C(-2147483648)) || (num > INT64_C(2147483647))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ } else {
+ unum = strtoull(word, &ptr, LY_BASE_DEC);
+ if (unum > UINT64_C(4294967295)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - word) != word_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (errno == ERANGE) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (val_kw == LY_STMT_VALUE) {
+ enm->value = num;
+ } else {
+ enm->value = unum;
+ }
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ ret = parse_ext(ctx, word, word_len, enm, val_kw, 0, &enm->exts);
+ LY_CHECK_GOTO(ret, cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(val_kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the enum or bit statement. Substatement of type statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] enum_kw Type of this particular keyword.
+ * @param[in,out] enums Enums or bits to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_type_enum(struct lysp_yang_ctx *ctx, enum ly_stmt enum_kw, struct lysp_type_enum **enums)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_type_enum *enm;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *enums, enm, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, enum_kw == LY_STMT_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ if (enum_kw == LY_STMT_ENUM) {
+ ret = lysp_check_enum_name((struct lysp_ctx *)ctx, (const char *)word, word_len);
+ LY_CHECK_ERR_RET(ret, free(buf), ret);
+ } /* else nothing specific for YANG_BIT */
+
+ INSERT_WORD_GOTO(ctx, buf, enm->name, word, word_len, ret, cleanup);
+ CHECK_UNIQUENESS(ctx, *enums, name, lyplg_ext_stmt2str(enum_kw), enm->name);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, enm->dsc, LY_STMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "if-feature", lyplg_ext_stmt2str(enum_kw));
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, enm->ref, LY_STMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &enm->flags, &enm->exts));
+ break;
+ case LY_STMT_VALUE:
+ LY_CHECK_ERR_RET(enum_kw == LY_STMT_BIT, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw),
+ lyplg_ext_stmt2str(enum_kw)), LY_EVALID);
+ LY_CHECK_RET(parse_type_enum_value_pos(ctx, kw, enm));
+ break;
+ case LY_STMT_POSITION:
+ LY_CHECK_ERR_RET(enum_kw == LY_STMT_ENUM, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw),
+ lyplg_ext_stmt2str(enum_kw)), LY_EVALID);
+ LY_CHECK_RET(parse_type_enum_value_pos(ctx, kw, enm));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, enm, enum_kw, 0, &enm->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(enum_kw));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, enm->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the fraction-digits statement. Substatement of type statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] type Type to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_type_fracdigits(struct lysp_yang_ctx *ctx, struct lysp_type *type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word, *ptr;
+ size_t word_len;
+ unsigned long long num;
+ enum ly_stmt kw;
+
+ if (type->fraction_digits) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "fraction-digits");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ errno = 0;
+ num = strtoull(word, &ptr, LY_BASE_DEC);
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - word) != word_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if ((errno == ERANGE) || (num > LY_TYPE_DEC64_FD_MAX)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "fraction-digits");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ type->fraction_digits = num;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, type, LY_STMT_FRACTION_DIGITS, 0, &type->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "fraction-digits");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the require-instance statement. Substatement of type statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] type Type to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_type_reqinstance(struct lysp_yang_ctx *ctx, struct lysp_type *type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (type->flags & LYS_SET_REQINST) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "require-instance");
+ return LY_EVALID;
+ }
+ type->flags |= LYS_SET_REQINST;
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if ((word_len == ly_strlen_const("true")) && !strncmp(word, "true", word_len)) {
+ type->require_instance = 1;
+ } else if ((word_len != ly_strlen_const("false")) || strncmp(word, "false", word_len)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "require-instance");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, type, LY_STMT_REQUIRE_INSTANCE, 0, &type->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "require-instance");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the modifier statement. Substatement of type pattern statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] restr Restriction to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_type_pattern_modifier(struct lysp_yang_ctx *ctx, struct lysp_restr *restr)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (restr->arg.str[0] == LYSP_RESTR_PATTERN_NACK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "modifier");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if ((word_len != ly_strlen_const("invert-match")) || strncmp(word, "invert-match", word_len)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* replace the value in the dictionary */
+ buf = malloc(strlen(restr->arg.str) + 1);
+ LY_CHECK_ERR_GOTO(!buf, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+ strcpy(buf, restr->arg.str);
+ lydict_remove(PARSER_CTX(ctx), restr->arg.str);
+
+ assert(buf[0] == LYSP_RESTR_PATTERN_ACK);
+ buf[0] = LYSP_RESTR_PATTERN_NACK;
+ ret = lydict_insert_zc(PARSER_CTX(ctx), buf, &restr->arg.str);
+ buf = NULL;
+ LY_CHECK_GOTO(ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, restr, LY_STMT_MODIFIER, 0, &restr->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "modifier");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the pattern statement. Substatement of type statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] patterns Restrictions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_type_pattern(struct lysp_yang_ctx *ctx, struct lysp_restr **patterns)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_restr *restr;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *patterns, restr, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ /* add special meaning first byte */
+ if (buf) {
+ buf = ly_realloc(buf, word_len + 2);
+ word = buf;
+ } else {
+ buf = malloc(word_len + 2);
+ }
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+ if (word_len) {
+ memmove(buf + 1, word, word_len);
+ }
+ buf[0] = LYSP_RESTR_PATTERN_ACK; /* pattern's default regular-match flag */
+ buf[word_len + 1] = '\0'; /* terminating NULL byte */
+ LY_CHECK_RET(lydict_insert_zc(PARSER_CTX(ctx), buf, &restr->arg.str));
+ restr->arg.mod = PARSER_CUR_PMOD(ctx);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, restr->dsc, LY_STMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, restr->ref, LY_STMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_APP_TAG:
+ LY_CHECK_RET(parse_text_field(ctx, restr, LY_STMT_ERROR_APP_TAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_ERROR_MESSAGE:
+ LY_CHECK_RET(parse_text_field(ctx, restr, LY_STMT_ERROR_MESSAGE, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
+ break;
+ case LY_STMT_MODIFIER:
+ PARSER_CHECK_STMTVER2_RET(ctx, "modifier", "pattern");
+ LY_CHECK_RET(parse_type_pattern_modifier(ctx, restr));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, restr, LY_STMT_PATTERN, 0, &restr->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "pattern");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, restr->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the type statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] type Type to wrote to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_type(struct lysp_yang_ctx *ctx, struct lysp_type *type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ const char *str_path = NULL;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_type *nest_type;
+
+ if (type->name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "type");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, type->name, word, word_len, ret, cleanup);
+
+ /* set module */
+ type->pmod = PARSER_CUR_PMOD(ctx);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_BASE:
+ LY_CHECK_RET(parse_text_fields(ctx, LY_STMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts));
+ type->flags |= LYS_SET_BASE;
+ break;
+ case LY_STMT_BIT:
+ LY_CHECK_RET(parse_type_enum(ctx, kw, &type->bits));
+ type->flags |= LYS_SET_BIT;
+ break;
+ case LY_STMT_ENUM:
+ LY_CHECK_RET(parse_type_enum(ctx, kw, &type->enums));
+ type->flags |= LYS_SET_ENUM;
+ break;
+ case LY_STMT_FRACTION_DIGITS:
+ LY_CHECK_RET(parse_type_fracdigits(ctx, type));
+ type->flags |= LYS_SET_FRDIGITS;
+ break;
+ case LY_STMT_LENGTH:
+ if (type->length) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(kw));
+ return LY_EVALID;
+ }
+ type->length = calloc(1, sizeof *type->length);
+ LY_CHECK_ERR_RET(!type->length, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+
+ LY_CHECK_RET(parse_restr(ctx, kw, type->length));
+ type->flags |= LYS_SET_LENGTH;
+ break;
+ case LY_STMT_PATH:
+ if (type->path) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(LY_STMT_PATH));
+ return LY_EVALID;
+ }
+
+ /* Usually, in the parser_yang.c, the result of the parsing is stored directly in the
+ * corresponding structure, so in case of failure, the lysp_module_free function will take
+ * care of removing the parsed value from the dictionary. But in this case, it is not possible
+ * to rely on lysp_module_free because the result of the parsing is stored in a local variable.
+ */
+ LY_CHECK_ERR_RET(ret = parse_text_field(ctx, type, LY_STMT_PATH, 0, &str_path, Y_STR_ARG, &type->exts),
+ lydict_remove(PARSER_CTX(ctx), str_path), ret);
+ ret = ly_path_parse(PARSER_CTX(ctx), NULL, str_path, 0, 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &type->path);
+ /* Moreover, even if successful, the string is removed from the dictionary. */
+ lydict_remove(PARSER_CTX(ctx), str_path);
+ LY_CHECK_RET(ret);
+ type->flags |= LYS_SET_PATH;
+ break;
+ case LY_STMT_PATTERN:
+ LY_CHECK_RET(parse_type_pattern(ctx, &type->patterns));
+ type->flags |= LYS_SET_PATTERN;
+ break;
+ case LY_STMT_RANGE:
+ if (type->range) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(kw));
+ return LY_EVALID;
+ }
+ type->range = calloc(1, sizeof *type->range);
+ LY_CHECK_ERR_RET(!type->range, LOGMEM(PARSER_CTX(ctx)), LY_EMEM);
+
+ LY_CHECK_RET(parse_restr(ctx, kw, type->range));
+ type->flags |= LYS_SET_RANGE;
+ break;
+ case LY_STMT_REQUIRE_INSTANCE:
+ LY_CHECK_RET(parse_type_reqinstance(ctx, type));
+ /* LYS_SET_REQINST checked and set inside parse_type_reqinstance() */
+ break;
+ case LY_STMT_TYPE:
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), type->types, nest_type, LY_EMEM);
+ LY_CHECK_RET(parse_type(ctx, nest_type));
+ type->flags |= LYS_SET_TYPE;
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, type, LY_STMT_TYPE, 0, &type->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "type");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, type->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the leaf statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_leaf(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_leaf *leaf;
+
+ /* create new leaf structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, leaf, next, LY_EMEM);
+ leaf->nodetype = LYS_LEAF;
+ leaf->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, leaf->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &leaf->flags, &leaf->exts));
+ break;
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(parse_text_field(ctx, &leaf->dflt, LY_STMT_DEFAULT, 0, &leaf->dflt.str, Y_STR_ARG, &leaf->exts));
+ leaf->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, leaf->dsc, LY_STMT_DESCRIPTION, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &leaf->iffeatures, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(parse_mandatory(ctx, &leaf->flags, &leaf->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(parse_restrs(ctx, kw, &leaf->musts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, leaf->ref, LY_STMT_REFERENCE, 0, &leaf->ref, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &leaf->flags, &leaf->exts));
+ break;
+ case LY_STMT_TYPE:
+ LY_CHECK_RET(parse_type(ctx, &leaf->type));
+ break;
+ case LY_STMT_UNITS:
+ LY_CHECK_RET(parse_text_field(ctx, leaf->units, LY_STMT_UNITS, 0, &leaf->units, Y_STR_ARG, &leaf->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &leaf->when));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, leaf, LY_STMT_LEAF, 0, &leaf->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "leaf");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, leaf->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!leaf->type.name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "leaf");
+ return LY_EVALID;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the max-elements statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] max Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word, *ptr;
+ size_t word_len;
+ unsigned long long num;
+ enum ly_stmt kw;
+
+ if (*flags & LYS_SET_MAX) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "max-elements");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_MAX;
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (ly_strncmp("unbounded", word, word_len)) {
+ errno = 0;
+ num = strtoull(word, &ptr, LY_BASE_DEC);
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - word) != word_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ *max = num;
+ } else {
+ /* unbounded */
+ *max = 0;
+ }
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, max, LY_STMT_MAX_ELEMENTS, 0, exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the min-elements statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] min Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word, *ptr;
+ size_t word_len;
+ unsigned long long num;
+ enum ly_stmt kw;
+
+ if (*flags & LYS_SET_MIN) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "min-elements");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_MIN;
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ errno = 0;
+ num = strtoull(word, &ptr, LY_BASE_DEC);
+ /* we have not parsed the whole argument */
+ if ((size_t)(ptr - word) != word_len) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "min-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ *min = num;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, min, LY_STMT_MIN_ELEMENTS, 0, exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "min-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the ordered-by statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] llist List or leaf-list to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_orderedby(struct lysp_yang_ctx *ctx, struct lysp_node *llist)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (llist->flags & LYS_ORDBY_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "ordered-by");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if ((word_len == ly_strlen_const("system")) && !strncmp(word, "system", word_len)) {
+ llist->flags |= LYS_ORDBY_SYSTEM;
+ } else if ((word_len == ly_strlen_const("user")) && !strncmp(word, "user", word_len)) {
+ llist->flags |= LYS_ORDBY_USER;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "ordered-by");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, llist, LY_STMT_ORDERED_BY, 0, &llist->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "ordered-by");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+/**
+ * @brief Parse the leaf-list statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_leaflist(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_leaflist *llist;
+
+ /* create new leaf-list structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, llist, next, LY_EMEM);
+ llist->nodetype = LYS_LEAFLIST;
+ llist->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, llist->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_DEFAULT:
+ PARSER_CHECK_STMTVER2_RET(ctx, "default", "leaf-list");
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_DEFAULT, &llist->dflts, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, llist->dsc, LY_STMT_DESCRIPTION, 0, &llist->dsc, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &llist->iffeatures, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ LY_CHECK_RET(parse_maxelements(ctx, &llist->max, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ LY_CHECK_RET(parse_minelements(ctx, &llist->min, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(parse_restrs(ctx, kw, &llist->musts));
+ break;
+ case LY_STMT_ORDERED_BY:
+ LY_CHECK_RET(parse_orderedby(ctx, &llist->node));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, llist->ref, LY_STMT_REFERENCE, 0, &llist->ref, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &llist->flags, &llist->exts));
+ break;
+ case LY_STMT_TYPE:
+ LY_CHECK_RET(parse_type(ctx, &llist->type));
+ break;
+ case LY_STMT_UNITS:
+ LY_CHECK_RET(parse_text_field(ctx, llist->units, LY_STMT_UNITS, 0, &llist->units, Y_STR_ARG, &llist->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &llist->when));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, llist, LY_STMT_LEAF_LIST, 0, &llist->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "llist");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, llist->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!llist->type.name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "leaf-list");
+ return LY_EVALID;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the refine statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] refines Refines to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_refine(struct lysp_yang_ctx *ctx, struct lysp_refine **refines)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_refine *rf;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *refines, rf, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+ CHECK_NONEMPTY(ctx, word_len, "refine");
+ INSERT_WORD_GOTO(ctx, buf, rf->nodeid, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_DEFAULT, &rf->dflts, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, rf->dsc, LY_STMT_DESCRIPTION, 0, &rf->dsc, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "if-feature", "refine");
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &rf->iffeatures, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ LY_CHECK_RET(parse_maxelements(ctx, &rf->max, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ LY_CHECK_RET(parse_minelements(ctx, &rf->min, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(parse_restrs(ctx, kw, &rf->musts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(parse_mandatory(ctx, &rf->flags, &rf->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, rf->ref, LY_STMT_REFERENCE, 0, &rf->ref, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_PRESENCE:
+ LY_CHECK_RET(parse_text_field(ctx, rf->presence, LY_STMT_PRESENCE, 0, &rf->presence, Y_STR_ARG, &rf->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, rf, LY_STMT_REFINE, 0, &rf->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "refine");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, rf->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the typedef statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] typedefs Typedefs to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_typedef(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_tpdf **typedefs)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_tpdf *tpdf;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *typedefs, tpdf, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, tpdf->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(parse_text_field(ctx, &tpdf->dflt, LY_STMT_DEFAULT, 0, &tpdf->dflt.str, Y_STR_ARG, &tpdf->exts));
+ tpdf->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, tpdf->dsc, LY_STMT_DESCRIPTION, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, tpdf->ref, LY_STMT_REFERENCE, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &tpdf->flags, &tpdf->exts));
+ break;
+ case LY_STMT_TYPE:
+ LY_CHECK_RET(parse_type(ctx, &tpdf->type));
+ break;
+ case LY_STMT_UNITS:
+ LY_CHECK_RET(parse_text_field(ctx, tpdf->units, LY_STMT_UNITS, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, tpdf, LY_STMT_TYPEDEF, 0, &tpdf->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "typedef");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, tpdf->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!tpdf->type.name) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "typedef");
+ return LY_EVALID;
+ }
+
+ /* 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));
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the input or output statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] kw Type of this particular keyword
+ * @param[in,out] inout_p Input/output pointer to write to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_inout(struct lysp_yang_ctx *ctx, enum ly_stmt inout_kw, struct lysp_node *parent,
+ struct lysp_node_action_inout *inout_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ ly_bool input = &((struct lysp_node_action *)parent)->input == inout_p ? 1 : 0;
+
+ if (inout_p->nodetype) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(inout_kw));
+ return LY_EVALID;
+ }
+
+ /* initiate structure */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), input ? "input" : "output", 0, &inout_p->name));
+ inout_p->nodetype = input ? LYS_INPUT : LYS_OUTPUT;
+ inout_p->parent = parent;
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", lyplg_ext_stmt2str(inout_kw));
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, (struct lysp_node *)inout_p, &inout_p->child));
+ break;
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node *)inout_p, &inout_p->typedefs));
+ break;
+ case LY_STMT_MUST:
+ PARSER_CHECK_STMTVER2_RET(ctx, "must", lyplg_ext_stmt2str(inout_kw));
+ LY_CHECK_RET(parse_restrs(ctx, kw, &inout_p->musts));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, (struct lysp_node *)inout_p, &inout_p->groupings));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, inout_p, inout_kw, 0, &inout_p->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(inout_kw));
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, inout_p->exts, ret, cleanup);
+ }
+
+ if (!inout_p->child) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "data-def-stmt", lyplg_ext_stmt2str(inout_kw));
+ return LY_EVALID;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the action statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] actions Actions to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_action(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_action **actions)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_action *act;
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), actions, act, next, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, act->name, word, word_len, ret, cleanup);
+ act->nodetype = parent ? LYS_ACTION : LYS_RPC;
+ act->parent = parent;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, act->dsc, LY_STMT_DESCRIPTION, 0, &act->dsc, Y_STR_ARG, &act->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &act->iffeatures, Y_STR_ARG, &act->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, act->ref, LY_STMT_REFERENCE, 0, &act->ref, Y_STR_ARG, &act->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &act->flags, &act->exts));
+ break;
+
+ case LY_STMT_INPUT:
+ LY_CHECK_RET(parse_inout(ctx, kw, (struct lysp_node *)act, &act->input));
+ break;
+ case LY_STMT_OUTPUT:
+ LY_CHECK_RET(parse_inout(ctx, kw, (struct lysp_node *)act, &act->output));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node *)act, &act->typedefs));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, (struct lysp_node *)act, &act->groupings));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, act, parent ? LY_STMT_ACTION : LY_STMT_RPC, 0, &act->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), parent ? "action" : "rpc");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, act->exts, ret, cleanup);
+ }
+
+ /* always initialize inout, they are technically present (needed for later deviations/refines) */
+ if (!act->input.nodetype) {
+ act->input.nodetype = LYS_INPUT;
+ act->input.parent = (struct lysp_node *)act;
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), "input", 0, &act->input.name));
+ }
+ if (!act->output.nodetype) {
+ act->output.nodetype = LYS_OUTPUT;
+ act->output.parent = (struct lysp_node *)act;
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), "output", 0, &act->output.name));
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the notification statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] notifs Notifications to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_notif(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_notif **notifs)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_notif *notif;
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), notifs, notif, next, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, notif->name, word, word_len, ret, cleanup);
+ notif->nodetype = LYS_NOTIF;
+ notif->parent = parent;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, notif->dsc, LY_STMT_DESCRIPTION, 0, &notif->dsc, Y_STR_ARG, &notif->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &notif->iffeatures, Y_STR_ARG, &notif->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, notif->ref, LY_STMT_REFERENCE, 0, &notif->ref, Y_STR_ARG, &notif->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &notif->flags, &notif->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "notification");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)notif, &notif->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)notif, &notif->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)notif, &notif->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)notif, &notif->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)notif, &notif->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)notif, &notif->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, (struct lysp_node *)notif, &notif->child));
+ break;
+
+ case LY_STMT_MUST:
+ PARSER_CHECK_STMTVER2_RET(ctx, "must", "notification");
+ LY_CHECK_RET(parse_restrs(ctx, kw, &notif->musts));
+ break;
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node *)notif, &notif->typedefs));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, (struct lysp_node *)notif, &notif->groupings));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, notif, LY_STMT_NOTIFICATION, 0, &notif->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "notification");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, notif->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the grouping statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] groupings Groupings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_grouping(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_grp **groupings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_grp *grp;
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), groupings, grp, next, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, grp->name, word, word_len, ret, cleanup);
+ grp->nodetype = LYS_GROUPING;
+ grp->parent = parent;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, grp->dsc, LY_STMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, grp->ref, LY_STMT_REFERENCE, 0, &grp->ref, Y_STR_ARG, &grp->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &grp->flags, &grp->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "grouping");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, &grp->node, &grp->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, &grp->node, &grp->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, &grp->node, &grp->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, &grp->node, &grp->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, &grp->node, &grp->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, &grp->node, &grp->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, &grp->node, &grp->child));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, &grp->node, &grp->typedefs));
+ break;
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "grouping");
+ LY_CHECK_RET(parse_action(ctx, &grp->node, &grp->actions));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, &grp->node, &grp->groupings));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "grouping");
+ LY_CHECK_RET(parse_notif(ctx, &grp->node, &grp->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, grp, LY_STMT_GROUPING, 0, &grp->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "grouping");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, grp->exts, ret, cleanup);
+ }
+
+ /* 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));
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the augment statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] augments Augments to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_augment(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_augment **augments)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_augment *aug;
+
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), augments, aug, next, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+ CHECK_NONEMPTY(ctx, word_len, "augment");
+ INSERT_WORD_GOTO(ctx, buf, aug->nodeid, word, word_len, ret, cleanup);
+ aug->nodetype = LYS_AUGMENT;
+ aug->parent = parent;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, aug->dsc, LY_STMT_DESCRIPTION, 0, &aug->dsc, Y_STR_ARG, &aug->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &aug->iffeatures, Y_STR_ARG, &aug->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, aug->ref, LY_STMT_REFERENCE, 0, &aug->ref, Y_STR_ARG, &aug->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &aug->flags, &aug->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &aug->when));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "augment");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_CASE:
+ LY_CHECK_RET(parse_case(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, (struct lysp_node *)aug, &aug->child));
+ break;
+
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "augment");
+ LY_CHECK_RET(parse_action(ctx, (struct lysp_node *)aug, &aug->actions));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "augment");
+ LY_CHECK_RET(parse_notif(ctx, (struct lysp_node *)aug, &aug->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, aug, LY_STMT_AUGMENT, 0, &aug->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "augment");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, aug->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the uses statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_uses(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_uses *uses;
+
+ /* create uses structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, uses, next, LY_EMEM);
+ uses->nodetype = LYS_USES;
+ uses->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, uses->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, uses->dsc, LY_STMT_DESCRIPTION, 0, &uses->dsc, Y_STR_ARG, &uses->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &uses->iffeatures, Y_STR_ARG, &uses->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, uses->ref, LY_STMT_REFERENCE, 0, &uses->ref, Y_STR_ARG, &uses->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &uses->flags, &uses->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &uses->when));
+ break;
+
+ case LY_STMT_REFINE:
+ LY_CHECK_RET(parse_refine(ctx, &uses->refines));
+ break;
+ case LY_STMT_AUGMENT:
+ LY_CHECK_RET(parse_augment(ctx, (struct lysp_node *)uses, &uses->augments));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, uses, LY_STMT_USES, 0, &uses->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "uses");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, uses->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the case statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_case(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_case *cas;
+
+ /* create new case structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, cas, next, LY_EMEM);
+ cas->nodetype = LYS_CASE;
+ cas->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, cas->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, cas->dsc, LY_STMT_DESCRIPTION, 0, &cas->dsc, Y_STR_ARG, &cas->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &cas->iffeatures, Y_STR_ARG, &cas->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, cas->ref, LY_STMT_REFERENCE, 0, &cas->ref, Y_STR_ARG, &cas->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &cas->flags, &cas->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &cas->when));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "case");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, (struct lysp_node *)cas, &cas->child));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, cas, LY_STMT_CASE, 0, &cas->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "case");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, cas->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the choice statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_choice(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_choice *choice;
+
+ /* create new choice structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, choice, next, LY_EMEM);
+ choice->nodetype = LYS_CHOICE;
+ choice->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, choice->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &choice->flags, &choice->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, choice->dsc, LY_STMT_DESCRIPTION, 0, &choice->dsc, Y_STR_ARG, &choice->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &choice->iffeatures, Y_STR_ARG, &choice->exts));
+ break;
+ case LY_STMT_MANDATORY:
+ LY_CHECK_RET(parse_mandatory(ctx, &choice->flags, &choice->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, choice->ref, LY_STMT_REFERENCE, 0, &choice->ref, Y_STR_ARG, &choice->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &choice->flags, &choice->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &choice->when));
+ break;
+ case LY_STMT_DEFAULT:
+ LY_CHECK_RET(parse_text_field(ctx, &choice->dflt, LY_STMT_DEFAULT, 0, &choice->dflt.str, Y_PREF_IDENTIF_ARG,
+ &choice->exts));
+ choice->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "choice");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_CASE:
+ LY_CHECK_RET(parse_case(ctx, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_CHOICE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "choice", "choice");
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)choice, &choice->child));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, choice, LY_STMT_CHOICE, 0, &choice->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "choice");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, choice->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the container statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_container(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_container *cont;
+
+ /* create new container structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, cont, next, LY_EMEM);
+ cont->nodetype = LYS_CONTAINER;
+ cont->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, cont->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &cont->flags, &cont->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, cont->dsc, LY_STMT_DESCRIPTION, 0, &cont->dsc, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &cont->iffeatures, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, cont->ref, LY_STMT_REFERENCE, 0, &cont->ref, Y_STR_ARG, &cont->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &cont->flags, &cont->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &cont->when));
+ break;
+ case LY_STMT_PRESENCE:
+ LY_CHECK_RET(parse_text_field(ctx, cont->presence, LY_STMT_PRESENCE, 0, &cont->presence, Y_STR_ARG, &cont->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "container");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)cont, &cont->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)cont, &cont->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)cont, &cont->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)cont, &cont->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)cont, &cont->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)cont, &cont->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, (struct lysp_node *)cont, &cont->child));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node *)cont, &cont->typedefs));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(parse_restrs(ctx, kw, &cont->musts));
+ break;
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "container");
+ LY_CHECK_RET(parse_action(ctx, (struct lysp_node *)cont, &cont->actions));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, (struct lysp_node *)cont, &cont->groupings));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "container");
+ LY_CHECK_RET(parse_notif(ctx, (struct lysp_node *)cont, &cont->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, cont, LY_STMT_CONTAINER, 0, &cont->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "container");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, cont->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the list statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] siblings Siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_list(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_node_list *list;
+
+ /* create new list structure */
+ LY_LIST_NEW_RET(PARSER_CTX(ctx), siblings, list, next, LY_EMEM);
+ list->nodetype = LYS_LIST;
+ list->parent = parent;
+
+ /* get name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, list->name, word, word_len, ret, cleanup);
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ LY_CHECK_RET(parse_config(ctx, &list->flags, &list->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, list->dsc, LY_STMT_DESCRIPTION, 0, &list->dsc, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &list->iffeatures, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, list->ref, LY_STMT_REFERENCE, 0, &list->ref, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &list->flags, &list->exts));
+ break;
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(parse_when(ctx, &list->when));
+ break;
+ case LY_STMT_KEY:
+ LY_CHECK_RET(parse_text_field(ctx, list, LY_STMT_KEY, 0, &list->key, Y_STR_ARG, &list->exts));
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ LY_CHECK_RET(parse_maxelements(ctx, &list->max, &list->flags, &list->exts));
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ LY_CHECK_RET(parse_minelements(ctx, &list->min, &list->flags, &list->exts));
+ break;
+ case LY_STMT_ORDERED_BY:
+ LY_CHECK_RET(parse_orderedby(ctx, &list->node));
+ break;
+ case LY_STMT_UNIQUE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_UNIQUE, &list->uniques, Y_STR_ARG, &list->exts));
+ break;
+
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "list");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, (struct lysp_node *)list, &list->child));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, (struct lysp_node *)list, &list->child));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, (struct lysp_node *)list, &list->child));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, (struct lysp_node *)list, &list->child));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, (struct lysp_node *)list, &list->child));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, (struct lysp_node *)list, &list->child));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, (struct lysp_node *)list, &list->child));
+ break;
+
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node *)list, &list->typedefs));
+ break;
+ case LY_STMT_MUST:
+ LY_CHECK_RET(parse_restrs(ctx, kw, &list->musts));
+ break;
+ case LY_STMT_ACTION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "action", "list");
+ LY_CHECK_RET(parse_action(ctx, (struct lysp_node *)list, &list->actions));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, (struct lysp_node *)list, &list->groupings));
+ break;
+ case LY_STMT_NOTIFICATION:
+ PARSER_CHECK_STMTVER2_RET(ctx, "notification", "list");
+ LY_CHECK_RET(parse_notif(ctx, (struct lysp_node *)list, &list->notifs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, list, LY_STMT_LIST, 0, &list->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "list");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, list->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the yin-element statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] ext Extension to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_yinelement(struct lysp_yang_ctx *ctx, struct lysp_ext *ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (ext->flags & LYS_YINELEM_MASK) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "yin-element");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
+
+ if ((word_len == ly_strlen_const("true")) && !strncmp(word, "true", word_len)) {
+ ext->flags |= LYS_YINELEM_TRUE;
+ } else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
+ ext->flags |= LYS_YINELEM_FALSE;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yin-element");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, ext, LY_STMT_YIN_ELEMENT, 0, &ext->exts));
+ LY_CHECK_RET(ret);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "yin-element");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the argument statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] ext Extension to fill.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_argument(struct lysp_yang_ctx *ctx, struct lysp_ext *ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+
+ if (ext->argname) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "argument");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, ext->argname, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_YIN_ELEMENT:
+ LY_CHECK_RET(parse_yinelement(ctx, ext));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, ext, LY_STMT_ARGUMENT, 0, &ext->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "argument");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, NULL, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the extension statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] extensions Extensions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+parse_extension(struct lysp_yang_ctx *ctx, struct lysp_ext **extensions)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_ext *ex;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *extensions, ex, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, ex->name, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, ex->dsc, LY_STMT_DESCRIPTION, 0, &ex->dsc, Y_STR_ARG, &ex->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, ex->ref, LY_STMT_REFERENCE, 0, &ex->ref, Y_STR_ARG, &ex->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &ex->flags, &ex->exts));
+ break;
+ case LY_STMT_ARGUMENT:
+ LY_CHECK_RET(parse_argument(ctx, ex));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, ex, LY_STMT_EXTENSION, 0, &ex->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "extension");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, ex->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the deviate statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] deviates Deviates to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_deviate(struct lysp_yang_ctx *ctx, struct lysp_deviate **deviates)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *word;
+ size_t word_len, dev_mod;
+ enum ly_stmt kw;
+ struct lysf_ctx fctx = {.ctx = PARSER_CTX(ctx)};
+ struct lysp_deviate *d = NULL;
+ struct lysp_deviate_add *d_add = NULL;
+ struct lysp_deviate_rpl *d_rpl = NULL;
+ struct lysp_deviate_del *d_del = NULL;
+ const char **d_units = NULL;
+ struct lysp_qname **d_uniques = NULL, **d_dflts = NULL;
+ struct lysp_restr **d_musts = NULL;
+ uint16_t *d_flags = 0;
+ uint32_t *d_min = 0, *d_max = 0;
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+
+ if ((word_len == ly_strlen_const("not-supported")) && !strncmp(word, "not-supported", word_len)) {
+ dev_mod = LYS_DEV_NOT_SUPPORTED;
+ } else if ((word_len == ly_strlen_const("add")) && !strncmp(word, "add", word_len)) {
+ dev_mod = LYS_DEV_ADD;
+ } else if ((word_len == ly_strlen_const("replace")) && !strncmp(word, "replace", word_len)) {
+ dev_mod = LYS_DEV_REPLACE;
+ } else if ((word_len == ly_strlen_const("delete")) && !strncmp(word, "delete", word_len)) {
+ dev_mod = LYS_DEV_DELETE;
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "deviate");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* create structure */
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ d = calloc(1, sizeof *d);
+ LY_CHECK_ERR_GOTO(!d, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+ break;
+ case LYS_DEV_ADD:
+ d_add = calloc(1, sizeof *d_add);
+ LY_CHECK_ERR_GOTO(!d_add, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+ d = (struct lysp_deviate *)d_add;
+ d_units = &d_add->units;
+ d_uniques = &d_add->uniques;
+ d_dflts = &d_add->dflts;
+ d_musts = &d_add->musts;
+ d_flags = &d_add->flags;
+ d_min = &d_add->min;
+ d_max = &d_add->max;
+ break;
+ case LYS_DEV_REPLACE:
+ d_rpl = calloc(1, sizeof *d_rpl);
+ LY_CHECK_ERR_GOTO(!d_rpl, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+ d = (struct lysp_deviate *)d_rpl;
+ d_units = &d_rpl->units;
+ d_flags = &d_rpl->flags;
+ d_min = &d_rpl->min;
+ d_max = &d_rpl->max;
+ break;
+ case LYS_DEV_DELETE:
+ d_del = calloc(1, sizeof *d_del);
+ LY_CHECK_ERR_GOTO(!d_del, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+ d = (struct lysp_deviate *)d_del;
+ d_units = &d_del->units;
+ d_uniques = &d_del->uniques;
+ d_dflts = &d_del->dflts;
+ d_musts = &d_del->musts;
+ break;
+ default:
+ assert(0);
+ LOGINT(PARSER_CTX(ctx));
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ d->mod = dev_mod;
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_CONFIG:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_DELETE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_config(ctx, d_flags, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_DEFAULT:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ case LYS_DEV_REPLACE:
+ ret = parse_text_field(ctx, &d_rpl->dflt, LY_STMT_DEFAULT, 0, &d_rpl->dflt.str, Y_STR_ARG, &d->exts);
+ LY_CHECK_GOTO(ret, cleanup);
+ d_rpl->dflt.mod = PARSER_CUR_PMOD(ctx);
+ break;
+ default:
+ LY_CHECK_GOTO(ret = parse_qnames(ctx, LY_STMT_DEFAULT, d_dflts, Y_STR_ARG, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_MANDATORY:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_DELETE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_mandatory(ctx, d_flags, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_DELETE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_maxelements(ctx, d_max, d_flags, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_MIN_ELEMENTS:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_DELETE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_minelements(ctx, d_min, d_flags, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_MUST:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_REPLACE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_restrs(ctx, kw, d_musts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_TYPE:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_ADD:
+ case LYS_DEV_DELETE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ if (d_rpl->type) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ d_rpl->type = calloc(1, sizeof *d_rpl->type);
+ LY_CHECK_ERR_GOTO(!d_rpl->type, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = parse_type(ctx, d_rpl->type), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_UNIQUE:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_REPLACE:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_qnames(ctx, LY_STMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_UNITS:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ default:
+ LY_CHECK_GOTO(ret = parse_text_field(ctx, *d_units, LY_STMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts), cleanup);
+ break;
+ }
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, d, LY_STMT_DEVIATE, 0, &d->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "deviate");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, d->exts, ret, cleanup);
+ }
+
+cleanup:
+ free(buf);
+ if (ret) {
+ lysp_deviate_free(&fctx, d);
+ free(d);
+ } else {
+ /* insert into siblings */
+ LY_LIST_INSERT(deviates, d, next);
+ }
+ return ret;
+}
+
+/**
+ * @brief Parse the deviation statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] deviations Deviations to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_deviation(struct lysp_yang_ctx *ctx, struct lysp_deviation **deviations)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_deviation *dev;
+ struct lysf_ctx fctx = {.ctx = PARSER_CTX(ctx)};
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *deviations, dev, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
+ CHECK_NONEMPTY(ctx, word_len, "deviation");
+ INSERT_WORD_GOTO(ctx, buf, dev->nodeid, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_GOTO(ret = parse_text_field(ctx, dev->dsc, LY_STMT_DESCRIPTION, 0, &dev->dsc, Y_STR_ARG, &dev->exts), cleanup);
+ break;
+ case LY_STMT_DEVIATE:
+ LY_CHECK_GOTO(ret = parse_deviate(ctx, &dev->deviates), cleanup);
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_GOTO(ret = parse_text_field(ctx, dev->ref, LY_STMT_REFERENCE, 0, &dev->ref, Y_STR_ARG, &dev->exts), cleanup);
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_GOTO(ret = parse_ext(ctx, word, word_len, dev, LY_STMT_DEVIATION, 0, &dev->exts), cleanup);
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "deviation");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, dev->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!dev->deviates) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "deviate", "deviation");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+cleanup:
+ if (ret) {
+ lysp_deviation_free(&fctx, dev);
+ LY_ARRAY_DECREMENT_FREE(*deviations);
+ }
+ return ret;
+}
+
+/**
+ * @brief Parse the feature statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] features Features to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_feature(struct lysp_yang_ctx *ctx, struct lysp_feature **features)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_feature *feat;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *features, feat, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, feat->name, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, feat->dsc, LY_STMT_DESCRIPTION, 0, &feat->dsc, Y_STR_ARG, &feat->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &feat->iffeatures, Y_STR_ARG, &feat->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, feat->ref, LY_STMT_REFERENCE, 0, &feat->ref, Y_STR_ARG, &feat->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &feat->flags, &feat->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, feat, LY_STMT_FEATURE, 0, &feat->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "feature");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, feat->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse the identity statement.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] identities Identities to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_identity(struct lysp_yang_ctx *ctx, struct lysp_ident **identities)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_ident *ident;
+
+ LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *identities, ident, LY_EMEM);
+
+ /* get value */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, ident->name, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+ switch (kw) {
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, ident->dsc, LY_STMT_DESCRIPTION, 0, &ident->dsc, Y_STR_ARG, &ident->exts));
+ break;
+ case LY_STMT_IF_FEATURE:
+ PARSER_CHECK_STMTVER2_RET(ctx, "if-feature", "identity");
+ LY_CHECK_RET(parse_qnames(ctx, LY_STMT_IF_FEATURE, &ident->iffeatures, Y_STR_ARG, &ident->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, ident->ref, LY_STMT_REFERENCE, 0, &ident->ref, Y_STR_ARG, &ident->exts));
+ break;
+ case LY_STMT_STATUS:
+ LY_CHECK_RET(parse_status(ctx, &ident->flags, &ident->exts));
+ break;
+ case LY_STMT_BASE:
+ if (ident->bases && (PARSER_CUR_PMOD(ctx)->version < LYS_VERSION_1_1)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Identity can be derived from multiple base identities only in YANG 1.1 modules");
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(parse_text_fields(ctx, LY_STMT_BASE, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, ident, LY_STMT_IDENTITY, 0, &ident->exts));
+ break;
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "identity");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, ident->exts, ret, cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse module substatements.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in,out] mod Module to write to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_module(struct lysp_yang_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw, prev_kw = 0;
+ enum yang_module_stmt mod_stmt = Y_MOD_MODULE_HEADER;
+ const struct lysp_submodule *dup;
+
+ mod->is_submod = 0;
+
+ /* module name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, mod->mod->name, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+
+#define CHECK_ORDER(SECTION) \
+ if (mod_stmt > SECTION) {\
+ LOGVAL_PARSER(ctx, LY_VCODE_INORD, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(prev_kw)); return LY_EVALID;\
+ } mod_stmt = SECTION
+
+ switch (kw) {
+ /* module header */
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_PREFIX:
+ CHECK_ORDER(Y_MOD_MODULE_HEADER);
+ break;
+ case LY_STMT_YANG_VERSION:
+ CHECK_ORDER(Y_MOD_MODULE_HEADER);
+ break;
+ /* linkage */
+ case LY_STMT_INCLUDE:
+ case LY_STMT_IMPORT:
+ CHECK_ORDER(Y_MOD_LINKAGE);
+ break;
+ /* meta */
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_REFERENCE:
+ CHECK_ORDER(Y_MOD_META);
+ break;
+
+ /* revision */
+ case LY_STMT_REVISION:
+ CHECK_ORDER(Y_MOD_REVISION);
+ break;
+ /* body */
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_AUGMENT:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_RPC:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_USES:
+ mod_stmt = Y_MOD_BODY;
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ /* no place in the statement order defined */
+ break;
+ default:
+ /* error handled in the next switch */
+ break;
+ }
+#undef CHECK_ORDER
+
+ prev_kw = kw;
+ switch (kw) {
+ /* module header */
+ case LY_STMT_YANG_VERSION:
+ LY_CHECK_RET(parse_yangversion(ctx, mod));
+ break;
+ case LY_STMT_NAMESPACE:
+ LY_CHECK_RET(parse_text_field(ctx, mod, LY_STMT_NAMESPACE, 0, &mod->mod->ns, Y_STR_ARG, &mod->exts));
+ break;
+ case LY_STMT_PREFIX:
+ LY_CHECK_RET(parse_text_field(ctx, mod->mod->prefix, LY_STMT_PREFIX, 0, &mod->mod->prefix, Y_IDENTIF_ARG, &mod->exts));
+ break;
+
+ /* linkage */
+ case LY_STMT_INCLUDE:
+ LY_CHECK_RET(parse_include(ctx, mod->mod->name, &mod->includes));
+ break;
+ case LY_STMT_IMPORT:
+ LY_CHECK_RET(parse_import(ctx, mod->mod->prefix, &mod->imports));
+ break;
+
+ /* meta */
+ case LY_STMT_ORGANIZATION:
+ LY_CHECK_RET(parse_text_field(ctx, mod, LY_STMT_ORGANIZATION, 0, &mod->mod->org, Y_STR_ARG, &mod->exts));
+ break;
+ case LY_STMT_CONTACT:
+ LY_CHECK_RET(parse_text_field(ctx, mod, LY_STMT_CONTACT, 0, &mod->mod->contact, Y_STR_ARG, &mod->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, mod->mod->dsc, LY_STMT_DESCRIPTION, 0, &mod->mod->dsc, Y_STR_ARG, &mod->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, mod->mod->ref, LY_STMT_REFERENCE, 0, &mod->mod->ref, Y_STR_ARG, &mod->exts));
+ break;
+
+ /* revision */
+ case LY_STMT_REVISION:
+ LY_CHECK_RET(parse_revision(ctx, &mod->revs));
+ break;
+
+ /* body */
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "module");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, NULL, &mod->data));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, NULL, &mod->data));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, NULL, &mod->data));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, NULL, &mod->data));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, NULL, &mod->data));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, NULL, &mod->data));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, NULL, &mod->data));
+ break;
+
+ case LY_STMT_AUGMENT:
+ LY_CHECK_RET(parse_augment(ctx, NULL, &mod->augments));
+ break;
+ case LY_STMT_DEVIATION:
+ LY_CHECK_RET(parse_deviation(ctx, &mod->deviations));
+ break;
+ case LY_STMT_EXTENSION:
+ LY_CHECK_RET(parse_extension(ctx, &mod->extensions));
+ break;
+ case LY_STMT_FEATURE:
+ LY_CHECK_RET(parse_feature(ctx, &mod->features));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, NULL, &mod->groupings));
+ break;
+ case LY_STMT_IDENTITY:
+ LY_CHECK_RET(parse_identity(ctx, &mod->identities));
+ break;
+ case LY_STMT_NOTIFICATION:
+ LY_CHECK_RET(parse_notif(ctx, NULL, &mod->notifs));
+ break;
+ case LY_STMT_RPC:
+ LY_CHECK_RET(parse_action(ctx, NULL, &mod->rpcs));
+ break;
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, NULL, &mod->typedefs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, mod, LY_STMT_MODULE, 0, &mod->exts));
+ break;
+
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "module");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, mod->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!mod->mod->ns) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "namespace", "module");
+ return LY_EVALID;
+ } else if (!mod->mod->prefix) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "prefix", "module");
+ return LY_EVALID;
+ }
+
+ /* submodules share the namespace with the module names, so there must not be
+ * a submodule of the same name in the context, no need for revision matching */
+ dup = ly_ctx_get_submodule_latest(PARSER_CTX(ctx), mod->mod->name);
+ if (dup) {
+ LOGVAL_PARSER(ctx, LY_VCODE_NAME2_COL, "module", "submodule", mod->mod->name);
+ return LY_EVALID;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse submodule substatements.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[out] submod Parsed submodule structure.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_submodule(struct lysp_yang_ctx *ctx, struct lysp_submodule *submod)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ size_t word_len;
+ enum ly_stmt kw, prev_kw = 0;
+ enum yang_module_stmt mod_stmt = Y_MOD_MODULE_HEADER;
+ const struct lysp_submodule *dup;
+
+ submod->is_submod = 1;
+
+ /* submodule name */
+ LY_CHECK_RET(get_argument(ctx, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
+ INSERT_WORD_GOTO(ctx, buf, submod->name, word, word_len, ret, cleanup);
+
+ YANG_READ_SUBSTMT_FOR_GOTO(ctx, kw, word, word_len, ret, cleanup) {
+
+#define CHECK_ORDER(SECTION) \
+ if (mod_stmt > SECTION) {LOGVAL_PARSER(ctx, LY_VCODE_INORD, lyplg_ext_stmt2str(kw), lyplg_ext_stmt2str(prev_kw)); return LY_EVALID;}mod_stmt = SECTION
+
+ switch (kw) {
+ /* module header */
+ case LY_STMT_BELONGS_TO:
+ CHECK_ORDER(Y_MOD_MODULE_HEADER);
+ break;
+ case LY_STMT_YANG_VERSION:
+ CHECK_ORDER(Y_MOD_MODULE_HEADER);
+ break;
+ /* linkage */
+ case LY_STMT_INCLUDE:
+ case LY_STMT_IMPORT:
+ CHECK_ORDER(Y_MOD_LINKAGE);
+ break;
+ /* meta */
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_REFERENCE:
+ CHECK_ORDER(Y_MOD_META);
+ break;
+
+ /* revision */
+ case LY_STMT_REVISION:
+ CHECK_ORDER(Y_MOD_REVISION);
+ break;
+ /* body */
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_AUGMENT:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_RPC:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_USES:
+ mod_stmt = Y_MOD_BODY;
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ /* no place in the statement order defined */
+ break;
+ default:
+ /* error handled in the next switch */
+ break;
+ }
+#undef CHECK_ORDER
+
+ prev_kw = kw;
+ switch (kw) {
+ /* module header */
+ case LY_STMT_YANG_VERSION:
+ LY_CHECK_RET(parse_yangversion(ctx, (struct lysp_module *)submod));
+ break;
+ case LY_STMT_BELONGS_TO:
+ LY_CHECK_RET(parse_belongsto(ctx, submod));
+ break;
+
+ /* linkage */
+ case LY_STMT_INCLUDE:
+ if (submod->version == LYS_VERSION_1_1) {
+ LOGWRN(PARSER_CTX(ctx), "YANG version 1.1 expects all includes in main module, includes in submodules (%s) are not necessary.",
+ submod->name);
+ }
+ LY_CHECK_RET(parse_include(ctx, submod->name, &submod->includes));
+ break;
+ case LY_STMT_IMPORT:
+ LY_CHECK_RET(parse_import(ctx, submod->prefix, &submod->imports));
+ break;
+
+ /* meta */
+ case LY_STMT_ORGANIZATION:
+ LY_CHECK_RET(parse_text_field(ctx, submod, LY_STMT_ORGANIZATION, 0, &submod->org, Y_STR_ARG, &submod->exts));
+ break;
+ case LY_STMT_CONTACT:
+ LY_CHECK_RET(parse_text_field(ctx, submod, LY_STMT_CONTACT, 0, &submod->contact, Y_STR_ARG, &submod->exts));
+ break;
+ case LY_STMT_DESCRIPTION:
+ LY_CHECK_RET(parse_text_field(ctx, submod->dsc, LY_STMT_DESCRIPTION, 0, &submod->dsc, Y_STR_ARG, &submod->exts));
+ break;
+ case LY_STMT_REFERENCE:
+ LY_CHECK_RET(parse_text_field(ctx, submod->ref, LY_STMT_REFERENCE, 0, &submod->ref, Y_STR_ARG, &submod->exts));
+ break;
+
+ /* revision */
+ case LY_STMT_REVISION:
+ LY_CHECK_RET(parse_revision(ctx, &submod->revs));
+ break;
+
+ /* body */
+ case LY_STMT_ANYDATA:
+ PARSER_CHECK_STMTVER2_RET(ctx, "anydata", "submodule");
+ /* fall through */
+ case LY_STMT_ANYXML:
+ LY_CHECK_RET(parse_any(ctx, kw, NULL, &submod->data));
+ break;
+ case LY_STMT_CHOICE:
+ LY_CHECK_RET(parse_choice(ctx, NULL, &submod->data));
+ break;
+ case LY_STMT_CONTAINER:
+ LY_CHECK_RET(parse_container(ctx, NULL, &submod->data));
+ break;
+ case LY_STMT_LEAF:
+ LY_CHECK_RET(parse_leaf(ctx, NULL, &submod->data));
+ break;
+ case LY_STMT_LEAF_LIST:
+ LY_CHECK_RET(parse_leaflist(ctx, NULL, &submod->data));
+ break;
+ case LY_STMT_LIST:
+ LY_CHECK_RET(parse_list(ctx, NULL, &submod->data));
+ break;
+ case LY_STMT_USES:
+ LY_CHECK_RET(parse_uses(ctx, NULL, &submod->data));
+ break;
+
+ case LY_STMT_AUGMENT:
+ LY_CHECK_RET(parse_augment(ctx, NULL, &submod->augments));
+ break;
+ case LY_STMT_DEVIATION:
+ LY_CHECK_RET(parse_deviation(ctx, &submod->deviations));
+ break;
+ case LY_STMT_EXTENSION:
+ LY_CHECK_RET(parse_extension(ctx, &submod->extensions));
+ break;
+ case LY_STMT_FEATURE:
+ LY_CHECK_RET(parse_feature(ctx, &submod->features));
+ break;
+ case LY_STMT_GROUPING:
+ LY_CHECK_RET(parse_grouping(ctx, NULL, &submod->groupings));
+ break;
+ case LY_STMT_IDENTITY:
+ LY_CHECK_RET(parse_identity(ctx, &submod->identities));
+ break;
+ case LY_STMT_NOTIFICATION:
+ LY_CHECK_RET(parse_notif(ctx, NULL, &submod->notifs));
+ break;
+ case LY_STMT_RPC:
+ LY_CHECK_RET(parse_action(ctx, NULL, &submod->rpcs));
+ break;
+ case LY_STMT_TYPEDEF:
+ LY_CHECK_RET(parse_typedef(ctx, NULL, &submod->typedefs));
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ LY_CHECK_RET(parse_ext(ctx, word, word_len, submod, LY_STMT_SUBMODULE, 0, &submod->exts));
+ break;
+
+ default:
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, lyplg_ext_stmt2str(kw), "submodule");
+ return LY_EVALID;
+ }
+ YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, submod->exts, ret, cleanup);
+ }
+
+ /* mandatory substatements */
+ if (!submod->prefix) {
+ LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "belongs-to", "submodule");
+ return LY_EVALID;
+ }
+
+ /* submodules share the namespace with the module names, so there must not be
+ * a submodule of the same name in the context, no need for revision matching */
+ dup = ly_ctx_get_submodule_latest(PARSER_CTX(ctx), submod->name);
+ /* main modules may have different revisions */
+ if (dup && strcmp(dup->mod->name, submod->mod->name)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_NAME_COL, "submodules", dup->name);
+ return LY_EVALID;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Skip any redundant characters, namely whitespaces and comments.
+ *
+ * @param[in] ctx Yang parser context.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on invalid comment.
+ */
+static LY_ERR
+skip_redundant_chars(struct lysp_yang_ctx *ctx)
+{
+ /* read some trailing spaces, new lines, or comments */
+ while (ctx->in->current[0]) {
+ if (!strncmp(ctx->in->current, "//", 2)) {
+ /* one-line comment */
+ ly_in_skip(ctx->in, 2);
+ LY_CHECK_RET(skip_comment(ctx, 1));
+ } else if (!strncmp(ctx->in->current, "/*", 2)) {
+ /* block comment */
+ ly_in_skip(ctx->in, 2);
+ LY_CHECK_RET(skip_comment(ctx, 2));
+ } else if (isspace(ctx->in->current[0])) {
+ /* whitespace */
+ if (ctx->in->current[0] == '\n') {
+ LY_IN_NEW_LINE(ctx->in);
+ }
+ ly_in_skip(ctx->in, 1);
+ } else {
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+yang_parse_submodule(struct lysp_yang_ctx **context, struct ly_ctx *ly_ctx, struct lysp_ctx *main_ctx,
+ struct ly_in *in, struct lysp_submodule **submod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_submodule *mod_p = NULL;
+ struct lysf_ctx fctx = {.ctx = ly_ctx};
+
+ assert(context && ly_ctx && main_ctx && in && submod);
+
+ /* create context */
+ *context = calloc(1, sizeof **context);
+ LY_CHECK_ERR_RET(!(*context), LOGMEM(ly_ctx), LY_EMEM);
+ (*context)->format = LYS_IN_YANG;
+ (*context)->in = in;
+ (*context)->main_ctx = main_ctx;
+
+ mod_p = calloc(1, sizeof *mod_p);
+ LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(ly_ctx); ret = LY_EMEM, cleanup);
+ mod_p->mod = PARSER_CUR_PMOD(main_ctx)->mod;
+ mod_p->parsing = 1;
+
+ /* use main context parsed mods adding the current one */
+ (*context)->parsed_mods = main_ctx->parsed_mods;
+ ly_set_add((*context)->parsed_mods, mod_p, 1, NULL);
+
+ LOG_LOCSET(NULL, NULL, NULL, in);
+
+ /* skip redundant but valid characters at the beginning */
+ ret = skip_redundant_chars(*context);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* "module"/"submodule" */
+ ret = get_keyword(*context, &kw, &word, &word_len);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (kw == LY_STMT_MODULE) {
+ LOGERR(ly_ctx, LY_EDENIED, "Input data contains module in situation when a submodule is expected.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (kw != LY_STMT_SUBMODULE) {
+ LOGVAL_PARSER(*context, LY_VCODE_MOD_SUBOMD, lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* substatements */
+ ret = parse_submodule(*context, mod_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* skip redundant but valid characters at the end */
+ ret = skip_redundant_chars(*context);
+ LY_CHECK_GOTO(ret, cleanup);
+ if (in->current[0]) {
+ LOGVAL_PARSER(*context, LY_VCODE_TRAILING_SUBMOD, 15, in->current, strlen(in->current) > 15 ? "..." : "");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ mod_p->parsing = 0;
+ *submod = mod_p;
+
+cleanup:
+ LOG_LOCBACK(0, 0, 0, 1);
+ if (ret) {
+ lysp_module_free(&fctx, (struct lysp_module *)mod_p);
+ lysp_yang_ctx_free(*context);
+ *context = NULL;
+ }
+
+ return ret;
+}
+
+LY_ERR
+yang_parse_module(struct lysp_yang_ctx **context, struct ly_in *in, struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *word;
+ size_t word_len;
+ enum ly_stmt kw;
+ struct lysp_module *mod_p = NULL;
+ struct lysf_ctx fctx = {.ctx = mod->ctx};
+
+ /* create context */
+ *context = calloc(1, sizeof **context);
+ LY_CHECK_ERR_RET(!(*context), LOGMEM(mod->ctx), LY_EMEM);
+ (*context)->format = LYS_IN_YANG;
+ LY_CHECK_ERR_RET(ly_set_new(&(*context)->parsed_mods), free(*context); LOGMEM(mod->ctx), LY_EMEM);
+ (*context)->in = in;
+ (*context)->main_ctx = (struct lysp_ctx *)(*context);
+
+ mod_p = calloc(1, sizeof *mod_p);
+ LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(mod->ctx), cleanup);
+ mod_p->mod = mod;
+ ly_set_add((*context)->parsed_mods, mod_p, 1, NULL);
+
+ LOG_LOCSET(NULL, NULL, NULL, in);
+
+ /* skip redundant but valid characters at the beginning */
+ ret = skip_redundant_chars(*context);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* "module"/"submodule" */
+ ret = get_keyword(*context, &kw, &word, &word_len);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (kw == LY_STMT_SUBMODULE) {
+ LOGERR(mod->ctx, LY_EDENIED, "Input data contains submodule which cannot be parsed directly without its main module.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (kw != LY_STMT_MODULE) {
+ LOGVAL_PARSER((*context), LY_VCODE_MOD_SUBOMD, lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* substatements */
+ ret = parse_module(*context, mod_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* skip redundant but valid characters at the end */
+ ret = skip_redundant_chars(*context);
+ LY_CHECK_GOTO(ret, cleanup);
+ if (in->current[0]) {
+ LOGVAL_PARSER(*context, LY_VCODE_TRAILING_MOD, 15, in->current, strlen(in->current) > 15 ? "..." : "");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ mod->parsed = mod_p;
+
+cleanup:
+ LOG_LOCBACK(0, 0, 0, 1);
+ if (ret) {
+ lysp_module_free(&fctx, mod_p);
+ lysp_yang_ctx_free(*context);
+ *context = NULL;
+ }
+
+ return ret;
+}
diff --git a/src/parser_yin.c b/src/parser_yin.c
new file mode 100644
index 0000000..fa44968
--- /dev/null
+++ b/src/parser_yin.c
@@ -0,0 +1,4012 @@
+/**
+ * @file parser_yin.c
+ * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YIN parser.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "in.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_internal.h"
+#include "parser_schema.h"
+#include "path.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+
+struct lys_glob_unres;
+
+/**
+ * @brief check if given string is URI of yin namespace.
+ *
+ * @param ns Namespace URI to check.
+ *
+ * @return true if ns equals YIN_NS_URI false otherwise.
+ */
+#define IS_YIN_NS(ns) (strcmp(ns, YIN_NS_URI) == 0)
+
+enum yin_argument {
+ YIN_ARG_UNKNOWN = 0, /**< parsed argument can not be matched with any supported yin argument keyword */
+ YIN_ARG_NAME, /**< argument name */
+ YIN_ARG_TARGET_NODE, /**< argument target-node */
+ YIN_ARG_MODULE, /**< argument module */
+ YIN_ARG_VALUE, /**< argument value */
+ YIN_ARG_TEXT, /**< argument text */
+ YIN_ARG_CONDITION, /**< argument condition */
+ YIN_ARG_URI, /**< argument uri */
+ YIN_ARG_DATE, /**< argument data */
+ YIN_ARG_TAG, /**< argument tag */
+ YIN_ARG_NONE /**< empty (special value) */
+};
+
+const char * const yin_attr_list[] = {
+ [YIN_ARG_NAME] = "name",
+ [YIN_ARG_TARGET_NODE] = "target-node",
+ [YIN_ARG_MODULE] = "module",
+ [YIN_ARG_VALUE] = "value",
+ [YIN_ARG_TEXT] = "text",
+ [YIN_ARG_CONDITION] = "condition",
+ [YIN_ARG_URI] = "uri",
+ [YIN_ARG_DATE] = "date",
+ [YIN_ARG_TAG] = "tag",
+ [YIN_ARG_NONE] = "none",
+};
+
+#define yin_attr2str(STMT) yin_attr_list[STMT]
+
+#define VALID_VALS1 " Only valid value is \"%s\"."
+#define VALID_VALS2 " Valid values are \"%s\" and \"%s\"."
+#define VALID_VALS3 " Valid values are \"%s\", \"%s\" and \"%s\"."
+#define VALID_VALS4 " Valid values are \"%s\", \"%s\", \"%s\" and \"%s\"."
+
+/* shortcut to determin if keyword can in general be subelement of deviation regardles of it's type */
+#define isdevsub(kw) (kw == LY_STMT_CONFIG || kw == LY_STMT_DEFAULT || kw == LY_STMT_MANDATORY || \
+ kw == LY_STMT_MAX_ELEMENTS || kw == LY_STMT_MIN_ELEMENTS || \
+ kw == LY_STMT_MUST || kw == LY_STMT_TYPE || kw == LY_STMT_UNIQUE || \
+ kw == LY_STMT_UNITS || kw == LY_STMT_EXTENSION_INSTANCE)
+
+/* flags to set constraints of subelements */
+#define YIN_SUBELEM_MANDATORY 0x01 /**< is set when subelement is mandatory */
+#define YIN_SUBELEM_UNIQUE 0x02 /**< is set when subelement is unique */
+#define YIN_SUBELEM_FIRST 0x04 /**< is set when subelement is actually yang argument mapped to yin element */
+#define YIN_SUBELEM_VER2 0x08 /**< subelemnt is allowed only in modules with version at least 2 (YANG 1.1) */
+
+#define YIN_SUBELEM_PARSED 0x80 /**< is set during parsing when given subelement is encountered for the first
+ time to simply check validity of given constraints */
+
+struct yin_subelement {
+ enum ly_stmt type; /**< type of keyword */
+ void *dest; /**< meta infromation passed to responsible function (mostly information about where parsed
+ subelement should be stored) */
+ uint16_t flags; /**< describes constraints of subelement can be set to YIN_SUBELEM_MANDATORY,
+ YIN_SUBELEM_UNIQUE, YIN_SUBELEM_FIRST, YIN_SUBELEM_VER2, and YIN_SUBELEM_DEFAULT_TEXT */
+};
+
+/* Meta information passed to yin_parse_argument function,
+ holds information about where content of argument element will be stored. */
+struct yin_argument_meta {
+ uint16_t *flags; /**< Argument flags */
+ const char **argument; /**< Argument value */
+};
+
+/**
+ * @brief Meta information passed to functions working with tree_schema,
+ * that require additional information about parent node.
+ */
+struct tree_node_meta {
+ struct lysp_node *parent; /**< parent node */
+ struct lysp_node **nodes; /**< linked list of siblings */
+};
+
+/**
+ * @brief Meta information passed to yin_parse_import function.
+ */
+struct import_meta {
+ const char *prefix; /**< module prefix. */
+ struct lysp_import **imports; /**< imports to add to. */
+};
+
+/**
+ * @brief Meta information passed to yin_parse_include function.
+ */
+struct include_meta {
+ const char *name; /**< Module/submodule name. */
+ struct lysp_include **includes; /**< [Sized array](@ref sizedarrays) of parsed includes to add to. */
+};
+
+/**
+ * @brief Meta information passed to yin_parse_inout function.
+ */
+struct inout_meta {
+ struct lysp_node *parent; /**< Parent node. */
+ struct lysp_node_action_inout *inout_p; /**< inout_p Input/output pointer to write to. */
+};
+
+/**
+ * @brief Meta information passed to yin_parse_minmax function.
+ */
+struct minmax_dev_meta {
+ uint32_t *lim; /**< min/max value to write to. */
+ uint16_t *flags; /**< min/max flags to write to. */
+ struct lysp_ext_instance **exts; /**< extension instances to add to. */
+};
+
+LY_ERR yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info, size_t subelem_info_size,
+ const void *parent, enum ly_stmt parent_stmt, const char **text_content, struct lysp_ext_instance **exts);
+
+/**
+ * @brief Match yang keyword from yin data.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] name Start of keyword name
+ * @param[in] name_len Lenght of keyword name.
+ * @param[in] prefix Start of keyword prefix.
+ * @param[in] prefix_len Lenght of prefix.
+ * @param[in] parent Identification of parent element, use LY_STMT_NONE for elements without parent.
+ * @return yang_keyword values.
+ */
+enum ly_stmt
+yin_match_keyword(struct lysp_yin_ctx *ctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
+ enum ly_stmt parent)
+{
+ const char *start = NULL;
+ enum ly_stmt kw = LY_STMT_NONE;
+ const struct lyxml_ns *ns = NULL;
+ struct ly_in *in;
+
+ if (!name || (name_len == 0)) {
+ return LY_STMT_NONE;
+ }
+
+ ns = lyxml_ns_get(&ctx->xmlctx->ns, prefix, prefix_len);
+ if (ns) {
+ if (!IS_YIN_NS(ns->uri)) {
+ return LY_STMT_EXTENSION_INSTANCE;
+ }
+ } else {
+ /* elements without namespace are automatically unknown */
+ return LY_STMT_NONE;
+ }
+
+ LY_CHECK_RET(ly_in_new_memory(name, &in), LY_STMT_NONE);
+ start = in->current;
+ kw = lysp_match_kw(in, NULL);
+ name = in->current;
+ ly_in_free(in, 0);
+
+ if (name - start == (long)name_len) {
+ /* this is done because of collision in yang statement value and yang argument mapped to yin element value */
+ if ((kw == LY_STMT_VALUE) && (parent == LY_STMT_ERROR_MESSAGE)) {
+ return LY_STMT_ARG_VALUE;
+ }
+ return kw;
+ } else {
+ if (strncmp(start, "text", name_len) == 0) {
+ return LY_STMT_ARG_TEXT;
+ } else {
+ return LY_STMT_NONE;
+ }
+ }
+}
+
+/**
+ * @brief Match argument name.
+ *
+ * @param[in] name String representing name.
+ * @param[in] len Lenght of the name.
+ * @return yin_argument values.
+ */
+enum yin_argument
+yin_match_argument_name(const char *name, size_t len)
+{
+ enum yin_argument arg = YIN_ARG_UNKNOWN;
+ size_t already_read = 0;
+
+ LY_CHECK_RET(len == 0, YIN_ARG_NONE);
+
+#define READ_INC(LEN) already_read += LEN
+#define ARG_SET(STMT) arg=STMT
+#define ARG_CHECK(STR, LEN) (!strncmp((name) + already_read, STR, LEN) && (READ_INC(LEN)))
+
+ switch (*name) {
+ case 'c':
+ READ_INC(1);
+ if (ARG_CHECK("ondition", 8)) {
+ ARG_SET(YIN_ARG_CONDITION);
+ }
+ break;
+
+ case 'd':
+ READ_INC(1);
+ if (ARG_CHECK("ate", 3)) {
+ ARG_SET(YIN_ARG_DATE);
+ }
+ break;
+
+ case 'm':
+ READ_INC(1);
+ if (ARG_CHECK("odule", 5)) {
+ ARG_SET(YIN_ARG_MODULE);
+ }
+ break;
+
+ case 'n':
+ READ_INC(1);
+ if (ARG_CHECK("ame", 3)) {
+ ARG_SET(YIN_ARG_NAME);
+ }
+ break;
+
+ case 't':
+ READ_INC(1);
+ if (ARG_CHECK("a", 1)) {
+ if (ARG_CHECK("g", 1)) {
+ ARG_SET(YIN_ARG_TAG);
+ } else if (ARG_CHECK("rget-node", 9)) {
+ ARG_SET(YIN_ARG_TARGET_NODE);
+ }
+ } else if (ARG_CHECK("ext", 3)) {
+ ARG_SET(YIN_ARG_TEXT);
+ }
+ break;
+
+ case 'u':
+ READ_INC(1);
+ if (ARG_CHECK("ri", 2)) {
+ ARG_SET(YIN_ARG_URI);
+ }
+ break;
+
+ case 'v':
+ READ_INC(1);
+ if (ARG_CHECK("alue", 4)) {
+ ARG_SET(YIN_ARG_VALUE);
+ }
+ break;
+ }
+
+ /* whole argument must be matched */
+ if (already_read != len) {
+ arg = YIN_ARG_UNKNOWN;
+ }
+
+#undef ARG_CHECK
+#undef ARG_SET
+#undef READ_INC
+
+ return arg;
+}
+
+#define IS_NODE_ELEM(kw) (kw == LY_STMT_ANYXML || kw == LY_STMT_ANYDATA || kw == LY_STMT_LEAF || kw == LY_STMT_LEAF_LIST || \
+ kw == LY_STMT_TYPEDEF || kw == LY_STMT_USES || kw == LY_STMT_LIST || kw == LY_STMT_NOTIFICATION || \
+ kw == LY_STMT_GROUPING || kw == LY_STMT_CONTAINER || kw == LY_STMT_CASE || kw == LY_STMT_CHOICE || \
+ kw == LY_STMT_ACTION || kw == LY_STMT_RPC || kw == LY_STMT_AUGMENT)
+
+#define HAS_META(kw) (IS_NODE_ELEM(kw) || kw == LY_STMT_IMPORT || kw == LY_STMT_INCLUDE || kw == LY_STMT_INPUT || kw == LY_STMT_OUTPUT)
+
+/**
+ * @brief Free subelems information allocated on heap.
+ *
+ * @param[in] count Size of subelems array.
+ * @param[in] subelems Subelems array to free.
+ */
+static void
+subelems_deallocator(size_t count, struct yin_subelement *subelems)
+{
+ for (size_t i = 0; i < count; ++i) {
+ if (HAS_META(subelems[i].type)) {
+ free(subelems[i].dest);
+ }
+ }
+
+ free(subelems);
+}
+
+/**
+ * @brief Allocate subelems information on heap.
+ *
+ * @param[in] ctx YIN parser context, used for logging.
+ * @param[in] count Number of subelements.
+ * @param[in] parent Parent node if any.
+ * @param[out] result Allocated subelems array.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+subelems_allocator(struct lysp_yin_ctx *ctx, size_t count, struct lysp_node *parent,
+ struct yin_subelement **result, ...)
+{
+ va_list ap;
+
+ *result = calloc(count, sizeof **result);
+ LY_CHECK_GOTO(!(*result), mem_err);
+
+ va_start(ap, result);
+ for (size_t i = 0; i < count; ++i) {
+ /* TYPE */
+ (*result)[i].type = va_arg(ap, enum ly_stmt);
+ /* DEST */
+ if (IS_NODE_ELEM((*result)[i].type)) {
+ struct tree_node_meta *node_meta = NULL;
+
+ node_meta = calloc(1, sizeof *node_meta);
+ LY_CHECK_GOTO(!node_meta, mem_err);
+ node_meta->parent = parent;
+ node_meta->nodes = va_arg(ap, void *);
+ (*result)[i].dest = node_meta;
+ } else if ((*result)[i].type == LY_STMT_IMPORT) {
+ struct import_meta *imp_meta = NULL;
+
+ imp_meta = calloc(1, sizeof *imp_meta);
+ LY_CHECK_GOTO(!imp_meta, mem_err);
+ imp_meta->prefix = va_arg(ap, const char *);
+ imp_meta->imports = va_arg(ap, struct lysp_import **);
+ (*result)[i].dest = imp_meta;
+ } else if ((*result)[i].type == LY_STMT_INCLUDE) {
+ struct include_meta *inc_meta = NULL;
+
+ inc_meta = calloc(1, sizeof *inc_meta);
+ LY_CHECK_GOTO(!inc_meta, mem_err);
+ inc_meta->name = va_arg(ap, const char *);
+ inc_meta->includes = va_arg(ap, struct lysp_include **);
+ (*result)[i].dest = inc_meta;
+ } else if (((*result)[i].type == LY_STMT_INPUT) || ((*result)[i].type == LY_STMT_OUTPUT)) {
+ struct inout_meta *inout_meta = NULL;
+
+ inout_meta = calloc(1, sizeof *inout_meta);
+ LY_CHECK_GOTO(!inout_meta, mem_err);
+ inout_meta->parent = parent;
+ inout_meta->inout_p = va_arg(ap, struct lysp_node_action_inout *);
+ (*result)[i].dest = inout_meta;
+ } else {
+ (*result)[i].dest = va_arg(ap, void *);
+ }
+ /* FLAGS */
+ (*result)[i].flags = va_arg(ap, int);
+ }
+ va_end(ap);
+
+ return LY_SUCCESS;
+
+mem_err:
+ subelems_deallocator(count, *result);
+ LOGMEM(ctx->xmlctx->ctx);
+ return LY_EMEM;
+}
+
+/**
+ * @brief Check that val is valid UTF8 character sequence of val_type.
+ * Doesn't check empty string, only character validity.
+ *
+ * @param[in] ctx Yin parser context for logging.
+ * @param[in] val_type Type of the input string to select method of checking character validity.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_validate_value(struct lysp_yin_ctx *ctx, enum yang_arg val_type)
+{
+ uint8_t prefix = 0;
+ uint32_t c;
+ size_t utf8_char_len, already_read = 0;
+ const char *val;
+
+ assert((ctx->xmlctx->status == LYXML_ELEM_CONTENT) || (ctx->xmlctx->status == LYXML_ATTR_CONTENT));
+
+ val = ctx->xmlctx->value;
+ while (already_read < ctx->xmlctx->value_len) {
+ LY_CHECK_ERR_RET(ly_getutf8((const char **)&val, &c, &utf8_char_len),
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INCHAR, (val)[-utf8_char_len]), LY_EVALID);
+
+ switch (val_type) {
+ case Y_IDENTIF_ARG:
+ LY_CHECK_RET(lysp_check_identifierchar((struct lysp_ctx *)ctx, c, already_read ? 0 : 1, NULL));
+ break;
+ case Y_PREF_IDENTIF_ARG:
+ LY_CHECK_RET(lysp_check_identifierchar((struct lysp_ctx *)ctx, c, already_read ? 0 : 1, &prefix));
+ break;
+ case Y_STR_ARG:
+ case Y_MAYBE_STR_ARG:
+ LY_CHECK_RET(lysp_check_stringchar((struct lysp_ctx *)ctx, c));
+ break;
+ }
+
+ already_read += utf8_char_len;
+ LY_CHECK_ERR_RET(already_read > ctx->xmlctx->value_len, LOGINT(ctx->xmlctx->ctx), LY_EINT);
+ }
+
+ if ((already_read == 0) &&
+ ((val_type == Y_PREF_IDENTIF_ARG) || (val_type == Y_IDENTIF_ARG))) {
+ /* Empty identifier or prefix*/
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YIN, "Empty identifier is not allowed.");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse yin attributes.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] arg_type Type of argument that is expected in parsed element (use YIN_ARG_NONE for elements without
+ * special argument).
+ * @param[out] arg_val Where value of argument should be stored. Can be NULL iff arg_type is specified as YIN_ARG_NONE.
+ * @param[in] val_type Type of expected value of attribute.
+ * @param[in] current_element Identification of current element, used for logging.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_attribute(struct lysp_yin_ctx *ctx, enum yin_argument arg_type, const char **arg_val, enum yang_arg val_type,
+ enum ly_stmt current_element)
+{
+ enum yin_argument arg = YIN_ARG_UNKNOWN;
+ bool found = false;
+
+ /* validation of attributes */
+ while (ctx->xmlctx->status == LYXML_ATTRIBUTE) {
+ /* yin arguments represented as attributes have no namespace, which in this case means no prefix */
+ if (!ctx->xmlctx->prefix) {
+ arg = yin_match_argument_name(ctx->xmlctx->name, ctx->xmlctx->name_len);
+ if (arg == YIN_ARG_NONE) {
+ /* skip it */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ } else if (arg == arg_type) {
+ LY_CHECK_ERR_RET(found, LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_DUP_ATTR,
+ yin_attr2str(arg), lyplg_ext_stmt2str(current_element)), LY_EVALID);
+ found = true;
+
+ /* go to value */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_validate_value(ctx, val_type));
+ INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, *arg_val);
+ LY_CHECK_RET(!(*arg_val), LY_EMEM);
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_ATTR, ctx->xmlctx->name_len,
+ ctx->xmlctx->name, lyplg_ext_stmt2str(current_element));
+ return LY_EVALID;
+ }
+ } else {
+ /* skip it */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+
+ /* next attribute */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+
+ /* anything else than Y_MAYBE_STR_ARG is mandatory */
+ if ((val_type != Y_MAYBE_STR_ARG) && !found) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LYVE_SYNTAX_YIN, "Missing mandatory attribute %s of %s element.",
+ yin_attr2str(arg_type), lyplg_ext_stmt2str(current_element));
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get record with given type.
+ *
+ * @param[in] type Type of wanted record.
+ * @param[in] array_size Size of array.
+ * @param[in] array Searched array.
+ * @return Pointer to desired record on success, NULL if element is not in the array.
+ */
+static struct yin_subelement *
+get_record(enum ly_stmt type, LY_ARRAY_COUNT_TYPE array_size, struct yin_subelement *array)
+{
+ for (LY_ARRAY_COUNT_TYPE u = 0; u < array_size; ++u) {
+ if (array[u].type == type) {
+ return &array[u];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * @brief Helper function to check mandatory constraint of subelement.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] subelem_info Array of information about subelements.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in] current_element Identification of element that is currently being parsed, used for logging.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_check_subelem_mandatory_constraint(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info,
+ signed char subelem_info_size, enum ly_stmt current_element)
+{
+ for (signed char i = 0; i < subelem_info_size; ++i) {
+ /* if there is element that is mandatory and isn't parsed log error and return LY_EVALID */
+ if ((subelem_info[i].flags & YIN_SUBELEM_MANDATORY) && !(subelem_info[i].flags & YIN_SUBELEM_PARSED)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_MAND_SUBELEM,
+ lyplg_ext_stmt2str(subelem_info[i].type), lyplg_ext_stmt2str(current_element));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Helper function to check "first" constraint of subelement.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] subelem_info Array of information about subelements.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in] current_element Identification of element that is currently being parsed, used for logging.
+ * @param[in] exp_first Record in subelem_info array that is expected to be defined as first subelement, used for logging.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_check_subelem_first_constraint(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info,
+ signed char subelem_info_size, enum ly_stmt current_element,
+ struct yin_subelement *exp_first)
+{
+ for (signed char i = 0; i < subelem_info_size; ++i) {
+ if (subelem_info[i].flags & YIN_SUBELEM_PARSED) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_FIRT_SUBELEM,
+ lyplg_ext_stmt2str(exp_first->type), lyplg_ext_stmt2str(current_element));
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse simple element without any special constraints and argument mapped to yin attribute,
+ * for example prefix or namespace element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[out] value Where value of attribute should be stored.
+ * @param[in] arg_type Expected type of attribute.
+ * @param[in] arg_val_type Type of expected value of attribute.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_simple_element(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt, const char **value,
+ enum yin_argument arg_type, enum yang_arg arg_val_type, struct lysp_ext_instance **exts)
+{
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, arg_type, value, arg_val_type, parent_stmt));
+
+ return yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, parent_stmt, NULL, exts);
+}
+
+/**
+ * @brief Parse path element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[out] type Type structure to store parsed value, flags and extension instances.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_path(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ LY_ERR ret;
+ const char *str_path;
+
+ LY_CHECK_RET(yin_parse_simple_element(ctx, type, LY_STMT_PATH, &str_path, YIN_ARG_VALUE, Y_STR_ARG, &type->exts));
+
+ ret = ly_path_parse(ctx->xmlctx->ctx, NULL, str_path, 0, 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &type->path);
+ lydict_remove(ctx->xmlctx->ctx, str_path);
+ LY_CHECK_RET(ret);
+ type->flags |= LYS_SET_PATH;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add extension array to context exts instances.
+ *
+ * @param[in] ctx YIN parser context.
+ * @param[in] exts Parsed extension array that is final (no realloc anymore).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+yin_unres_exts_add(struct lysp_yin_ctx *ctx, struct lysp_ext_instance *exts)
+{
+ if (!exts) {
+ return LY_SUCCESS;
+ }
+
+ return ly_set_add(&ctx->main_ctx->ext_inst, exts, 1, NULL);
+}
+
+/**
+ * @brief Parse pattern element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] type Type structure to store parsed value, flags and extension instances.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_pattern(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ const char *real_value = NULL;
+ char *saved_value = NULL;
+ struct lysp_restr *restr;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, type->patterns, restr, LY_EMEM);
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &real_value, Y_STR_ARG, LY_STMT_PATTERN));
+ size_t len = strlen(real_value);
+
+ saved_value = malloc(len + 2);
+ LY_CHECK_ERR_RET(!saved_value, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ memmove(saved_value + 1, real_value, len);
+ lydict_remove(ctx->xmlctx->ctx, real_value);
+ saved_value[0] = LYSP_RESTR_PATTERN_ACK;
+ saved_value[len + 1] = '\0';
+ LY_CHECK_RET(lydict_insert_zc(ctx->xmlctx->ctx, saved_value, &restr->arg.str));
+ restr->arg.mod = PARSER_CUR_PMOD(ctx);
+ type->flags |= LYS_SET_PATTERN;
+
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &restr->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ERROR_APP_TAG, &restr->eapptag, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ERROR_MESSAGE, &restr->emsg, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MODIFIER, &restr->arg, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &restr->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), restr, LY_STMT_PATTERN, NULL, &restr->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, restr->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse fraction-digits element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] type Type structure to store value, flags and extension instances.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_fracdigits(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ const char *temp_val = NULL;
+ char *ptr;
+ unsigned long long num;
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_FRACTION_DIGITS));
+
+ if ((temp_val[0] == '\0') || (temp_val[0] == '0') || !isdigit(temp_val[0])) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "fraction-digits");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ num = strtoull(temp_val, &ptr, LY_BASE_DEC);
+ if (*ptr != '\0') {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "fraction-digits");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > LY_TYPE_DEC64_FD_MAX)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "fraction-digits");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ type->fraction_digits = num;
+ type->flags |= LYS_SET_FRDIGITS;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), type, LY_STMT_FRACTION_DIGITS, NULL, &type->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, type->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse enum element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] type Type structure to store parsed value, flags and extension instances.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_enum(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ struct lysp_type_enum *en;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, type->enums, en, LY_EMEM);
+ type->flags |= LYS_SET_ENUM;
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &en->name, Y_STR_ARG, LY_STMT_ENUM));
+ LY_CHECK_RET(lysp_check_enum_name((struct lysp_ctx *)ctx, en->name, strlen(en->name)));
+ CHECK_NONEMPTY((struct lysp_ctx *)ctx, strlen(en->name), "enum");
+ CHECK_UNIQUENESS((struct lysp_ctx *)ctx, type->enums, name, "enum", en->name);
+
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &en->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &en->iffeatures, 0},
+ {LY_STMT_REFERENCE, &en->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &en->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_VALUE, en, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), en, LY_STMT_ENUM, NULL, &en->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, en->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse bit element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] type Type structure to store parsed value, flags and extension instances.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_bit(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ struct lysp_type_enum *en;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, type->bits, en, LY_EMEM);
+ type->flags |= LYS_SET_BIT;
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &en->name, Y_IDENTIF_ARG, LY_STMT_BIT));
+ CHECK_UNIQUENESS((struct lysp_ctx *)ctx, type->bits, name, "bit", en->name);
+
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &en->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &en->iffeatures, 0},
+ {LY_STMT_POSITION, en, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &en->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &en->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), en, LY_STMT_BIT, NULL, &en->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, en->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse simple element without any special constraints and argument mapped to yin attribute, that can have
+ * more instances, such as base or if-feature.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[out] values Parsed values to add to.
+ * @param[in] arg_type Expected type of attribute.
+ * @param[in] arg_val_type Type of expected value of attribute.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_simple_elements(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, const char ***values,
+ enum yin_argument arg_type, enum yang_arg arg_val_type, struct lysp_ext_instance **exts)
+{
+ const char **value;
+ LY_ARRAY_COUNT_TYPE index = LY_ARRAY_COUNT(*values);
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, &index, 0}
+ };
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *values, value, LY_EMEM);
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, arg_type, value, arg_val_type, parent_stmt));
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), *values, parent_stmt, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse simple element without any special constraints and argument mapped to yin attribute.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[in] subinfo Information about subelement, is used to determin which function should be called and where to store parsed value.
+ * @param[in] arg_type Expected type of attribute.
+ * @param[in] arg_val_type Type of expected value of attribute.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_simple_elem(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt, struct yin_subelement *subinfo,
+ enum yin_argument arg_type, enum yang_arg arg_val_type, struct lysp_ext_instance **exts)
+{
+ if (subinfo->flags & YIN_SUBELEM_UNIQUE) {
+ LY_CHECK_RET(yin_parse_simple_element(ctx, parent, parent_stmt, (const char **)subinfo->dest, arg_type,
+ arg_val_type, exts));
+ } else {
+ LY_CHECK_RET(yin_parse_simple_elements(ctx, parent_stmt, (const char ***)subinfo->dest, arg_type,
+ arg_val_type, exts));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse base element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[out] dest Where parsed values should be stored.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_base(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, void *dest, struct lysp_ext_instance **exts)
+{
+ struct lysp_type *type;
+
+ if (parent_stmt == LY_STMT_TYPE) {
+ type = (struct lysp_type *)dest;
+ LY_CHECK_RET(yin_parse_simple_elements(ctx, LY_STMT_BASE, &type->bases, YIN_ARG_NAME, Y_PREF_IDENTIF_ARG, exts));
+ type->flags |= LYS_SET_BASE;
+ } else if (parent_stmt == LY_STMT_IDENTITY) {
+ LY_CHECK_RET(yin_parse_simple_elements(ctx, LY_STMT_BASE, (const char ***)dest, YIN_ARG_NAME, Y_PREF_IDENTIF_ARG, exts));
+ } else {
+ LOGINT(ctx->xmlctx->ctx);
+ return LY_EINT;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse require-instance element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[out] type Type structure to store value, flag and extensions.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_pasrse_reqinstance(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ const char *temp_val = NULL;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ type->flags |= LYS_SET_REQINST;
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_REQUIRE_INSTANCE));
+ if (strcmp(temp_val, "true") == 0) {
+ type->require_instance = 1;
+ } else if (strcmp(temp_val, "false") != 0) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS2, temp_val, "value",
+ "require-instance", "true", "false");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), type, LY_STMT_REQUIRE_INSTANCE, NULL, &type->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, type->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse modifier element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in,out] pat Value to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_modifier(struct lysp_yin_ctx *ctx, const void *parent, const char **pat, struct lysp_ext_instance **exts)
+{
+ const char *temp_val;
+ char *modified_val;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ assert(**pat == 0x06);
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_MODIFIER));
+ if (strcmp(temp_val, "invert-match") != 0) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS1, temp_val, "value",
+ "modifier", "invert-match");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ /* allocate new value */
+ modified_val = malloc(strlen(*pat) + 1);
+ LY_CHECK_ERR_RET(!modified_val, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ strcpy(modified_val, *pat);
+ lydict_remove(ctx->xmlctx->ctx, *pat);
+
+ /* modify the new value */
+ modified_val[0] = LYSP_RESTR_PATTERN_NACK;
+ LY_CHECK_RET(lydict_insert_zc(ctx->xmlctx->ctx, modified_val, pat));
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, LY_STMT_MODIFIER, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a restriction element (length, range or one instance of must).
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] restr_kw Identificaton of element that is being parsed, can be set to LY_STMT_MUST, LY_STMT_LENGTH or LY_STMT_RANGE.
+ * @param[in] restr Value to write to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_restriction(struct lysp_yin_ctx *ctx, enum ly_stmt restr_kw, struct lysp_restr *restr)
+{
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &restr->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ERROR_APP_TAG, &restr->eapptag, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ERROR_MESSAGE, &restr->emsg, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &restr->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+ /* argument of must is called condition, but argument of length and range is called value */
+ enum yin_argument arg_type = (restr_kw == LY_STMT_MUST) ? YIN_ARG_CONDITION : YIN_ARG_VALUE;
+
+ assert(restr_kw == LY_STMT_MUST || restr_kw == LY_STMT_LENGTH || restr_kw == LY_STMT_RANGE);
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, arg_type, &restr->arg.str, Y_STR_ARG, restr_kw));
+ restr->arg.mod = PARSER_CUR_PMOD(ctx);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), restr, restr_kw, NULL, &restr->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, restr->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse range element.
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[out] type Type structure to store parsed value and flags.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_range(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ type->range = calloc(1, sizeof *type->range);
+ LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ LY_CHECK_RET(yin_parse_restriction(ctx, LY_STMT_RANGE, type->range));
+ type->flags |= LYS_SET_RANGE;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse length element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[out] type Type structure to store parsed value and flags.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_length(struct lysp_yin_ctx *ctx, struct lysp_type *type)
+{
+ type->length = calloc(1, sizeof *type->length);
+ LY_CHECK_ERR_RET(!type->length, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ LY_CHECK_RET(yin_parse_restriction(ctx, LY_STMT_LENGTH, type->length));
+ type->flags |= LYS_SET_LENGTH;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse must element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] restrs Restrictions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_must(struct lysp_yin_ctx *ctx, struct lysp_restr **restrs)
+{
+ struct lysp_restr *restr;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *restrs, restr, LY_EMEM);
+ return yin_parse_restriction(ctx, LY_STMT_MUST, restr);
+}
+
+/**
+ * @brief Parse a node id into an array.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[in] subinfo Information about subelement, is used to determin which function should be called and where to store parsed value.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_qname(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, struct yin_subelement *subinfo,
+ struct lysp_ext_instance **exts)
+{
+ struct lysp_qname *qname, **qnames;
+
+ switch (parent_stmt) {
+ case LY_STMT_DEFAULT:
+ if (subinfo->flags & YIN_SUBELEM_UNIQUE) {
+ qname = (struct lysp_qname *)subinfo->dest;
+ } else {
+ qnames = (struct lysp_qname **)subinfo->dest;
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *qnames, qname, LY_EMEM);
+ }
+ qname->mod = PARSER_CUR_PMOD(ctx);
+ return yin_parse_simple_element(ctx, qname, parent_stmt, &qname->str, YIN_ARG_VALUE, Y_STR_ARG, exts);
+ case LY_STMT_UNIQUE:
+ assert(!(subinfo->flags & YIN_SUBELEM_UNIQUE));
+
+ qnames = (struct lysp_qname **)subinfo->dest;
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *qnames, qname, LY_EMEM);
+ qname->mod = PARSER_CUR_PMOD(ctx);
+ return yin_parse_simple_element(ctx, *qnames, parent_stmt, &qname->str, YIN_ARG_TAG, Y_STR_ARG, exts);
+ case LY_STMT_IF_FEATURE:
+ assert(!(subinfo->flags & YIN_SUBELEM_UNIQUE));
+
+ qnames = (struct lysp_qname **)subinfo->dest;
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *qnames, qname, LY_EMEM);
+ qname->mod = PARSER_CUR_PMOD(ctx);
+ return yin_parse_simple_element(ctx, *qnames, parent_stmt, &qname->str, YIN_ARG_NAME, Y_STR_ARG, exts);
+ default:
+ break;
+ }
+
+ LOGINT(ctx->xmlctx->ctx);
+ return LY_EINT;
+}
+
+/**
+ * @brief Parse position or value element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[out] enm Enum structure to save value, flags and extension instances.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_value_pos(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, struct lysp_type_enum *enm)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *temp_val = NULL;
+ char *ptr;
+ long long num = 0;
+ unsigned long long unum = 0;
+
+ assert(parent_stmt == LY_STMT_POSITION || parent_stmt == LY_STMT_VALUE);
+
+ /* set value flag */
+ enm->flags |= LYS_SET_VALUE;
+
+ /* get attribute value */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ LY_CHECK_GOTO(ret = yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, parent_stmt), cleanup);
+ if (!temp_val || (temp_val[0] == '\0') || (temp_val[0] == '+') ||
+ ((temp_val[0] == '0') && (temp_val[1] != '\0')) || ((parent_stmt == LY_STMT_POSITION) && !strcmp(temp_val, "-0"))) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* convert value */
+ errno = 0;
+ if (parent_stmt == LY_STMT_VALUE) {
+ num = strtoll(temp_val, &ptr, LY_BASE_DEC);
+ if ((num < INT64_C(-2147483648)) || (num > INT64_C(2147483647))) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ } else {
+ unum = strtoull(temp_val, &ptr, LY_BASE_DEC);
+ if (unum > UINT64_C(4294967295)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ /* check if whole argument value was converted */
+ if (*ptr != '\0') {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (errno == ERANGE) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_OOB_YIN, temp_val, "value", lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ /* save correctly ternary operator can't be used because num and unum have different signes */
+ if (parent_stmt == LY_STMT_VALUE) {
+ enm->value = num;
+ } else {
+ enm->value = unum;
+ }
+
+ /* parse subelements */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_GOTO(ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), enm, parent_stmt, NULL, &enm->exts), cleanup);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_GOTO(ret = yin_unres_exts_add(ctx, enm->exts), cleanup);
+
+cleanup:
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return ret;
+}
+
+/**
+ * @brief Parse belongs-to element.
+ *
+ * @param[in] ctx YIN parser context for logging and to store current state.
+ * @param[out] submod Structure of submodule that is being parsed.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values
+ */
+static LY_ERR
+yin_parse_belongs_to(struct lysp_yin_ctx *ctx, struct lysp_submodule *submod, struct lysp_ext_instance **exts)
+{
+ const char *belongsto;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_PREFIX, &submod->prefix, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_MODULE, &belongsto, Y_IDENTIF_ARG, LY_STMT_BELONGS_TO));
+ if (PARSER_CUR_PMOD(ctx)->mod->name != belongsto) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YIN, "Submodule \"belongs-to\" value \"%s\" does not match its module name \"%s\".",
+ belongsto, PARSER_CUR_PMOD(ctx)->mod->name);
+ lydict_remove(ctx->xmlctx->ctx, belongsto);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, belongsto);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), submod, LY_STMT_BELONGS_TO, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Function to parse meta tags (description, contact, ...) eg. elements with
+ * text element as child.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[out] value Where the content of meta element should be stored.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_meta(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt, const char **value,
+ struct lysp_ext_instance **exts)
+{
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ {LY_STMT_ARG_TEXT, value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE | YIN_SUBELEM_FIRST}
+ };
+
+ assert(parent_stmt == LY_STMT_ORGANIZATION || parent_stmt == LY_STMT_CONTACT || parent_stmt == LY_STMT_DESCRIPTION ||
+ parent_stmt == LY_STMT_REFERENCE);
+
+ /* check attributes */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NONE, NULL, Y_MAYBE_STR_ARG, parent_stmt));
+
+ /* parse content */
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, parent_stmt, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse error-message element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[out] value Where the content of error-message element should be stored.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_err_msg(struct lysp_yin_ctx *ctx, const void *parent, const char **value, struct lysp_ext_instance **exts)
+{
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ {LY_STMT_ARG_VALUE, value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE | YIN_SUBELEM_FIRST}
+ };
+
+ /* check attributes */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NONE, NULL, Y_MAYBE_STR_ARG, LY_STMT_ERROR_MESSAGE));
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, LY_STMT_ERROR_MESSAGE, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse type element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[in,out] subinfo Information about subelement, is used to determin which function should be called and where
+ * to store parsed value.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_type(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, struct yin_subelement *subinfo)
+{
+ struct lysp_type *type = NULL;
+
+ if (parent_stmt == LY_STMT_DEVIATE) {
+ *(struct lysp_type **)subinfo->dest = calloc(1, sizeof **(struct lysp_type **)subinfo->dest);
+ LY_CHECK_ERR_RET(!(*(struct lysp_type **)subinfo->dest), LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ type = *((struct lysp_type **)subinfo->dest);
+ } else {
+ type = (struct lysp_type *)subinfo->dest;
+ }
+
+ /* type as child of another type */
+ if (parent_stmt == LY_STMT_TYPE) {
+ struct lysp_type *nested_type = NULL;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, type->types, nested_type, LY_EMEM);
+ type->flags |= LYS_SET_TYPE;
+ type = nested_type;
+ }
+
+ type->pmod = PARSER_CUR_PMOD(ctx);
+
+ struct yin_subelement subelems[] = {
+ {LY_STMT_BASE, type, 0},
+ {LY_STMT_BIT, type, 0},
+ {LY_STMT_ENUM, type, 0},
+ {LY_STMT_FRACTION_DIGITS, type, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_LENGTH, type, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_PATH, type, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_PATTERN, type, 0},
+ {LY_STMT_RANGE, type, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REQUIRE_INSTANCE, type, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_TYPE, type, 0},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &type->name, Y_PREF_IDENTIF_ARG, LY_STMT_TYPE));
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), type, LY_STMT_TYPE, NULL, &type->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, type->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse max-elements element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] max Value to write to.
+ * @param[in] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_maxelements(struct lysp_yin_ctx *ctx, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *temp_val = NULL;
+ char *ptr;
+ unsigned long long num;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ *flags |= LYS_SET_MAX;
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ LY_CHECK_GOTO(ret = yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_MAX_ELEMENTS), cleanup);
+ if (!temp_val || (temp_val[0] == '\0') || (temp_val[0] == '0') || ((temp_val[0] != 'u') && !isdigit(temp_val[0]))) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (strcmp(temp_val, "unbounded")) {
+ errno = 0;
+ num = strtoull(temp_val, &ptr, LY_BASE_DEC);
+ if (*ptr != '\0') {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_OOB_YIN, temp_val, "value", "max-elements");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ *max = num;
+ }
+
+ ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), max, LY_STMT_MAX_ELEMENTS, NULL, exts);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return ret;
+}
+
+/**
+ * @brief Parse min-elements element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] min Value to write to.
+ * @param[in] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_minelements(struct lysp_yin_ctx *ctx, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ const char *temp_val = NULL;
+ char *ptr;
+ unsigned long long num;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ *flags |= LYS_SET_MIN;
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_MIN_ELEMENTS));
+
+ if (!temp_val || (temp_val[0] == '\0') || ((temp_val[0] == '0') && (temp_val[1] != '\0'))) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "min-elements");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ num = strtoull(temp_val, &ptr, LY_BASE_DEC);
+ if (ptr[0] != 0) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "value", "min-elements");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_OOB_YIN, temp_val, "value", "min-elements");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ *min = num;
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), min, LY_STMT_MIN_ELEMENTS, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse min-elements or max-elements element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[in] cur_stmt Type of current element.
+ * @param[in] dest Where the parsed value and flags should be stored.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_minmax(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, enum ly_stmt cur_stmt, void *dest)
+{
+ uint32_t *lim;
+ uint16_t *flags;
+ struct lysp_ext_instance **exts;
+
+ assert(cur_stmt == LY_STMT_MAX_ELEMENTS || cur_stmt == LY_STMT_MIN_ELEMENTS);
+ assert(parent_stmt == LY_STMT_LEAF_LIST || parent_stmt == LY_STMT_REFINE || parent_stmt == LY_STMT_LIST ||
+ parent_stmt == LY_STMT_DEVIATE);
+
+ if (parent_stmt == LY_STMT_LEAF_LIST) {
+ lim = (cur_stmt == LY_STMT_MAX_ELEMENTS) ? &((struct lysp_node_leaflist *)dest)->max : &((struct lysp_node_leaflist *)dest)->min;
+ flags = &((struct lysp_node_leaflist *)dest)->flags;
+ exts = &((struct lysp_node_leaflist *)dest)->exts;
+ } else if (parent_stmt == LY_STMT_REFINE) {
+ lim = (cur_stmt == LY_STMT_MAX_ELEMENTS) ? &((struct lysp_refine *)dest)->max : &((struct lysp_refine *)dest)->min;
+ flags = &((struct lysp_refine *)dest)->flags;
+ exts = &((struct lysp_refine *)dest)->exts;
+ } else if (parent_stmt == LY_STMT_LIST) {
+ lim = (cur_stmt == LY_STMT_MAX_ELEMENTS) ? &((struct lysp_node_list *)dest)->max : &((struct lysp_node_list *)dest)->min;
+ flags = &((struct lysp_node_list *)dest)->flags;
+ exts = &((struct lysp_node_list *)dest)->exts;
+ } else {
+ lim = ((struct minmax_dev_meta *)dest)->lim;
+ flags = ((struct minmax_dev_meta *)dest)->flags;
+ exts = ((struct minmax_dev_meta *)dest)->exts;
+ }
+
+ if (cur_stmt == LY_STMT_MAX_ELEMENTS) {
+ LY_CHECK_RET(yin_parse_maxelements(ctx, lim, flags, exts));
+ } else {
+ LY_CHECK_RET(yin_parse_minelements(ctx, lim, flags, exts));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse ordered-by element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_orderedby(struct lysp_yin_ctx *ctx, const void *parent, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ const char *temp_val;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_ORDERED_BY));
+ if (strcmp(temp_val, "system") == 0) {
+ *flags |= LYS_ORDBY_SYSTEM;
+ } else if (strcmp(temp_val, "user") == 0) {
+ *flags |= LYS_ORDBY_USER;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS2, temp_val, "value",
+ "ordered-by", "system", "user");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, LY_STMT_ORDERED_BY, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse any-data or any-xml element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] any_stmt Type of current statement, can be set to LY_STMT_ANYDATA or LY_STMT_ANYXML
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_any(struct lysp_yin_ctx *ctx, enum ly_stmt any_kw, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_anydata *any;
+
+ /* create new sibling */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, any, next, LY_EMEM);
+ any->nodetype = (any_kw == LY_STMT_ANYDATA) ? LYS_ANYDATA : LYS_ANYXML;
+ any->parent = node_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &any->name, Y_IDENTIF_ARG, any_kw));
+
+ struct yin_subelement subelems[] = {
+ {LY_STMT_CONFIG, &any->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DESCRIPTION, &any->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &any->iffeatures, 0},
+ {LY_STMT_MANDATORY, &any->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MUST, &any->musts, 0},
+ {LY_STMT_REFERENCE, &any->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &any->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_WHEN, &any->when, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), any, any_kw, NULL, &any->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, any->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief parse leaf element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_leaf(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_leaf *leaf;
+
+ /* create structure new leaf */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, leaf, next, LY_EMEM);
+ leaf->nodetype = LYS_LEAF;
+ leaf->parent = node_meta->parent;
+
+ /* parser argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &leaf->name, Y_IDENTIF_ARG, LY_STMT_LEAF));
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_CONFIG, &leaf->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DEFAULT, &leaf->dflt, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DESCRIPTION, &leaf->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &leaf->iffeatures, 0},
+ {LY_STMT_MANDATORY, &leaf->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MUST, &leaf->musts, 0},
+ {LY_STMT_REFERENCE, &leaf->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &leaf->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_TYPE, &leaf->type, YIN_SUBELEM_UNIQUE | YIN_SUBELEM_MANDATORY},
+ {LY_STMT_UNITS, &leaf->units, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_WHEN, &leaf->when, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), leaf, LY_STMT_LEAF, NULL, &leaf->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, leaf->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse leaf-list element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_leaflist(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_leaflist *llist;
+
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, llist, next, LY_EMEM);
+
+ llist->nodetype = LYS_LEAFLIST;
+ llist->parent = node_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &llist->name, Y_IDENTIF_ARG, LY_STMT_LEAF_LIST));
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_CONFIG, &llist->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DEFAULT, &llist->dflts, 0},
+ {LY_STMT_DESCRIPTION, &llist->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &llist->iffeatures, 0},
+ {LY_STMT_MAX_ELEMENTS, llist, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MIN_ELEMENTS, llist, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MUST, &llist->musts, 0},
+ {LY_STMT_ORDERED_BY, &llist->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &llist->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &llist->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_TYPE, &llist->type, YIN_SUBELEM_UNIQUE | YIN_SUBELEM_MANDATORY},
+ {LY_STMT_UNITS, &llist->units, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_WHEN, &llist->when, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), llist, LY_STMT_LEAF_LIST, NULL, &llist->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, llist->exts));
+
+ /* check invalid combination of subelements */
+ if ((llist->min) && (llist->dflts)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INCHILDSTMSCOMB_YIN, "min-elements", "default", "leaf-list");
+ return LY_EVALID;
+ }
+ if (llist->max && (llist->min > llist->max)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_MINMAX, llist->min, llist->max);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse typedef element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] typedef_meta Meta information about parent node and typedefs to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_typedef(struct lysp_yin_ctx *ctx, struct tree_node_meta *typedef_meta)
+{
+ struct lysp_tpdf *tpdf;
+ struct lysp_tpdf **tpdfs = (struct lysp_tpdf **)typedef_meta->nodes;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *tpdfs, tpdf, LY_EMEM);
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &tpdf->name, Y_IDENTIF_ARG, LY_STMT_TYPEDEF));
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DEFAULT, &tpdf->dflt, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DESCRIPTION, &tpdf->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &tpdf->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &tpdf->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_TYPE, &tpdf->type, YIN_SUBELEM_UNIQUE | YIN_SUBELEM_MANDATORY},
+ {LY_STMT_UNITS, &tpdf->units, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), tpdf, LY_STMT_TYPEDEF, NULL, &tpdf->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, tpdf->exts));
+
+ /* 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));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse refine element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] refines Refines to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_refine(struct lysp_yin_ctx *ctx, struct lysp_refine **refines)
+{
+ struct lysp_refine *rf;
+
+ /* allocate new refine */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *refines, rf, LY_EMEM);
+
+ /* parse attribute */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_TARGET_NODE, &rf->nodeid, Y_STR_ARG, LY_STMT_REFINE));
+ CHECK_NONEMPTY(ctx, strlen(rf->nodeid), "refine");
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_CONFIG, &rf->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DEFAULT, &rf->dflts, 0},
+ {LY_STMT_DESCRIPTION, &rf->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &rf->iffeatures, 0},
+ {LY_STMT_MANDATORY, &rf->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MAX_ELEMENTS, rf, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MIN_ELEMENTS, rf, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MUST, &rf->musts, 0},
+ {LY_STMT_PRESENCE, &rf->presence, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &rf->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), rf, LY_STMT_REFINE, NULL, &rf->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, rf->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse uses element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_uses(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_uses *uses;
+
+ /* create new uses */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, uses, next, LY_EMEM);
+ uses->nodetype = LYS_USES;
+ uses->parent = node_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &uses->name, Y_PREF_IDENTIF_ARG, LY_STMT_USES));
+
+ /* parse content */
+ struct tree_node_meta augments = {(struct lysp_node *)uses, (struct lysp_node **)&uses->augments};
+ struct yin_subelement subelems[] = {
+ {LY_STMT_AUGMENT, &augments, 0},
+ {LY_STMT_DESCRIPTION, &uses->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &uses->iffeatures, 0},
+ {LY_STMT_REFERENCE, &uses->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFINE, &uses->refines, 0},
+ {LY_STMT_STATUS, &uses->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_WHEN, &uses->when, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), uses, LY_STMT_USES, NULL, &uses->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, uses->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse revision element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] revs Parsed revisions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_revision(struct lysp_yin_ctx *ctx, struct lysp_revision **revs)
+{
+ struct lysp_revision *rev;
+ const char *temp_date = NULL;
+
+ /* allocate new reivison */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *revs, rev, LY_EMEM);
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_DATE, &temp_date, Y_STR_ARG, LY_STMT_REVISION));
+ /* check value */
+ if (lysp_check_date((struct lysp_ctx *)ctx, temp_date, strlen(temp_date), "revision")) {
+ lydict_remove(ctx->xmlctx->ctx, temp_date);
+ return LY_EVALID;
+ }
+ memcpy(rev->date, temp_date, LY_REV_SIZE);
+ lydict_remove(ctx->xmlctx->ctx, temp_date);
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &rev->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &rev->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), rev, LY_STMT_REVISION, NULL, &rev->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, rev->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse include element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] inc_meta Meta informatinou about module/submodule name and includes to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_include(struct lysp_yin_ctx *ctx, struct include_meta *inc_meta)
+{
+ struct lysp_include *inc;
+
+ /* allocate new include */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *inc_meta->includes, inc, LY_EMEM);
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_MODULE, &inc->name, Y_IDENTIF_ARG, LY_STMT_INCLUDE));
+
+ /* submodules share the namespace with the module names, so there must not be
+ * a module of the same name in the context, no need for revision matching */
+ if (!strcmp(inc_meta->name, inc->name) || ly_ctx_get_module_latest(ctx->xmlctx->ctx, inc->name)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_NAME2_COL, "module", "submodule", inc->name);
+ return LY_EVALID;
+ }
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &inc->dsc, YIN_SUBELEM_UNIQUE | YIN_SUBELEM_VER2},
+ {LY_STMT_REFERENCE, &inc->ref, YIN_SUBELEM_UNIQUE | YIN_SUBELEM_VER2},
+ {LY_STMT_REVISION_DATE, &inc->rev, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), inc, LY_STMT_INCLUDE, NULL, &inc->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, inc->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse revision-date element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] rev Array to store the parsed value in.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_revision_date(struct lysp_yin_ctx *ctx, char *rev, struct lysp_ext_instance **exts)
+{
+ const char *temp_rev;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_DATE, &temp_rev, Y_STR_ARG, LY_STMT_REVISION_DATE));
+ LY_CHECK_ERR_RET(lysp_check_date((struct lysp_ctx *)ctx, temp_rev, strlen(temp_rev), "revision-date") != LY_SUCCESS,
+ lydict_remove(ctx->xmlctx->ctx, temp_rev), LY_EVALID);
+
+ strcpy(rev, temp_rev);
+ lydict_remove(ctx->xmlctx->ctx, temp_rev);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), rev, LY_STMT_REVISION_DATE, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse config element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_config(struct lysp_yin_ctx *ctx, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ const char *temp_val = NULL;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_CONFIG));
+ if (strcmp(temp_val, "true") == 0) {
+ *flags |= LYS_CONFIG_W;
+ } else if (strcmp(temp_val, "false") == 0) {
+ *flags |= LYS_CONFIG_R;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS2, temp_val, "value", "config",
+ "true", "false");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), flags, LY_STMT_CONFIG, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse yang-version element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[out] version Storage for the parsed information.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_yangversion(struct lysp_yin_ctx *ctx, const void *parent, uint8_t *version, struct lysp_ext_instance **exts)
+{
+ const char *temp_version = NULL;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_version, Y_STR_ARG, LY_STMT_YANG_VERSION));
+ if (strcmp(temp_version, "1") == 0) {
+ *version = LYS_VERSION_1_0;
+ } else if (strcmp(temp_version, "1.1") == 0) {
+ *version = LYS_VERSION_1_1;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS2, temp_version, "value",
+ "yang-version", "1", "1.1");
+ lydict_remove(ctx->xmlctx->ctx, temp_version);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_version);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, LY_STMT_YANG_VERSION, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse import element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] imp_meta Meta information about prefix and imports to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_import(struct lysp_yin_ctx *ctx, struct import_meta *imp_meta)
+{
+ struct lysp_import *imp;
+
+ /* allocate new element in sized array for import */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *imp_meta->imports, imp, LY_EMEM);
+
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &imp->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_PREFIX, &imp->prefix, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &imp->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REVISION_DATE, imp->rev, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ /* parse import attributes */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_MODULE, &imp->name, Y_IDENTIF_ARG, LY_STMT_IMPORT));
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), imp, LY_STMT_IMPORT, NULL, &imp->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, imp->exts));
+
+ /* check prefix validity */
+ LY_CHECK_RET(lysp_check_prefix((struct lysp_ctx *)ctx, *imp_meta->imports, imp_meta->prefix, &imp->prefix), LY_EVALID);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse mandatory element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_mandatory(struct lysp_yin_ctx *ctx, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ const char *temp_val = NULL;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_MANDATORY));
+ if (strcmp(temp_val, "true") == 0) {
+ *flags |= LYS_MAND_TRUE;
+ } else if (strcmp(temp_val, "false") == 0) {
+ *flags |= LYS_MAND_FALSE;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS2, temp_val, "value",
+ "mandatory", "true", "false");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), flags, LY_STMT_MANDATORY, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse status element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_status(struct lysp_yin_ctx *ctx, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ const char *value = NULL;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &value, Y_STR_ARG, LY_STMT_STATUS));
+ if (strcmp(value, "current") == 0) {
+ *flags |= LYS_STATUS_CURR;
+ } else if (strcmp(value, "deprecated") == 0) {
+ *flags |= LYS_STATUS_DEPRC;
+ } else if (strcmp(value, "obsolete") == 0) {
+ *flags |= LYS_STATUS_OBSLT;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS3, value, "value",
+ "status", "current", "deprecated", "obsolete");
+ lydict_remove(ctx->xmlctx->ctx, value);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, value);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), flags, LY_STMT_STATUS, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse when element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[out] when_p When pointer to parse to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_when(struct lysp_yin_ctx *ctx, struct lysp_when **when_p)
+{
+ struct lysp_when *when;
+ LY_ERR ret = LY_SUCCESS;
+
+ when = calloc(1, sizeof *when);
+ LY_CHECK_ERR_RET(!when, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+
+ ret = lyxml_ctx_next(ctx->xmlctx);
+ LY_CHECK_ERR_RET(ret, free(when), ret);
+
+ ret = yin_parse_attribute(ctx, YIN_ARG_CONDITION, &when->cond, Y_STR_ARG, LY_STMT_WHEN);
+ LY_CHECK_ERR_RET(ret, free(when), ret);
+
+ *when_p = when;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &when->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &when->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), when, LY_STMT_WHEN, NULL, &when->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, when->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse yin-elemenet element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_yin_element(struct lysp_yin_ctx *ctx, const void *parent, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ const char *temp_val = NULL;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_YIN_ELEMENT));
+ if (strcmp(temp_val, "true") == 0) {
+ *flags |= LYS_YINELEM_TRUE;
+ } else if (strcmp(temp_val, "false") == 0) {
+ *flags |= LYS_YINELEM_FALSE;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS2, temp_val, "value",
+ "yin-element", "true", "false");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, LY_STMT_YIN_ELEMENT, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse argument element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in,out] arg_meta Meta information about destination of parsed data.
+ * @param[in,out] exts Extension instances to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_argument(struct lysp_yin_ctx *ctx, const void *parent, struct yin_argument_meta *arg_meta, struct lysp_ext_instance **exts)
+{
+ struct yin_subelement subelems[] = {
+ {LY_STMT_YIN_ELEMENT, arg_meta->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, arg_meta->argument, Y_IDENTIF_ARG, LY_STMT_ARGUMENT));
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), parent, LY_STMT_ARGUMENT, NULL, exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse the extension statement.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] extensions Extensions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_extension(struct lysp_yin_ctx *ctx, struct lysp_ext **extensions)
+{
+ struct lysp_ext *ex;
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *extensions, ex, LY_EMEM);
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &ex->name, Y_IDENTIF_ARG, LY_STMT_EXTENSION));
+
+ struct yin_argument_meta arg_info = {&ex->flags, &ex->argname};
+ struct yin_subelement subelems[] = {
+ {LY_STMT_ARGUMENT, &arg_info, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DESCRIPTION, &ex->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_REFERENCE, &ex->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &ex->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), ex, LY_STMT_EXTENSION, NULL, &ex->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, ex->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse feature element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] features Features to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_feature(struct lysp_yin_ctx *ctx, struct lysp_feature **features)
+{
+ struct lysp_feature *feat;
+
+ /* allocate new feature */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *features, feat, LY_EMEM);
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &feat->name, Y_IDENTIF_ARG, LY_STMT_FEATURE));
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &feat->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &feat->iffeatures, 0},
+ {LY_STMT_REFERENCE, &feat->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &feat->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), feat, LY_STMT_FEATURE, NULL, &feat->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, feat->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse identity element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] identities Identities to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_identity(struct lysp_yin_ctx *ctx, struct lysp_ident **identities)
+{
+ struct lysp_ident *ident;
+
+ /* allocate new identity */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *identities, ident, LY_EMEM);
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &ident->name, Y_IDENTIF_ARG, LY_STMT_IDENTITY));
+
+ /* parse content */
+ struct yin_subelement subelems[] = {
+ {LY_STMT_BASE, &ident->bases, 0},
+ {LY_STMT_DESCRIPTION, &ident->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_IF_FEATURE, &ident->iffeatures, YIN_SUBELEM_VER2},
+ {LY_STMT_REFERENCE, &ident->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_STATUS, &ident->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ LY_CHECK_RET(yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), ident, LY_STMT_IDENTITY, NULL, &ident->exts));
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, ident->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse list element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_list(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_list *list;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, list, next, LY_EMEM);
+ list->nodetype = LYS_LIST;
+ list->parent = node_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &list->name, Y_IDENTIF_ARG, LY_STMT_LIST));
+
+ /* parse list content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 25, (struct lysp_node *)list, &subelems,
+ LY_STMT_ACTION, &list->actions, 0,
+ LY_STMT_ANYDATA, &list->child, 0,
+ LY_STMT_ANYXML, &list->child, 0,
+ LY_STMT_CHOICE, &list->child, 0,
+ LY_STMT_CONFIG, &list->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_CONTAINER, &list->child, 0,
+ LY_STMT_DESCRIPTION, &list->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_GROUPING, &list->groupings, 0,
+ LY_STMT_IF_FEATURE, &list->iffeatures, 0,
+ LY_STMT_KEY, &list->key, YIN_SUBELEM_UNIQUE,
+ LY_STMT_LEAF, &list->child, 0,
+ LY_STMT_LEAF_LIST, &list->child, 0,
+ LY_STMT_LIST, &list->child, 0,
+ LY_STMT_MAX_ELEMENTS, list, YIN_SUBELEM_UNIQUE,
+ LY_STMT_MIN_ELEMENTS, list, YIN_SUBELEM_UNIQUE,
+ LY_STMT_MUST, &list->musts, 0,
+ LY_STMT_NOTIFICATION, &list->notifs, 0,
+ LY_STMT_ORDERED_BY, &list->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REFERENCE, &list->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &list->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_TYPEDEF, &list->typedefs, 0,
+ LY_STMT_UNIQUE, &list->uniques, 0,
+ LY_STMT_USES, &list->child, 0,
+ LY_STMT_WHEN, &list->when, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, list, LY_STMT_LIST, NULL, &list->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, list->exts));
+
+ if (list->max && (list->min > list->max)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_MINMAX, list->min, list->max);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse notification element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] notif_meta Meta information about parent node and notifications to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_notification(struct lysp_yin_ctx *ctx, struct tree_node_meta *notif_meta)
+{
+ struct lysp_node_notif *notif;
+ struct lysp_node_notif **notifs = (struct lysp_node_notif **)notif_meta->nodes;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ /* allocate new notification */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, notifs, notif, next, LY_EMEM);
+ notif->nodetype = LYS_NOTIF;
+ notif->parent = notif_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &notif->name, Y_IDENTIF_ARG, LY_STMT_NOTIFICATION));
+
+ /* parse notification content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 16, (struct lysp_node *)notif, &subelems,
+ LY_STMT_ANYDATA, &notif->child, 0,
+ LY_STMT_ANYXML, &notif->child, 0,
+ LY_STMT_CHOICE, &notif->child, 0,
+ LY_STMT_CONTAINER, &notif->child, 0,
+ LY_STMT_DESCRIPTION, &notif->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_GROUPING, &notif->groupings, 0,
+ LY_STMT_IF_FEATURE, &notif->iffeatures, 0,
+ LY_STMT_LEAF, &notif->child, 0,
+ LY_STMT_LEAF_LIST, &notif->child, 0,
+ LY_STMT_LIST, &notif->child, 0,
+ LY_STMT_MUST, &notif->musts, YIN_SUBELEM_VER2,
+ LY_STMT_REFERENCE, &notif->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &notif->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_TYPEDEF, &notif->typedefs, 0,
+ LY_STMT_USES, &notif->child, 0,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+
+ ret = yin_parse_content(ctx, subelems, subelems_size, notif, LY_STMT_NOTIFICATION, NULL, &notif->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, notif->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse grouping element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in,out] gr_meta Meta information about parent node and groupings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_grouping(struct lysp_yin_ctx *ctx, struct tree_node_meta *gr_meta)
+{
+ struct lysp_node_grp *grp;
+ struct lysp_node_grp **grps = (struct lysp_node_grp **)gr_meta->nodes;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ /* create new grouping */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, grps, grp, next, LY_EMEM);
+ grp->nodetype = LYS_GROUPING;
+ grp->parent = gr_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &grp->name, Y_IDENTIF_ARG, LY_STMT_GROUPING));
+
+ /* parse grouping content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 16, &grp->node, &subelems,
+ LY_STMT_ACTION, &grp->actions, 0,
+ LY_STMT_ANYDATA, &grp->child, 0,
+ LY_STMT_ANYXML, &grp->child, 0,
+ LY_STMT_CHOICE, &grp->child, 0,
+ LY_STMT_CONTAINER, &grp->child, 0,
+ LY_STMT_DESCRIPTION, &grp->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_GROUPING, &grp->groupings, 0,
+ LY_STMT_LEAF, &grp->child, 0,
+ LY_STMT_LEAF_LIST, &grp->child, 0,
+ LY_STMT_LIST, &grp->child, 0,
+ LY_STMT_NOTIFICATION, &grp->notifs, 0,
+ LY_STMT_REFERENCE, &grp->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &grp->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_TYPEDEF, &grp->typedefs, 0,
+ LY_STMT_USES, &grp->child, 0,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, grp, LY_STMT_GROUPING, NULL, &grp->exts);
+ subelems_deallocator(subelems_size, subelems);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, grp->exts));
+
+ /* 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));
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Parse container element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_container(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_container *cont;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ /* create new container */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, cont, next, LY_EMEM);
+ cont->nodetype = LYS_CONTAINER;
+ cont->parent = node_meta->parent;
+
+ /* parse aegument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &cont->name, Y_IDENTIF_ARG, LY_STMT_CONTAINER));
+
+ /* parse container content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 21, (struct lysp_node *)cont, &subelems,
+ LY_STMT_ACTION, &cont->actions, YIN_SUBELEM_VER2,
+ LY_STMT_ANYDATA, &cont->child, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &cont->child, 0,
+ LY_STMT_CHOICE, &cont->child, 0,
+ LY_STMT_CONFIG, &cont->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_CONTAINER, &cont->child, 0,
+ LY_STMT_DESCRIPTION, &cont->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_GROUPING, &cont->groupings, 0,
+ LY_STMT_IF_FEATURE, &cont->iffeatures, 0,
+ LY_STMT_LEAF, &cont->child, 0,
+ LY_STMT_LEAF_LIST, &cont->child, 0,
+ LY_STMT_LIST, &cont->child, 0,
+ LY_STMT_MUST, &cont->musts, 0,
+ LY_STMT_NOTIFICATION, &cont->notifs, YIN_SUBELEM_VER2,
+ LY_STMT_PRESENCE, &cont->presence, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REFERENCE, &cont->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &cont->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_TYPEDEF, &cont->typedefs, 0,
+ LY_STMT_USES, &cont->child, 0,
+ LY_STMT_WHEN, &cont->when, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, cont, LY_STMT_CONTAINER, NULL, &cont->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, cont->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse case element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_case(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ struct lysp_node_case *cas;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ /* create new case */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, cas, next, LY_EMEM);
+ cas->nodetype = LYS_CASE;
+ cas->parent = node_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &cas->name, Y_IDENTIF_ARG, LY_STMT_CASE));
+
+ /* parse case content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 14, (struct lysp_node *)cas, &subelems,
+ LY_STMT_ANYDATA, &cas->child, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &cas->child, 0,
+ LY_STMT_CHOICE, &cas->child, 0,
+ LY_STMT_CONTAINER, &cas->child, 0,
+ LY_STMT_DESCRIPTION, &cas->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_IF_FEATURE, &cas->iffeatures, 0,
+ LY_STMT_LEAF, &cas->child, 0,
+ LY_STMT_LEAF_LIST, &cas->child, 0,
+ LY_STMT_LIST, &cas->child, 0,
+ LY_STMT_REFERENCE, &cas->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &cas->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_USES, &cas->child, 0,
+ LY_STMT_WHEN, &cas->when, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, cas, LY_STMT_CASE, NULL, &cas->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, cas->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse choice element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] node_meta Meta information about parent node and siblings to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_choice(struct lysp_yin_ctx *ctx, struct tree_node_meta *node_meta)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ struct lysp_node_choice *choice;
+ size_t subelems_size;
+
+ /* create new choice */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, node_meta->nodes, choice, next, LY_EMEM);
+
+ choice->nodetype = LYS_CHOICE;
+ choice->parent = node_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &choice->name, Y_IDENTIF_ARG, LY_STMT_CHOICE));
+
+ /* parse choice content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 17, (struct lysp_node *)choice, &subelems,
+ LY_STMT_ANYDATA, &choice->child, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &choice->child, 0,
+ LY_STMT_CASE, &choice->child, 0,
+ LY_STMT_CHOICE, &choice->child, YIN_SUBELEM_VER2,
+ LY_STMT_CONFIG, &choice->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_CONTAINER, &choice->child, 0,
+ LY_STMT_DEFAULT, &choice->dflt, YIN_SUBELEM_UNIQUE,
+ LY_STMT_DESCRIPTION, &choice->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_IF_FEATURE, &choice->iffeatures, 0,
+ LY_STMT_LEAF, &choice->child, 0,
+ LY_STMT_LEAF_LIST, &choice->child, 0,
+ LY_STMT_LIST, &choice->child, 0,
+ LY_STMT_MANDATORY, &choice->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REFERENCE, &choice->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &choice->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_WHEN, &choice->when, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, choice, LY_STMT_CHOICE, NULL, &choice->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, choice->exts));
+
+ return ret;
+}
+
+/**
+ * @brief Parse input or output element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] inout_kw Identification of input/output element.
+ * @param[in] inout_meta Meta information about parent node and siblings and input/output pointer to write to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_inout(struct lysp_yin_ctx *ctx, enum ly_stmt inout_kw, struct inout_meta *inout_meta)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ /* initiate structure */
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), (inout_kw == LY_STMT_INPUT) ? "input" : "output", 0, &inout_meta->inout_p->name));
+ inout_meta->inout_p->nodetype = (inout_kw == LY_STMT_INPUT) ? LYS_INPUT : LYS_OUTPUT;
+ inout_meta->inout_p->parent = inout_meta->parent;
+
+ /* check attributes */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NONE, NULL, Y_MAYBE_STR_ARG, inout_kw));
+
+ /* parser input/output content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 12, (struct lysp_node *)inout_meta->inout_p, &subelems,
+ LY_STMT_ANYDATA, &inout_meta->inout_p->child, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &inout_meta->inout_p->child, 0,
+ LY_STMT_CHOICE, &inout_meta->inout_p->child, 0,
+ LY_STMT_CONTAINER, &inout_meta->inout_p->child, 0,
+ LY_STMT_GROUPING, &inout_meta->inout_p->groupings, 0,
+ LY_STMT_LEAF, &inout_meta->inout_p->child, 0,
+ LY_STMT_LEAF_LIST, &inout_meta->inout_p->child, 0,
+ LY_STMT_LIST, &inout_meta->inout_p->child, 0,
+ LY_STMT_MUST, &inout_meta->inout_p->musts, YIN_SUBELEM_VER2,
+ LY_STMT_TYPEDEF, &inout_meta->inout_p->typedefs, 0,
+ LY_STMT_USES, &inout_meta->inout_p->child, 0,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, inout_meta->inout_p, inout_kw, NULL, &inout_meta->inout_p->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, inout_meta->inout_p->exts));
+
+ if (!inout_meta->inout_p->child) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_MISSTMT, "data-def-stmt", lyplg_ext_stmt2str(inout_kw));
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse action element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] act_meta Meta information about parent node and actions to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_action(struct lysp_yin_ctx *ctx, struct tree_node_meta *act_meta)
+{
+ struct lysp_node_action *act, **acts = (struct lysp_node_action **)act_meta->nodes;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+ enum ly_stmt kw = act_meta->parent ? LY_STMT_ACTION : LY_STMT_RPC;
+
+ /* create new action */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, acts, act, next, LY_EMEM);
+ act->nodetype = act_meta->parent ? LYS_ACTION : LYS_RPC;
+ act->parent = act_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &act->name, Y_IDENTIF_ARG, kw));
+
+ /* parse content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 9, (struct lysp_node *)act, &subelems,
+ LY_STMT_DESCRIPTION, &act->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_GROUPING, &act->groupings, 0,
+ LY_STMT_IF_FEATURE, &act->iffeatures, 0,
+ LY_STMT_INPUT, &act->input, YIN_SUBELEM_UNIQUE,
+ LY_STMT_OUTPUT, &act->output, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REFERENCE, &act->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &act->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_TYPEDEF, &act->typedefs, 0,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = (yin_parse_content(ctx, subelems, subelems_size, act, kw, NULL, &act->exts));
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* always initialize inout, they are technically present (needed for later deviations/refines) */
+ if (!act->input.nodetype) {
+ act->input.nodetype = LYS_INPUT;
+ act->input.parent = (struct lysp_node *)act;
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), "input", 0, &act->input.name));
+ }
+ if (!act->output.nodetype) {
+ act->output.nodetype = LYS_OUTPUT;
+ act->output.parent = (struct lysp_node *)act;
+ LY_CHECK_RET(lydict_insert(PARSER_CTX(ctx), "output", 0, &act->output.name));
+ }
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, act->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse augment element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] aug_meta Meta information about parent node and augments to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_augment(struct lysp_yin_ctx *ctx, struct tree_node_meta *aug_meta)
+{
+ struct lysp_node_augment *aug;
+ struct lysp_node_augment **augs = (struct lysp_node_augment **)aug_meta->nodes;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ size_t subelems_size;
+
+ /* create new augment */
+ LY_LIST_NEW_RET(ctx->xmlctx->ctx, augs, aug, next, LY_EMEM);
+ aug->nodetype = LYS_AUGMENT;
+ aug->parent = aug_meta->parent;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_TARGET_NODE, &aug->nodeid, Y_STR_ARG, LY_STMT_AUGMENT));
+ CHECK_NONEMPTY((struct lysp_ctx *)ctx, strlen(aug->nodeid), "augment");
+
+ /* parser augment content */
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 17, (struct lysp_node *)aug, &subelems,
+ LY_STMT_ACTION, &aug->actions, YIN_SUBELEM_VER2,
+ LY_STMT_ANYDATA, &aug->child, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &aug->child, 0,
+ LY_STMT_CASE, &aug->child, 0,
+ LY_STMT_CHOICE, &aug->child, 0,
+ LY_STMT_CONTAINER, &aug->child, 0,
+ LY_STMT_DESCRIPTION, &aug->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_IF_FEATURE, &aug->iffeatures, 0,
+ LY_STMT_LEAF, &aug->child, 0,
+ LY_STMT_LEAF_LIST, &aug->child, 0,
+ LY_STMT_LIST, &aug->child, 0,
+ LY_STMT_NOTIFICATION, &aug->notifs, YIN_SUBELEM_VER2,
+ LY_STMT_REFERENCE, &aug->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_STATUS, &aug->flags, YIN_SUBELEM_UNIQUE,
+ LY_STMT_USES, &aug->child, 0,
+ LY_STMT_WHEN, &aug->when, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+ ret = yin_parse_content(ctx, subelems, subelems_size, aug, LY_STMT_AUGMENT, NULL, &aug->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, aug->exts));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse deviate element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] deviates Deviates to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_deviate(struct lysp_yin_ctx *ctx, struct lysp_deviate **deviates)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint8_t dev_mod;
+ const char *temp_val;
+ struct lysp_deviate *d;
+ struct lysp_deviate_add *d_add = NULL;
+ struct lysp_deviate_rpl *d_rpl = NULL;
+ struct lysp_deviate_del *d_del = NULL;
+
+ /* parse argument */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, LY_STMT_DEVIATE));
+
+ if (strcmp(temp_val, "not-supported") == 0) {
+ dev_mod = LYS_DEV_NOT_SUPPORTED;
+ } else if (strcmp(temp_val, "add") == 0) {
+ dev_mod = LYS_DEV_ADD;
+ } else if (strcmp(temp_val, "replace") == 0) {
+ dev_mod = LYS_DEV_REPLACE;
+ } else if (strcmp(temp_val, "delete") == 0) {
+ dev_mod = LYS_DEV_DELETE;
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INVAL_YIN VALID_VALS4, temp_val, "value", "deviate",
+ "not-supported", "add", "replace", "delete");
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+ return LY_EVALID;
+ }
+ lydict_remove(ctx->xmlctx->ctx, temp_val);
+
+ if (dev_mod == LYS_DEV_NOT_SUPPORTED) {
+ d = calloc(1, sizeof *d);
+ LY_CHECK_ERR_RET(!d, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ struct yin_subelement subelems[] = {
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0}
+ };
+
+ ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), d, LY_STMT_DEVIATE, NULL, &d->exts);
+
+ } else if (dev_mod == LYS_DEV_ADD) {
+ d_add = calloc(1, sizeof *d_add);
+ LY_CHECK_ERR_RET(!d_add, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_add;
+ struct minmax_dev_meta min = {&d_add->min, &d_add->flags, &d_add->exts};
+ struct minmax_dev_meta max = {&d_add->max, &d_add->flags, &d_add->exts};
+ struct yin_subelement subelems[] = {
+ {LY_STMT_CONFIG, &d_add->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DEFAULT, &d_add->dflts, 0},
+ {LY_STMT_MANDATORY, &d_add->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MAX_ELEMENTS, &max, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MIN_ELEMENTS, &min, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MUST, &d_add->musts, 0},
+ {LY_STMT_UNIQUE, &d_add->uniques, 0},
+ {LY_STMT_UNITS, &d_add->units, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), d, LY_STMT_DEVIATE, NULL, &d->exts);
+
+ } else if (dev_mod == LYS_DEV_REPLACE) {
+ d_rpl = calloc(1, sizeof *d_rpl);
+ LY_CHECK_ERR_RET(!d_rpl, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_rpl;
+ struct minmax_dev_meta min = {&d_rpl->min, &d_rpl->flags, &d_rpl->exts};
+ struct minmax_dev_meta max = {&d_rpl->max, &d_rpl->flags, &d_rpl->exts};
+ struct yin_subelement subelems[] = {
+ {LY_STMT_CONFIG, &d_rpl->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DEFAULT, &d_rpl->dflt, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MANDATORY, &d_rpl->flags, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MAX_ELEMENTS, &max, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_MIN_ELEMENTS, &min, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_TYPE, &d_rpl->type, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_UNITS, &d_rpl->units, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), d, LY_STMT_DEVIATE, NULL, &d->exts);
+
+ } else {
+ d_del = calloc(1, sizeof *d_del);
+ LY_CHECK_ERR_RET(!d_del, LOGMEM(ctx->xmlctx->ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_del;
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DEFAULT, &d_del->dflts, 0},
+ {LY_STMT_MUST, &d_del->musts, 0},
+ {LY_STMT_UNIQUE, &d_del->uniques, 0},
+ {LY_STMT_UNITS, &d_del->units, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), d, LY_STMT_DEVIATE, NULL, &d->exts);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_GOTO(ret = yin_unres_exts_add(ctx, d->exts), cleanup);
+
+ d->mod = dev_mod;
+ /* insert into siblings */
+ LY_LIST_INSERT(deviates, d, next);
+
+ return ret;
+
+cleanup:
+ free(d);
+ return ret;
+}
+
+/**
+ * @brief Parse deviation element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] deviations Deviations to add to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_deviation(struct lysp_yin_ctx *ctx, struct lysp_deviation **deviations)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_deviation *dev = NULL;
+ struct lysf_ctx fctx = {.ctx = PARSER_CTX(ctx)};
+
+ /* create new deviation */
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *deviations, dev, LY_EMEM);
+
+ /* parse argument */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ LY_CHECK_GOTO(ret = yin_parse_attribute(ctx, YIN_ARG_TARGET_NODE, &dev->nodeid, Y_STR_ARG, LY_STMT_DEVIATION), cleanup);
+ CHECK_NONEMPTY((struct lysp_ctx *)ctx, strlen(dev->nodeid), "deviation");
+ struct yin_subelement subelems[] = {
+ {LY_STMT_DESCRIPTION, &dev->dsc, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DEVIATE, &dev->deviates, YIN_SUBELEM_MANDATORY},
+ {LY_STMT_REFERENCE, &dev->ref, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_EXTENSION_INSTANCE, NULL, 0},
+ };
+
+ ret = yin_parse_content(ctx, subelems, ly_sizeofarray(subelems), dev, LY_STMT_DEVIATION, NULL, &dev->exts);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_GOTO(ret = yin_unres_exts_add(ctx, dev->exts), cleanup);
+
+cleanup:
+ if (ret) {
+ lysp_deviation_free(&fctx, dev);
+ LY_ARRAY_DECREMENT_FREE(*deviations);
+ }
+ return ret;
+}
+
+/**
+ * @brief map keyword to keyword-group.
+ *
+ * @param[in] ctx YIN parser context used for logging.
+ * @param[in] kw Keyword that is child of module or submodule.
+ * @param[out] group Group of keyword.
+ * @return LY_SUCCESS on success LY_EINT if kw can't be mapped to kw_group, should not happen if called correctly.
+ */
+static LY_ERR
+kw2kw_group(struct lysp_yin_ctx *ctx, enum ly_stmt kw, enum yang_module_stmt *group)
+{
+ switch (kw) {
+ /* module header */
+ case LY_STMT_NONE:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_PREFIX:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_YANG_VERSION:
+ *group = Y_MOD_MODULE_HEADER;
+ break;
+ /* linkage */
+ case LY_STMT_INCLUDE:
+ case LY_STMT_IMPORT:
+ *group = Y_MOD_LINKAGE;
+ break;
+ /* meta */
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_REFERENCE:
+ *group = Y_MOD_META;
+ break;
+ /* revision */
+ case LY_STMT_REVISION:
+ *group = Y_MOD_REVISION;
+ break;
+ /* body */
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_AUGMENT:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_RPC:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_USES:
+ case LY_STMT_EXTENSION_INSTANCE:
+ *group = Y_MOD_BODY;
+ break;
+ default:
+ LOGINT(ctx->xmlctx->ctx);
+ return LY_EINT;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check if relative order of two keywords is valid.
+ *
+ * @param[in] ctx YIN parser context used for logging.
+ * @param[in] cur_stmt Type of current statement.
+ * @param[in] next_stmt Type of next statement.
+ * @param[in] parent_stmt Type of parent statement, can be se to to LY_STMT_MODULE of LY_STMT_SUBMODULE,
+ * because relative order is required only in module and submodule sub-elements, used for logging.
+ * @return LY_SUCCESS on success and LY_EVALID if relative order is invalid.
+ */
+static LY_ERR
+yin_check_relative_order(struct lysp_yin_ctx *ctx, enum ly_stmt cur_stmt, enum ly_stmt next_stmt, enum ly_stmt parent_stmt)
+{
+ enum yang_module_stmt gr, next_gr;
+
+ assert(parent_stmt == LY_STMT_MODULE || parent_stmt == LY_STMT_SUBMODULE);
+
+ if (cur_stmt == LY_STMT_EXTENSION_INSTANCE) {
+ /* no order defined */
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(kw2kw_group(ctx, cur_stmt, &gr));
+ LY_CHECK_RET(kw2kw_group(ctx, next_stmt, &next_gr));
+
+ if (gr > next_gr) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INORDER_YIN, lyplg_ext_stmt2str(parent_stmt), lyplg_ext_stmt2str(next_stmt),
+ lyplg_ext_stmt2str(cur_stmt));
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse argument of extension subelement that is classic yang keyword and not another instance of extension.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[out] arg Value to write to.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_extension_instance_arg(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, const char **arg)
+{
+ enum ly_stmt child;
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+
+ switch (parent_stmt) {
+ case LY_STMT_ACTION:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_BASE:
+ case LY_STMT_BIT:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_ENUM:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_MODULE:
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_RPC:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPE:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_UNITS:
+ case LY_STMT_USES:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_AUGMENT:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_REFINE:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_TARGET_NODE, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_CONFIG:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_KEY:
+ case LY_STMT_LENGTH:
+ case LY_STMT_MANDATORY:
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_ORDERED_BY:
+ case LY_STMT_PATH:
+ case LY_STMT_PATTERN:
+ case LY_STMT_POSITION:
+ case LY_STMT_PREFIX:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_RANGE:
+ case LY_STMT_REQUIRE_INSTANCE:
+ case LY_STMT_STATUS:
+ case LY_STMT_VALUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_VALUE, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ case LY_STMT_BELONGS_TO:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_MODULE, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NONE, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_MUST:
+ case LY_STMT_WHEN:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_CONDITION, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_NAMESPACE:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_URI, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_REVISION:
+ case LY_STMT_REVISION_DATE:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_DATE, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ case LY_STMT_UNIQUE:
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_TAG, arg, Y_MAYBE_STR_ARG, parent_stmt));
+ break;
+ /* argument is mapped to yin element */
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_ERROR_MESSAGE:
+ /* there shouldn't be any attribute, argument is supposed to be first subelement */
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NONE, arg, Y_MAYBE_STR_ARG, parent_stmt));
+
+ /* no content */
+ assert(ctx->xmlctx->status == LYXML_ELEM_CONTENT);
+ if (ctx->xmlctx->ws_only) {
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+ if (((ctx->xmlctx->status == LYXML_ELEM_CONTENT) && !ctx->xmlctx->ws_only) || (ctx->xmlctx->status != LYXML_ELEMENT)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_FIRT_SUBELEM,
+ parent_stmt == LY_STMT_ERROR_MESSAGE ? "value" : "text", lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+
+ /* parse child element */
+ child = yin_match_keyword(ctx, ctx->xmlctx->name, ctx->xmlctx->name_len, ctx->xmlctx->prefix,
+ ctx->xmlctx->prefix_len, parent_stmt);
+ if (((parent_stmt == LY_STMT_ERROR_MESSAGE) && (child != LY_STMT_ARG_VALUE)) ||
+ ((parent_stmt != LY_STMT_ERROR_MESSAGE) && (child != LY_STMT_ARG_TEXT))) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len, ctx->xmlctx->name,
+ lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+
+ /* no attributes expected */
+ while (ctx->xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+
+ /* load and save content */
+ INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, *arg);
+ LY_CHECK_RET(!*arg, LY_EMEM);
+
+ /* load closing tag of subelement */
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ break;
+ default:
+ LOGINT(ctx->xmlctx->ctx);
+ return LY_EINT;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse yin element into generic structure.
+ *
+ * @param[in,out] ctx Yin parser context for XML context, logging, and to store current state.
+ * @param[in] parent_stmt Type of parent statement.
+ * @param[out] element Where the element structure should be stored.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_element_generic(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, struct lysp_stmt **element)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_stmt *last = NULL, *new = NULL;
+ char *id;
+
+ assert(ctx->xmlctx->status == LYXML_ELEMENT);
+
+ /* allocate new structure for element */
+ *element = calloc(1, sizeof(**element));
+ LY_CHECK_ERR_GOTO(!(*element), LOGMEM(ctx->xmlctx->ctx); ret = LY_EMEM, cleanup);
+
+ /* store identifier */
+ if (ctx->xmlctx->prefix_len) {
+ if (asprintf(&id, "%.*s:%.*s", (int)ctx->xmlctx->prefix_len, ctx->xmlctx->prefix, (int)ctx->xmlctx->name_len,
+ ctx->xmlctx->name) == -1) {
+ LOGMEM(ctx->xmlctx->ctx);
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(ret = lydict_insert_zc(ctx->xmlctx->ctx, id, &(*element)->stmt), cleanup);
+
+ /* store prefix data for the statement */
+ LY_CHECK_GOTO(ret = ly_store_prefix_data(ctx->xmlctx->ctx, (*element)->stmt, strlen((*element)->stmt), LY_VALUE_XML,
+ &ctx->xmlctx->ns, &(*element)->format, &(*element)->prefix_data), cleanup);
+ } else {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->xmlctx->ctx, ctx->xmlctx->name, ctx->xmlctx->name_len, &(*element)->stmt), cleanup);
+ }
+
+ (*element)->kw = yin_match_keyword(ctx, ctx->xmlctx->name, ctx->xmlctx->name_len, ctx->xmlctx->prefix,
+ ctx->xmlctx->prefix_len, parent_stmt);
+
+ last = (*element)->child;
+ if ((*element)->kw == LY_STMT_NONE) {
+ /* unrecognized element */
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len, ctx->xmlctx->name,
+ lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if ((*element)->kw != LY_STMT_EXTENSION_INSTANCE) {
+ /* element is known yang keyword, which means argument can be parsed correctly. */
+ ret = yin_parse_extension_instance_arg(ctx, (*element)->kw, &(*element)->arg);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+
+ /* load attributes in generic way, save all attributes in linked list */
+ while (ctx->xmlctx->status == LYXML_ATTRIBUTE) {
+ new = calloc(1, sizeof(*last));
+ LY_CHECK_ERR_GOTO(!new, LOGMEM(ctx->xmlctx->ctx); ret = LY_EMEM, cleanup);
+ if (!(*element)->child) {
+ /* save first */
+ (*element)->child = new;
+ } else {
+ last->next = new;
+ }
+ last = new;
+
+ last->flags |= LYS_YIN_ATTR;
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->xmlctx->ctx, ctx->xmlctx->name, ctx->xmlctx->name_len, &last->stmt), cleanup);
+ last->kw = LY_STMT_NONE;
+ /* attributes with prefix are ignored */
+ if (!ctx->xmlctx->prefix) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+
+ INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, last->arg);
+ LY_CHECK_ERR_GOTO(!last->arg, ret = LY_EMEM, cleanup);
+ } else {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+ }
+
+ if ((ctx->xmlctx->status != LYXML_ELEM_CONTENT) || ctx->xmlctx->ws_only) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ while (ctx->xmlctx->status == LYXML_ELEMENT) {
+ /* parse subelements */
+ ret = yin_parse_element_generic(ctx, (*element)->kw, &new);
+ LY_CHECK_GOTO(ret, cleanup);
+ if (!(*element)->child) {
+ /* save first */
+ (*element)->child = new;
+ } else {
+ last->next = new;
+ }
+ last = new;
+
+ assert(ctx->xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+ } else {
+ /* save element content */
+ if (ctx->xmlctx->value_len) {
+ INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, (*element)->arg);
+ LY_CHECK_ERR_GOTO(!(*element)->arg, ret = LY_EMEM, cleanup);
+
+ /* store prefix data for the argument as well */
+ LY_CHECK_GOTO(ret = ly_store_prefix_data(ctx->xmlctx->ctx, (*element)->arg, strlen((*element)->arg), LY_VALUE_XML,
+ &ctx->xmlctx->ns, &(*element)->format, &(*element)->prefix_data), cleanup);
+ }
+
+ /* read closing tag */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse instance of extension.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[in] parent_stmt_index In case of several @p parent_stmt, index of the relevant @p parent statement.
+ * @param[in,out] exts Extension instance to add to.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_extension_instance(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt,
+ LY_ARRAY_COUNT_TYPE parent_stmt_index, struct lysp_ext_instance **exts)
+{
+ struct lysp_ext_instance *e;
+ struct lysp_stmt *last_subelem = NULL, *new_subelem = NULL;
+ char *ext_name;
+
+ assert(ctx->xmlctx->status == LYXML_ELEMENT);
+ assert(exts);
+
+ LY_ARRAY_NEW_RET(ctx->xmlctx->ctx, *exts, e, LY_EMEM);
+
+ if (!ctx->xmlctx->prefix_len) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%*.s\" without the mandatory prefix.",
+ (int)ctx->xmlctx->name_len, ctx->xmlctx->name);
+ return LY_EVALID;
+ }
+
+ /* store prefixed name */
+ if (asprintf(&ext_name, "%.*s:%.*s", (int)ctx->xmlctx->prefix_len, ctx->xmlctx->prefix, (int)ctx->xmlctx->name_len,
+ ctx->xmlctx->name) == -1) {
+ LOGMEM(ctx->xmlctx->ctx);
+ return LY_EMEM;
+ }
+ LY_CHECK_RET(lydict_insert_zc(ctx->xmlctx->ctx, ext_name, &e->name));
+
+ /* store prefix data for the name */
+ LY_CHECK_RET(ly_store_prefix_data(ctx->xmlctx->ctx, e->name, strlen(e->name), LY_VALUE_XML, &ctx->xmlctx->ns,
+ &e->format, &e->prefix_data));
+
+ e->parent = (void *)parent;
+ e->parent_stmt = parent_stmt;
+ e->parent_stmt_index = parent_stmt_index;
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+
+ /* store attributes as subelements */
+ while (ctx->xmlctx->status == LYXML_ATTRIBUTE) {
+ if (!ctx->xmlctx->prefix) {
+ new_subelem = calloc(1, sizeof(*new_subelem));
+ if (!e->child) {
+ e->child = new_subelem;
+ } else {
+ last_subelem->next = new_subelem;
+ }
+ last_subelem = new_subelem;
+
+ last_subelem->flags |= LYS_YIN_ATTR;
+ LY_CHECK_RET(lydict_insert(ctx->xmlctx->ctx, ctx->xmlctx->name, ctx->xmlctx->name_len, &last_subelem->stmt));
+ LY_CHECK_RET(!last_subelem->stmt, LY_EMEM);
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+
+ INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic,
+ last_subelem->arg);
+ LY_CHECK_RET(!last_subelem->arg, LY_EMEM);
+ } else {
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+
+ /* parse subelements */
+ assert(ctx->xmlctx->status == LYXML_ELEM_CONTENT);
+ if (ctx->xmlctx->ws_only) {
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ while (ctx->xmlctx->status == LYXML_ELEMENT) {
+ LY_CHECK_RET(yin_parse_element_generic(ctx, LY_STMT_EXTENSION_INSTANCE, &new_subelem));
+ if (!e->child) {
+ e->child = new_subelem;
+ } else {
+ last_subelem->next = new_subelem;
+ }
+ last_subelem = new_subelem;
+
+ assert(ctx->xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ }
+ } else if (ctx->xmlctx->value_len) {
+ /* invalid text content */
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%s\" with unexpected text content \".*s\".", ext_name,
+ (int)ctx->xmlctx->value_len, ctx->xmlctx->value);
+ return LY_EVALID;
+ }
+
+ e->parsed = NULL;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Generic function for content parsing
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] subelem_info array of valid subelement types and meta information
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in] parent Current statement parent.
+ * @param[in] parent_stmt Type of @p parent statement.
+ * @param[out] text_content Where the text content of element should be stored if any. Text content is ignored if set to NULL.
+ * @param[in,out] exts Extension instance to add to. Can be set to null if element cannot have extension as subelements.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info, size_t subelem_info_size,
+ const void *parent, enum ly_stmt parent_stmt, const char **text_content, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum LYXML_PARSER_STATUS next_status;
+ enum ly_stmt cur_stmt = LY_STMT_NONE, last_stmt = LY_STMT_NONE;
+ struct yin_subelement *subelem = NULL;
+
+ assert(ctx->xmlctx->status == LYXML_ELEM_CONTENT);
+
+ if (ctx->xmlctx->ws_only) {
+ /* check whether there are any children */
+ LY_CHECK_GOTO(ret = lyxml_ctx_peek(ctx->xmlctx, &next_status), cleanup);
+ } else {
+ /* we want to parse the value */
+ next_status = LYXML_ELEM_CLOSE;
+ }
+
+ if (next_status == LYXML_ELEMENT) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+
+ /* current element has subelements as content */
+ while (ctx->xmlctx->status == LYXML_ELEMENT) {
+ /* match keyword */
+ last_stmt = cur_stmt;
+ cur_stmt = yin_match_keyword(ctx, ctx->xmlctx->name, ctx->xmlctx->name_len, ctx->xmlctx->prefix,
+ ctx->xmlctx->prefix_len, parent_stmt);
+
+ /* check if this element can be child of current element */
+ subelem = get_record(cur_stmt, subelem_info_size, subelem_info);
+ if (!subelem) {
+ if ((parent_stmt == LY_STMT_DEVIATE) && isdevsub(cur_stmt)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INDEV_YIN, lyplg_ext_stmt2str(cur_stmt));
+ } else {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len,
+ ctx->xmlctx->name, lyplg_ext_stmt2str(parent_stmt));
+ }
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* relative order is required only in module and submodule sub-elements */
+ if ((parent_stmt == LY_STMT_MODULE) || (parent_stmt == LY_STMT_SUBMODULE)) {
+ ret = yin_check_relative_order(ctx, last_stmt, cur_stmt, parent_stmt);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* flag check */
+ if ((subelem->flags & YIN_SUBELEM_UNIQUE) && (subelem->flags & YIN_SUBELEM_PARSED)) {
+ /* subelement uniquenes */
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_SUBELEM_REDEF, lyplg_ext_stmt2str(cur_stmt), lyplg_ext_stmt2str(parent_stmt));
+ return LY_EVALID;
+ }
+ if (subelem->flags & YIN_SUBELEM_FIRST) {
+ /* subelement is supposed to be defined as first subelement */
+ ret = yin_check_subelem_first_constraint(ctx, subelem_info, subelem_info_size, parent_stmt, subelem);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ if (subelem->flags & YIN_SUBELEM_VER2) {
+ /* subelement is supported only in version 1.1 or higher */
+ if (PARSER_CUR_PMOD(ctx)->version < LYS_VERSION_1_1) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INSUBELEM2, lyplg_ext_stmt2str(cur_stmt), lyplg_ext_stmt2str(parent_stmt));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ /* note that element was parsed for easy uniqueness check in next iterations */
+ subelem->flags |= YIN_SUBELEM_PARSED;
+
+ switch (cur_stmt) {
+ /* call responsible function */
+ case LY_STMT_EXTENSION_INSTANCE:
+ ret = yin_parse_extension_instance(ctx, parent, parent_stmt,
+ (subelem->dest) ? *((LY_ARRAY_COUNT_TYPE *)subelem->dest) : 0, exts);
+ break;
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ ret = yin_parse_action(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ ret = yin_parse_any(ctx, cur_stmt, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_ARGUMENT:
+ ret = yin_parse_argument(ctx, parent, (struct yin_argument_meta *)subelem->dest, exts);
+ break;
+ case LY_STMT_AUGMENT:
+ ret = yin_parse_augment(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_BASE:
+ ret = yin_parse_base(ctx, parent_stmt, subelem->dest, exts);
+ break;
+ case LY_STMT_BELONGS_TO:
+ ret = yin_parse_belongs_to(ctx, (struct lysp_submodule *)subelem->dest, exts);
+ break;
+ case LY_STMT_BIT:
+ ret = yin_parse_bit(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_CASE:
+ ret = yin_parse_case(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_CHOICE:
+ ret = yin_parse_choice(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_CONFIG:
+ ret = yin_parse_config(ctx, (uint16_t *)subelem->dest, exts);
+ break;
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_REFERENCE:
+ ret = yin_parse_meta(ctx, parent, cur_stmt, (const char **)subelem->dest, exts);
+ break;
+ case LY_STMT_CONTAINER:
+ ret = yin_parse_container(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_DEFAULT:
+ ret = yin_parse_qname(ctx, cur_stmt, subelem, exts);
+ break;
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_KEY:
+ ret = yin_parse_simple_elem(ctx, parent, cur_stmt, subelem, YIN_ARG_VALUE, Y_STR_ARG, exts);
+ break;
+ case LY_STMT_PRESENCE:
+ ret = yin_parse_simple_elem(ctx, *(const char **)subelem->dest, cur_stmt, subelem, YIN_ARG_VALUE, Y_STR_ARG, exts);
+ break;
+ case LY_STMT_DEVIATE:
+ ret = yin_parse_deviate(ctx, (struct lysp_deviate **)subelem->dest);
+ break;
+ case LY_STMT_DEVIATION:
+ ret = yin_parse_deviation(ctx, (struct lysp_deviation **)subelem->dest);
+ break;
+ case LY_STMT_ENUM:
+ ret = yin_parse_enum(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_ERROR_MESSAGE:
+ ret = yin_parse_err_msg(ctx, parent, (const char **)subelem->dest, exts);
+ break;
+ case LY_STMT_EXTENSION:
+ ret = yin_parse_extension(ctx, (struct lysp_ext **)subelem->dest);
+ break;
+ case LY_STMT_FEATURE:
+ ret = yin_parse_feature(ctx, (struct lysp_feature **)subelem->dest);
+ break;
+ case LY_STMT_FRACTION_DIGITS:
+ ret = yin_parse_fracdigits(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_GROUPING:
+ ret = yin_parse_grouping(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_IDENTITY:
+ ret = yin_parse_identity(ctx, (struct lysp_ident **)subelem->dest);
+ break;
+ case LY_STMT_UNIQUE:
+ case LY_STMT_IF_FEATURE:
+ ret = yin_parse_qname(ctx, cur_stmt, subelem, exts);
+ break;
+ case LY_STMT_UNITS:
+ ret = yin_parse_simple_elem(ctx, *(const char **)subelem->dest, cur_stmt, subelem, YIN_ARG_NAME, Y_STR_ARG, exts);
+ break;
+ case LY_STMT_IMPORT:
+ ret = yin_parse_import(ctx, (struct import_meta *)subelem->dest);
+ break;
+ case LY_STMT_INCLUDE:
+ if ((parent_stmt == LY_STMT_SUBMODULE) && (PARSER_CUR_PMOD(ctx)->version == LYS_VERSION_1_1)) {
+ LOGWRN(PARSER_CTX(ctx), "YANG version 1.1 expects all includes in main module, includes in "
+ "submodules (%s) are not necessary.", ((struct lysp_submodule *)PARSER_CUR_PMOD(ctx))->name);
+ }
+ ret = yin_parse_include(ctx, (struct include_meta *)subelem->dest);
+ break;
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ ret = yin_parse_inout(ctx, cur_stmt, (struct inout_meta *)subelem->dest);
+ break;
+ case LY_STMT_LEAF:
+ ret = yin_parse_leaf(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_LEAF_LIST:
+ ret = yin_parse_leaflist(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_LENGTH:
+ ret = yin_parse_length(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_LIST:
+ ret = yin_parse_list(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_MANDATORY:
+ ret = yin_parse_mandatory(ctx, (uint16_t *)subelem->dest, exts);
+ break;
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ ret = yin_parse_minmax(ctx, parent_stmt, cur_stmt, subelem->dest);
+ break;
+ case LY_STMT_MODIFIER:
+ ret = yin_parse_modifier(ctx, parent, (const char **)subelem->dest, exts);
+ break;
+ case LY_STMT_MUST:
+ ret = yin_parse_must(ctx, (struct lysp_restr **)subelem->dest);
+ break;
+ case LY_STMT_NAMESPACE:
+ ret = yin_parse_simple_elem(ctx, parent, cur_stmt, subelem, YIN_ARG_URI, Y_STR_ARG, exts);
+ break;
+ case LY_STMT_NOTIFICATION:
+ ret = yin_parse_notification(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_ORDERED_BY:
+ ret = yin_parse_orderedby(ctx, parent, (uint16_t *)subelem->dest, exts);
+ break;
+ case LY_STMT_PATH:
+ ret = yin_parse_path(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_PATTERN:
+ ret = yin_parse_pattern(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_VALUE:
+ case LY_STMT_POSITION:
+ ret = yin_parse_value_pos(ctx, cur_stmt, (struct lysp_type_enum *)subelem->dest);
+ break;
+ case LY_STMT_PREFIX:
+ ret = yin_parse_simple_elem(ctx, *(const char **)subelem->dest, cur_stmt, subelem, YIN_ARG_VALUE,
+ Y_IDENTIF_ARG, exts);
+ break;
+ case LY_STMT_RANGE:
+ ret = yin_parse_range(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_REFINE:
+ ret = yin_parse_refine(ctx, (struct lysp_refine **)subelem->dest);
+ break;
+ case LY_STMT_REQUIRE_INSTANCE:
+ ret = yin_pasrse_reqinstance(ctx, (struct lysp_type *)subelem->dest);
+ break;
+ case LY_STMT_REVISION:
+ ret = yin_parse_revision(ctx, (struct lysp_revision **)subelem->dest);
+ break;
+ case LY_STMT_REVISION_DATE:
+ ret = yin_parse_revision_date(ctx, (char *)subelem->dest, exts);
+ break;
+ case LY_STMT_STATUS:
+ ret = yin_parse_status(ctx, (uint16_t *)subelem->dest, exts);
+ break;
+ case LY_STMT_TYPE:
+ ret = yin_parse_type(ctx, parent_stmt, subelem);
+ break;
+ case LY_STMT_TYPEDEF:
+ ret = yin_parse_typedef(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_USES:
+ ret = yin_parse_uses(ctx, (struct tree_node_meta *)subelem->dest);
+ break;
+ case LY_STMT_WHEN:
+ ret = yin_parse_when(ctx, (struct lysp_when **)subelem->dest);
+ break;
+ case LY_STMT_YANG_VERSION:
+ ret = yin_parse_yangversion(ctx, parent, (uint8_t *)subelem->dest, exts);
+ break;
+ case LY_STMT_YIN_ELEMENT:
+ ret = yin_parse_yin_element(ctx, parent, (uint16_t *)subelem->dest, exts);
+ break;
+ case LY_STMT_ARG_TEXT:
+ case LY_STMT_ARG_VALUE:
+ /* TODO what to do with content/attributes? */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ while (ctx->xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+ ret = yin_parse_content(ctx, NULL, 0, parent, cur_stmt, (const char **)subelem->dest, NULL);
+ break;
+ default:
+ LOGINT(ctx->xmlctx->ctx);
+ ret = LY_EINT;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ subelem = NULL;
+
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+ } else {
+ LY_CHECK_RET(ret);
+ /* elements with text or none content */
+ /* save text content, if text_content isn't set, it's just ignored */
+ /* no resources are allocated in this branch, no need to use cleanup label */
+ LY_CHECK_RET(yin_validate_value(ctx, Y_STR_ARG));
+ if (text_content) {
+ INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, *text_content);
+ LY_CHECK_RET(!*text_content, LY_EMEM);
+ }
+
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
+ }
+
+ /* mandatory subelements are checked only after whole element was succesfully parsed */
+ LY_CHECK_RET(yin_check_subelem_mandatory_constraint(ctx, subelem_info, subelem_info_size, parent_stmt));
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Parse module element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[out] mod Parsed module structure.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_mod(struct lysp_yin_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ const struct lysp_submodule *dup;
+ size_t subelems_size;
+
+ mod->is_submod = 0;
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &mod->mod->name, Y_IDENTIF_ARG, LY_STMT_MODULE));
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 28, NULL, &subelems,
+ LY_STMT_ANYDATA, &mod->data, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &mod->data, 0,
+ LY_STMT_AUGMENT, &mod->augments, 0,
+ LY_STMT_CHOICE, &mod->data, 0,
+ LY_STMT_CONTACT, &mod->mod->contact, YIN_SUBELEM_UNIQUE,
+ LY_STMT_CONTAINER, &mod->data, 0,
+ LY_STMT_DESCRIPTION, &mod->mod->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_DEVIATION, &mod->deviations, 0,
+ LY_STMT_EXTENSION, &mod->extensions, 0,
+ LY_STMT_FEATURE, &mod->features, 0,
+ LY_STMT_GROUPING, &mod->groupings, 0,
+ LY_STMT_IDENTITY, &mod->identities, 0,
+ LY_STMT_IMPORT, mod->mod->prefix, &mod->imports, 0,
+ LY_STMT_INCLUDE, mod->mod->name, &mod->includes, 0,
+ LY_STMT_LEAF, &mod->data, 0,
+ LY_STMT_LEAF_LIST, &mod->data, 0,
+ LY_STMT_LIST, &mod->data, 0,
+ LY_STMT_NAMESPACE, &mod->mod->ns, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE,
+ LY_STMT_NOTIFICATION, &mod->notifs, 0,
+ LY_STMT_ORGANIZATION, &mod->mod->org, YIN_SUBELEM_UNIQUE,
+ LY_STMT_PREFIX, &mod->mod->prefix, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE,
+ LY_STMT_REFERENCE, &mod->mod->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REVISION, &mod->revs, 0,
+ LY_STMT_RPC, &mod->rpcs, 0,
+ LY_STMT_TYPEDEF, &mod->typedefs, 0,
+ LY_STMT_USES, &mod->data, 0,
+ LY_STMT_YANG_VERSION, &mod->version, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+
+ ret = yin_parse_content(ctx, subelems, subelems_size, mod, LY_STMT_MODULE, NULL, &mod->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, mod->exts));
+
+ /* submodules share the namespace with the module names, so there must not be
+ * a submodule of the same name in the context, no need for revision matching */
+ dup = ly_ctx_get_submodule_latest(ctx->xmlctx->ctx, mod->mod->name);
+ if (dup) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_NAME2_COL, "module", "submodule", mod->mod->name);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse submodule element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] mod_attrs Attributes of submodule element.
+ * @param[out] submod Parsed submodule structure.
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_submod(struct lysp_yin_ctx *ctx, struct lysp_submodule *submod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement *subelems = NULL;
+ const struct lysp_submodule *dup;
+ size_t subelems_size;
+
+ submod->is_submod = 1;
+
+ LY_CHECK_RET(lyxml_ctx_next(ctx->xmlctx));
+ LY_CHECK_RET(yin_parse_attribute(ctx, YIN_ARG_NAME, &submod->name, Y_IDENTIF_ARG, LY_STMT_SUBMODULE));
+ LY_CHECK_RET(subelems_allocator(ctx, subelems_size = 27, NULL, &subelems,
+ LY_STMT_ANYDATA, &submod->data, YIN_SUBELEM_VER2,
+ LY_STMT_ANYXML, &submod->data, 0,
+ LY_STMT_AUGMENT, &submod->augments, 0,
+ LY_STMT_BELONGS_TO, submod, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE,
+ LY_STMT_CHOICE, &submod->data, 0,
+ LY_STMT_CONTACT, &submod->contact, YIN_SUBELEM_UNIQUE,
+ LY_STMT_CONTAINER, &submod->data, 0,
+ LY_STMT_DESCRIPTION, &submod->dsc, YIN_SUBELEM_UNIQUE,
+ LY_STMT_DEVIATION, &submod->deviations, 0,
+ LY_STMT_EXTENSION, &submod->extensions, 0,
+ LY_STMT_FEATURE, &submod->features, 0,
+ LY_STMT_GROUPING, &submod->groupings, 0,
+ LY_STMT_IDENTITY, &submod->identities, 0,
+ LY_STMT_IMPORT, submod->prefix, &submod->imports, 0,
+ LY_STMT_INCLUDE, submod->name, &submod->includes, 0,
+ LY_STMT_LEAF, &submod->data, 0,
+ LY_STMT_LEAF_LIST, &submod->data, 0,
+ LY_STMT_LIST, &submod->data, 0,
+ LY_STMT_NOTIFICATION, &submod->notifs, 0,
+ LY_STMT_ORGANIZATION, &submod->org, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REFERENCE, &submod->ref, YIN_SUBELEM_UNIQUE,
+ LY_STMT_REVISION, &submod->revs, 0,
+ LY_STMT_RPC, &submod->rpcs, 0,
+ LY_STMT_TYPEDEF, &submod->typedefs, 0,
+ LY_STMT_USES, &submod->data, 0,
+ LY_STMT_YANG_VERSION, &submod->version, YIN_SUBELEM_UNIQUE,
+ LY_STMT_EXTENSION_INSTANCE, NULL, 0));
+
+ ret = yin_parse_content(ctx, subelems, subelems_size, submod, LY_STMT_SUBMODULE, NULL, &submod->exts);
+ subelems_deallocator(subelems_size, subelems);
+ LY_CHECK_RET(ret);
+
+ /* store extension instance array (no realloc anymore) to find the plugin records and finish parsing */
+ LY_CHECK_RET(yin_unres_exts_add(ctx, submod->exts));
+
+ /* submodules share the namespace with the module names, so there must not be
+ * a submodule of the same name in the context, no need for revision matching */
+ dup = ly_ctx_get_submodule_latest(ctx->xmlctx->ctx, submod->name);
+ if (dup && strcmp(dup->mod->name, submod->mod->name)) {
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_NAME_COL, "submodules", dup->name);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+yin_parse_submodule(struct lysp_yin_ctx **yin_ctx, struct ly_ctx *ctx, struct lysp_ctx *main_ctx,
+ struct ly_in *in, struct lysp_submodule **submod)
+{
+ enum ly_stmt kw = LY_STMT_NONE;
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_submodule *mod_p = NULL;
+ struct lysf_ctx fctx = {.ctx = ctx};
+
+ assert(yin_ctx && ctx && main_ctx && in && submod);
+
+ /* create context */
+ *yin_ctx = calloc(1, sizeof **yin_ctx);
+ LY_CHECK_ERR_RET(!(*yin_ctx), LOGMEM(ctx), LY_EMEM);
+ (*yin_ctx)->format = LYS_IN_YIN;
+ (*yin_ctx)->main_ctx = main_ctx;
+ LY_CHECK_RET(lyxml_ctx_new(ctx, in, &(*yin_ctx)->xmlctx));
+
+ mod_p = calloc(1, sizeof *mod_p);
+ LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ mod_p->mod = PARSER_CUR_PMOD(main_ctx)->mod;
+ mod_p->parsing = 1;
+
+ /* use main context parsed mods adding the current one */
+ (*yin_ctx)->parsed_mods = main_ctx->parsed_mods;
+ ly_set_add((*yin_ctx)->parsed_mods, mod_p, 1, NULL);
+
+ /* check submodule */
+ kw = yin_match_keyword(*yin_ctx, (*yin_ctx)->xmlctx->name, (*yin_ctx)->xmlctx->name_len, (*yin_ctx)->xmlctx->prefix,
+ (*yin_ctx)->xmlctx->prefix_len, LY_STMT_NONE);
+ if (kw == LY_STMT_MODULE) {
+ LOGERR(ctx, LY_EDENIED, "Input data contains module when a submodule is expected.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (kw != LY_STMT_SUBMODULE) {
+ LOGVAL_PARSER((struct lysp_ctx *)*yin_ctx, LY_VCODE_MOD_SUBOMD, lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ ret = yin_parse_submod(*yin_ctx, mod_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* skip possible trailing whitespaces at end of the input */
+ while (isspace(in->current[0])) {
+ if (in->current[0] == '\n') {
+ LY_IN_NEW_LINE(in);
+ }
+ ly_in_skip(in, 1);
+ }
+ if (in->current[0]) {
+ LOGVAL_PARSER((struct lysp_ctx *)*yin_ctx, LY_VCODE_TRAILING_SUBMOD, 15, in->current,
+ strlen(in->current) > 15 ? "..." : "");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ mod_p->parsing = 0;
+ *submod = mod_p;
+
+cleanup:
+ LOG_LOCBACK(0, 0, 0, 1);
+ if (ret) {
+ lysp_module_free(&fctx, (struct lysp_module *)mod_p);
+ lysp_yin_ctx_free(*yin_ctx);
+ *yin_ctx = NULL;
+ }
+ return ret;
+}
+
+LY_ERR
+yin_parse_module(struct lysp_yin_ctx **yin_ctx, struct ly_in *in, struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum ly_stmt kw = LY_STMT_NONE;
+ struct lysp_module *mod_p = NULL;
+ struct lysf_ctx fctx = {.ctx = mod->ctx};
+
+ /* create context */
+ *yin_ctx = calloc(1, sizeof **yin_ctx);
+ LY_CHECK_ERR_RET(!(*yin_ctx), LOGMEM(mod->ctx), LY_EMEM);
+ (*yin_ctx)->format = LYS_IN_YIN;
+ LY_CHECK_ERR_RET(ly_set_new(&(*yin_ctx)->parsed_mods), free(*yin_ctx); LOGMEM(mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lyxml_ctx_new(mod->ctx, in, &(*yin_ctx)->xmlctx));
+ (*yin_ctx)->main_ctx = (struct lysp_ctx *)(*yin_ctx);
+
+ mod_p = calloc(1, sizeof *mod_p);
+ LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(mod->ctx), cleanup);
+ mod_p->mod = mod;
+ ly_set_add((*yin_ctx)->parsed_mods, mod_p, 1, NULL);
+
+ /* check module */
+ kw = yin_match_keyword(*yin_ctx, (*yin_ctx)->xmlctx->name, (*yin_ctx)->xmlctx->name_len, (*yin_ctx)->xmlctx->prefix,
+ (*yin_ctx)->xmlctx->prefix_len, LY_STMT_NONE);
+ if (kw == LY_STMT_SUBMODULE) {
+ LOGERR(mod->ctx, LY_EDENIED, "Input data contains submodule which cannot be parsed directly without its main module.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (kw != LY_STMT_MODULE) {
+ LOGVAL_PARSER((struct lysp_ctx *)*yin_ctx, LY_VCODE_MOD_SUBOMD, lyplg_ext_stmt2str(kw));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parse module substatements */
+ ret = yin_parse_mod(*yin_ctx, mod_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* skip possible trailing whitespaces at end of the input */
+ while (isspace(in->current[0])) {
+ if (in->current[0] == '\n') {
+ LY_IN_NEW_LINE(in);
+ }
+ ly_in_skip(in, 1);
+ }
+ if (in->current[0]) {
+ LOGVAL_PARSER((struct lysp_ctx *)*yin_ctx, LY_VCODE_TRAILING_MOD, 15, in->current,
+ strlen(in->current) > 15 ? "..." : "");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ mod->parsed = mod_p;
+
+cleanup:
+ LOG_LOCBACK(0, 0, 0, 1);
+ if (ret) {
+ lysp_module_free(&fctx, mod_p);
+ lysp_yin_ctx_free(*yin_ctx);
+ *yin_ctx = NULL;
+ }
+ return ret;
+}
diff --git a/src/path.c b/src/path.c
new file mode 100644
index 0000000..72e5fb5
--- /dev/null
+++ b/src/path.c
@@ -0,0 +1,1193 @@
+/**
+ * @file path.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Path functions
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#include "path.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+#include "plugins_types.h"
+#include "schema_compile.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+#define LOGVAL_P(CTX, CUR_NODE, CODE, ...) ly_vlog(CTX, (CUR_NODE) ? LY_VLOG_LYSC : LY_VLOG_NONE, CUR_NODE, CODE, ##__VA_ARGS__)
+
+/**
+ * @brief Check predicate syntax.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] cur_node Current (original context) node.
+ * @param[in] exp Parsed predicate.
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @param[in] prefix Prefix option.
+ * @param[in] pred Predicate option.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lyxp_expr *exp,
+ uint32_t *tok_idx, uint8_t prefix, uint8_t pred)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_set *set = NULL;
+ uint32_t i;
+ const char *name;
+ size_t name_len;
+
+ LOG_LOCSET(cur_node, NULL, NULL, NULL);
+
+ if (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1)) {
+ /* '[' */
+
+ if (((pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_KEYS)) &&
+ !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) {
+ ret = ly_set_new(&set);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ do {
+ /* NameTest is always expected here */
+ LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), token_error);
+
+ /* check prefix based on the options */
+ name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]);
+ if ((prefix == LY_PATH_PREFIX_MANDATORY) && !name) {
+ LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", exp->tok_len[*tok_idx],
+ exp->expr + exp->tok_pos[*tok_idx]);
+ goto token_error;
+ } else if ((prefix == LY_PATH_PREFIX_STRICT_INHERIT) && name) {
+ LOGVAL(ctx, LYVE_XPATH, "Redundant prefix for \"%.*s\" in path.", exp->tok_len[*tok_idx],
+ exp->expr + exp->tok_pos[*tok_idx]);
+ goto token_error;
+ }
+ if (!name) {
+ name = exp->expr + exp->tok_pos[*tok_idx];
+ name_len = exp->tok_len[*tok_idx];
+ } else {
+ ++name;
+ name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx]));
+ }
+
+ /* check whether it was not already specified */
+ for (i = 0; i < set->count; ++i) {
+ /* all the keys must be from the same module so this comparison should be fine */
+ if (!strncmp(set->objs[i], name, name_len) &&
+ lysp_check_identifierchar(NULL, ((char *)set->objs[i])[name_len], 0, NULL)) {
+ LOGVAL(ctx, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.", (int)name_len, name);
+ goto token_error;
+ }
+ }
+
+ /* add it into the set */
+ ret = ly_set_add(set, (void *)name, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* NameTest */
+ ++(*tok_idx);
+
+ /* '=' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+
+ /* Literal or Number */
+ LY_CHECK_GOTO(lyxp_next_token2(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL, LYXP_TOKEN_NUMBER), token_error);
+
+ /* ']' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error);
+
+ /* '[' */
+ } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1));
+
+ } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DOT)) {
+ /* '.' */
+
+ /* '=' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+
+ /* Literal or Number */
+ LY_CHECK_GOTO(lyxp_next_token2(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL, LYXP_TOKEN_NUMBER), token_error);
+
+ /* ']' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error);
+
+ } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_NUMBER)) {
+ /* Number */
+
+ /* check for index 0 */
+ if (!atoi(exp->expr + exp->tok_pos[*tok_idx - 1])) {
+ LOGVAL(ctx, LYVE_XPATH, "Invalid positional predicate \"%.*s\".", (int)exp->tok_len[*tok_idx - 1],
+ exp->expr + exp->tok_pos[*tok_idx - 1]);
+ goto token_error;
+ }
+
+ /* ']' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error);
+
+ } else if ((pred == LY_PATH_PRED_LEAFREF) && !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) {
+ assert(prefix == LY_PATH_PREFIX_OPTIONAL);
+ ret = ly_set_new(&set);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ do {
+ /* NameTest is always expected here */
+ LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), token_error);
+
+ name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]);
+ if (!name) {
+ name = exp->expr + exp->tok_pos[*tok_idx];
+ name_len = exp->tok_len[*tok_idx];
+ } else {
+ ++name;
+ name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx]));
+ }
+
+ /* check whether it was not already specified */
+ for (i = 0; i < set->count; ++i) {
+ /* all the keys must be from the same module so this comparison should be fine */
+ if (!strncmp(set->objs[i], name, name_len) &&
+ lysp_check_identifierchar(NULL, ((char *)set->objs[i])[name_len], 0, NULL)) {
+ LOGVAL(ctx, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.", (int)name_len, name);
+ goto token_error;
+ }
+ }
+
+ /* add it into the set */
+ ret = ly_set_add(set, (void *)name, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* NameTest */
+ ++(*tok_idx);
+
+ /* '=' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+
+ /* FuncName */
+ LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME), token_error);
+ if ((exp->tok_len[*tok_idx] != ly_strlen_const("current")) ||
+ strncmp(exp->expr + exp->tok_pos[*tok_idx], "current", ly_strlen_const("current"))) {
+ LOGVAL(ctx, LYVE_XPATH, "Invalid function \"%.*s\" invocation in path.",
+ exp->tok_len[*tok_idx], exp->expr + exp->tok_pos[*tok_idx]);
+ goto token_error;
+ }
+ ++(*tok_idx);
+
+ /* '(' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), token_error);
+
+ /* ')' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), token_error);
+
+ /* '/' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), token_error);
+
+ /* '..' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_DDOT), token_error);
+ do {
+ /* '/' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), token_error);
+ } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DDOT));
+
+ /* NameTest */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), token_error);
+
+ /* '/' */
+ while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_OPER_PATH)) {
+ /* NameTest */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), token_error);
+ }
+
+ /* ']' */
+ LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error);
+
+ /* '[' */
+ } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1));
+
+ } else if (lyxp_check_token(ctx, exp, *tok_idx, 0)) {
+ /* unexpected EOF */
+ goto token_error;
+ } else {
+ /* invalid token */
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), exp->expr + exp->tok_pos[*tok_idx]);
+ goto token_error;
+ }
+ }
+
+cleanup:
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ ly_set_free(set, NULL);
+ return ret;
+
+token_error:
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ ly_set_free(set, NULL);
+ return LY_EVALID;
+}
+
+LY_ERR
+ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
+ ly_bool lref, uint8_t begin, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ uint32_t tok_idx, cur_len;
+ const char *cur_node, *prev_prefix = NULL, *ptr;
+
+ assert((begin == LY_PATH_BEGIN_ABSOLUTE) || (begin == LY_PATH_BEGIN_EITHER));
+ assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY) ||
+ (prefix == LY_PATH_PREFIX_STRICT_INHERIT));
+ assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
+
+ LOG_LOCSET(ctx_node, NULL, NULL, NULL);
+
+ /* parse as a generic XPath expression */
+ LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 1, &exp), error);
+ tok_idx = 0;
+
+ if (begin == LY_PATH_BEGIN_EITHER) {
+ /* is the path relative? */
+ if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) {
+ /* relative path check specific to leafref */
+ if (lref) {
+ /* mandatory '..' */
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_DDOT), ret = LY_EVALID, error);
+
+ do {
+ /* '/' */
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+
+ /* optional '..' */
+ } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT));
+ }
+ }
+ } else {
+ /* '/' */
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+ }
+
+ do {
+ /* NameTest */
+ LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), ret = LY_EVALID, error);
+
+ /* check prefix based on the options */
+ cur_node = exp->expr + exp->tok_pos[tok_idx];
+ cur_len = exp->tok_len[tok_idx];
+ if (prefix == LY_PATH_PREFIX_MANDATORY) {
+ if (!strnstr(cur_node, ":", cur_len)) {
+ LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node);
+ ret = LY_EVALID;
+ goto error;
+ }
+ } else if (prefix == LY_PATH_PREFIX_STRICT_INHERIT) {
+ if (!prev_prefix) {
+ /* the first node must have a prefix */
+ if (!strnstr(cur_node, ":", cur_len)) {
+ LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ /* remember the first prefix */
+ prev_prefix = cur_node;
+ } else {
+ /* the prefix must be different, if any */
+ ptr = strnstr(cur_node, ":", cur_len);
+ if (ptr) {
+ if (!strncmp(prev_prefix, cur_node, ptr - cur_node) && (prev_prefix[ptr - cur_node] == ':')) {
+ LOGVAL(ctx, LYVE_XPATH, "Duplicate prefix for \"%.*s\" in path.", cur_len, cur_node);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ /* remember this next prefix */
+ prev_prefix = cur_node;
+ }
+ }
+ }
+
+ ++tok_idx;
+
+ /* Predicate* */
+ LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, ctx_node, exp, &tok_idx, prefix, pred), error);
+
+ /* '/' */
+ } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH));
+
+ /* trailing token check */
+ if (exp->used > tok_idx) {
+ LOGVAL(ctx, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of path.", exp->expr + exp->tok_pos[tok_idx]);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ *expr = exp;
+
+ LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0);
+ return LY_SUCCESS;
+
+error:
+ lyxp_expr_free(ctx, exp);
+ LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0);
+ return ret;
+}
+
+LY_ERR
+ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
+ size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ uint32_t tok_idx;
+
+ assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY));
+ assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
+
+ LOG_LOCSET(cur_node, NULL, NULL, NULL);
+
+ /* parse as a generic XPath expression */
+ LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error);
+ tok_idx = 0;
+
+ LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, cur_node, exp, &tok_idx, prefix, pred), error);
+
+ /* trailing token check */
+ if (exp->used > tok_idx) {
+ LOGVAL(ctx, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of predicate.",
+ exp->expr + exp->tok_pos[tok_idx]);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ *expr = exp;
+
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ return LY_SUCCESS;
+
+error:
+ lyxp_expr_free(ctx, exp);
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ return ret;
+}
+
+/**
+ * @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_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.
+ * @param[in] expr Parsed path.
+ * @param[in] tok_idx Index in @p expr.
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] top_ext Optional top-level extension to use for searching the schema node.
+ * @param[in] getnext_opts Options to be used for ::lys_getnext() calls.
+ * @param[out] snode Resolved schema node.
+ * @param[out] ext Optional extension instance of @p snode, if any.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
+ const struct lysc_node *prev_ctx_node, const struct lyxp_expr *expr, uint32_t tok_idx, LY_VALUE_FORMAT format,
+ 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;
+ const struct lys_module *mod = NULL;
+ struct lysc_ext_instance *e = NULL;
+ const char *pref, *name;
+ size_t len, name_len;
+
+ assert(expr->tokens[tok_idx] == LYXP_TOKEN_NAMETEST);
+
+ *snode = NULL;
+ if (ext) {
+ *ext = NULL;
+ }
+
+ /* get prefix */
+ if ((pref = strnstr(expr->expr + expr->tok_pos[tok_idx], ":", expr->tok_len[tok_idx]))) {
+ len = pref - (expr->expr + expr->tok_pos[tok_idx]);
+ pref = expr->expr + expr->tok_pos[tok_idx];
+ } else {
+ len = 0;
+ }
+
+ /* set name */
+ if (pref) {
+ name = pref + len + 1;
+ name_len = expr->tok_len[tok_idx] - len - 1;
+ } else {
+ name = expr->expr + expr->tok_pos[tok_idx];
+ name_len = expr->tok_len[tok_idx];
+ }
+
+ /* find node module */
+ if (pref) {
+ LOG_LOCSET(cur_node, NULL, NULL, 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;
+ }
+ }
+
+ 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;
+ } else if (!mod->implemented) {
+ LOGVAL(ctx, LYVE_XPATH, "Not implemented module \"%s\" in path.", mod->name);
+ ret = LY_EVALID;
+ goto error;
+ }
+
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ } else {
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ if (!cur_mod) {
+ LOGINT_RET(ctx);
+ }
+ /* use current module */
+ mod = cur_mod;
+ break;
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ if (!prev_ctx_node) {
+ LOGINT_RET(ctx);
+ }
+ /* inherit module of the previous node */
+ mod = prev_ctx_node->module;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ /* not really defined or accepted */
+ LOGINT_RET(ctx);
+ }
+ }
+
+ /* find schema node */
+ if (!prev_ctx_node && top_ext) {
+ *snode = lysc_ext_find_node(top_ext, mod, name, name_len, 0, getnext_opts);
+ } 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);
+ }
+ }
+ if (!(*snode)) {
+ LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name);
+ return LY_ENOTFOUND;
+ }
+
+success:
+ if (ext) {
+ *ext = e;
+ }
+ return LY_SUCCESS;
+
+error:
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ return ret;
+}
+
+LY_ERR
+ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
+ const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format,
+ void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_path_predicate *p;
+ const struct lysc_node *key;
+ const char *val;
+ size_t val_len, key_count;
+
+ assert(ctx && ctx_node);
+
+ LOG_LOCSET(cur_node, NULL, NULL, NULL);
+
+ *pred_type = 0;
+
+ if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
+ /* '[', no predicate */
+ goto cleanup; /* LY_SUCCESS */
+ }
+
+ 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.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (ctx_node->flags & LYS_KEYLESS) {
+ LOGVAL(ctx, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ ret = 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));
+ 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;
+ goto cleanup;
+ }
+ ++(*tok_idx);
+
+ if (!*pred_type) {
+ /* new predicate */
+ *pred_type = LY_PATH_PREDTYPE_LIST;
+ }
+ assert(*pred_type == LY_PATH_PREDTYPE_LIST);
+ LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+ p->key = key;
+
+ /* '=' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
+ ++(*tok_idx);
+
+ /* Literal or Number */
+ assert((expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) || (expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER));
+ if (expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) {
+ /* skip quotes */
+ val = expr->expr + expr->tok_pos[*tok_idx] + 1;
+ val_len = expr->tok_len[*tok_idx] - 2;
+ } else {
+ val = expr->expr + expr->tok_pos[*tok_idx];
+ val_len = expr->tok_len[*tok_idx];
+ }
+
+ /* store the value */
+ LOG_LOCSET(key, NULL, NULL, NULL);
+ ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, NULL, format,
+ prefix_data, LYD_HINT_DATA, key, NULL);
+ LOG_LOCBACK(key ? 1 : 0, 0, 0, 0);
+ LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
+ ++(*tok_idx);
+
+ /* "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);
+
+ /* ']' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
+ ++(*tok_idx);
+
+ /* another predicate follows? */
+ } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
+
+ /* check that all keys were set */
+ key_count = 0;
+ for (key = lysc_node_child(ctx_node); key && (key->flags & LYS_KEY); key = key->next) {
+ ++key_count;
+ }
+ 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.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, *predicates);
+ *predicates = NULL;
+ ret = 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.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ++(*tok_idx);
+
+ /* new predicate */
+ *pred_type = LY_PATH_PREDTYPE_LEAFLIST;
+ LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+
+ /* '=' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
+ ++(*tok_idx);
+
+ /* Literal or Number */
+ assert((expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) || (expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER));
+ if (expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) {
+ /* skip quotes */
+ val = expr->expr + expr->tok_pos[*tok_idx] + 1;
+ val_len = expr->tok_len[*tok_idx] - 2;
+ } else {
+ val = expr->expr + expr->tok_pos[*tok_idx];
+ val_len = expr->tok_len[*tok_idx];
+ }
+
+ /* store the value */
+ LOG_LOCSET(ctx_node, NULL, NULL, NULL);
+ ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, NULL, format,
+ prefix_data, LYD_HINT_DATA, ctx_node, NULL);
+ LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0);
+ LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
+ ++(*tok_idx);
+
+ /* "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);
+
+ /* ']' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
+ ++(*tok_idx);
+ } 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.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ 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.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ goto cleanup;
+ }
+
+ /* new predicate */
+ *pred_type = LY_PATH_PREDTYPE_POSITION;
+ LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+
+ /* syntax was already checked */
+ p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&val, LY_BASE_DEC);
+ ++(*tok_idx);
+
+ /* ']' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
+ ++(*tok_idx);
+ }
+
+cleanup:
+ LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Compile leafref predicate. Actually, it is only checked.
+ *
+ * @param[in] ctx_node Context node, node for which the predicate is defined.
+ * @param[in] cur_node Current (original context) node.
+ * @param[in] expr Parsed path.
+ * @param[in,out] tok_idx Index in @p expr, is adjusted for parsed tokens.
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @return LY_ERR value.
+ */
+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;
+ const struct lysc_node *key, *node, *node2;
+ struct ly_ctx *ctx = cur_node->module->ctx;
+
+ if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
+ /* '[', no predicate */
+ goto cleanup; /* LY_SUCCESS */
+ }
+
+ if (ctx_node->nodetype != LYS_LIST) {
+ LOGVAL(ctx, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (ctx_node->flags & LYS_KEYLESS) {
+ LOGVAL(ctx, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
+ lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
+ ret = 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,
+ NULL, 0, &key, NULL);
+ LY_CHECK_GOTO(ret, 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;
+ goto cleanup;
+ }
+ ++(*tok_idx);
+
+ /* we are not actually compiling, throw the key away */
+ (void)key;
+
+ /* '=' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
+ ++(*tok_idx);
+
+ /* FuncName */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_FUNCNAME);
+ ++(*tok_idx);
+
+ /* evaluating from the "current()" node */
+ node = cur_node;
+
+ /* '(' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
+ ++(*tok_idx);
+
+ /* ')' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
+ ++(*tok_idx);
+
+ do {
+ /* '/' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
+ ++(*tok_idx);
+
+ /* go to parent */
+ if (!node) {
+ LOGVAL(ctx, LYVE_XPATH, "Too many parent references in path.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ node = lysc_data_parent(node);
+
+ /* '..' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_DDOT);
+ ++(*tok_idx);
+ } while (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_DDOT);
+
+ do {
+ /* '/' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
+ ++(*tok_idx);
+
+ /* 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));
+ 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.",
+ lys_nodetype2str(node->nodetype), node->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* we are not actually compiling, throw the rightside node away */
+ (void)node;
+
+ /* ']' */
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
+ ++(*tok_idx);
+
+ /* another predicate follows? */
+ } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
+
+cleanup:
+ return (ret == LY_ENOTFOUND) ? LY_EVALID : ret;
+}
+
+/**
+ * @brief Compile path into ly_path structure. Any predicates of a leafref are only checked, not compiled.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] cur_mod Current module of the path (where it was "instantiated"), ignored of @p lref. Used for nodes
+ * without a prefix for ::LY_VALUE_SCHEMA and ::LY_VALUE_SCHEMA_RESOLVED format.
+ * @param[in] ctx_node Optional context node, mandatory of @p lref.
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find the top-level
+ * node inside the extension instance instead of a module. Note that this is the case not only if the @p ctx_node is NULL,
+ * but also if the relative path starting in @p ctx_node reaches the document root via double dots.
+ * @param[in] expr Parsed path.
+ * @param[in] lref Whether leafref is being compiled or not.
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
+ * @param[in] limit_access_tree Whether to limit accessible tree as described in
+ * [XPath context](https://datatracker.ietf.org/doc/html/rfc7950#section-6.4.1).
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[out] path Compiled path.
+ * @return LY_ERECOMPILE, only if @p lref.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, ly_bool lref, uint8_t oper, uint8_t target,
+ ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t tok_idx = 0, getnext_opts;
+ const struct lysc_node *node2, *cur_node, *op;
+ struct ly_path *p = NULL;
+ struct lysc_ext_instance *ext = NULL;
+
+ assert(ctx);
+ assert(!lref || ctx_node);
+ assert((oper == LY_PATH_OPER_INPUT) || (oper == LY_PATH_OPER_OUTPUT));
+ assert((target == LY_PATH_TARGET_SINGLE) || (target == LY_PATH_TARGET_MANY));
+
+ if (!limit_access_tree) {
+ op = NULL;
+ } else {
+ /* find operation, if we are in any */
+ for (op = ctx_node; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
+ }
+
+ *path = NULL;
+
+ /* remember original context node */
+ cur_node = ctx_node;
+ LOG_LOCSET(cur_node, NULL, NULL, NULL);
+
+ if (oper == LY_PATH_OPER_OUTPUT) {
+ getnext_opts = LYS_GETNEXT_OUTPUT;
+ } else {
+ getnext_opts = 0;
+ }
+
+ if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
+ /* absolute path */
+ ctx_node = NULL;
+
+ ++tok_idx;
+ } else {
+ /* relative path */
+ if (!ctx_node) {
+ LOGVAL(ctx, LYVE_XPATH, "No initial schema parent for a relative path.");
+ ret = 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;
+ goto cleanup;
+ }
+
+ /* get parent */
+ ctx_node = lysc_data_parent(ctx_node);
+
+ ++tok_idx;
+
+ assert(expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH);
+ ++tok_idx;
+ }
+ }
+
+ 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.",
+ lys_nodetype2str(p->node->nodetype), p->node->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* NameTest */
+ LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, expr, tok_idx, LYXP_TOKEN_NAMETEST), ret = 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,
+ 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;
+ goto cleanup;
+ }
+ ctx_node = node2;
+
+ /* new path segment */
+ LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, 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);
+ } else {
+ ret = ly_path_compile_predicate(ctx, cur_node, cur_mod, ctx_node, expr, &tok_idx, format, prefix_data,
+ &p->predicates, &p->pred_type);
+ }
+ LY_CHECK_GOTO(ret, 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;
+ 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.",
+ lys_nodetype2str(p->node->nodetype), p->node->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+cleanup:
+ if (ret) {
+ ly_path_free(ctx, *path);
+ *path = NULL;
+ }
+ LOG_LOCBACK(1, 0, 0, 0);
+ return (ret == LY_ENOTFOUND) ? LY_EVALID : ret;
+}
+
+LY_ERR
+ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
+{
+ return _ly_path_compile(ctx, cur_mod, ctx_node, top_ext, expr, 0, oper, target, limit_access_tree, format,
+ prefix_data, path);
+}
+
+LY_ERR
+ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lysc_ext_instance *top_ext,
+ const struct lyxp_expr *expr, uint8_t oper, uint8_t target, LY_VALUE_FORMAT format, void *prefix_data,
+ struct ly_path **path)
+{
+ return _ly_path_compile(ctx, ctx_node->module, ctx_node, top_ext, expr, 1, oper, target, 1, format, prefix_data, path);
+}
+
+LY_ERR
+ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx,
+ struct lyd_node **match)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_node *prev_node = NULL, *elem, *node = NULL, *target;
+ uint64_t pos;
+
+ assert(path && start);
+
+ if (lysc_data_parent(path[0].node)) {
+ /* relative path, start from the parent children */
+ start = lyd_child(start);
+ } else {
+ /* absolute path, start from the first top-level sibling */
+ while (start->parent) {
+ start = lyd_parent(start);
+ }
+ while (start->prev->next) {
+ start = start->prev;
+ }
+ }
+
+ LY_ARRAY_FOR(path, u) {
+ switch (path[u].pred_type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* we cannot use hashes and want an instance on a specific position */
+ pos = 1;
+ node = NULL;
+ LYD_LIST_FOR_INST(start, path[u].node, elem) {
+ if (pos == path[u].predicates[0].position) {
+ node = elem;
+ break;
+ }
+ ++pos;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* we will use hashes to find one leaf-list instance */
+ LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
+ lyd_find_sibling_first(start, target, &node);
+ lyd_free_tree(target);
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* we will use hashes to find one list instance */
+ LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target));
+ lyd_find_sibling_first(start, target, &node);
+ lyd_free_tree(target);
+ break;
+ case LY_PATH_PREDTYPE_NONE:
+ /* we will use hashes to find one any/container/leaf instance */
+ lyd_find_sibling_val(start, path[u].node, NULL, 0, &node);
+ break;
+ }
+
+ if (!node) {
+ /* no matching nodes */
+ break;
+ }
+
+ /* rememeber previous node */
+ prev_node = node;
+
+ /* next path segment, if any */
+ start = lyd_child(node);
+ }
+
+ if (node) {
+ /* we have found the full path */
+ if (path_idx) {
+ *path_idx = u;
+ }
+ if (match) {
+ *match = node;
+ }
+ return LY_SUCCESS;
+
+ } else if (prev_node) {
+ /* we have found only some partial match */
+ if (path_idx) {
+ *path_idx = u - 1;
+ }
+ if (match) {
+ *match = prev_node;
+ }
+ return LY_EINCOMPLETE;
+ }
+
+ /* we have not found any nodes */
+ if (path_idx) {
+ *path_idx = 0;
+ }
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+}
+
+LY_ERR
+ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
+{
+ LY_ERR ret;
+ struct lyd_node *m;
+
+ ret = ly_path_eval_partial(path, start, NULL, &m);
+
+ if (ret == LY_SUCCESS) {
+ /* last node was found */
+ if (match) {
+ *match = m;
+ }
+ return LY_SUCCESS;
+ }
+
+ /* not a full match */
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+}
+
+LY_ERR
+ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ if (!path) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_COUNT(path), LY_EMEM);
+ LY_ARRAY_FOR(path, u) {
+ LY_ARRAY_INCREMENT(*dup);
+ (*dup)[u].node = path[u].node;
+ if (path[u].predicates) {
+ LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_COUNT(path[u].predicates), LY_EMEM);
+ (*dup)[u].pred_type = path[u].pred_type;
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ struct ly_path_predicate *pred = &path[u].predicates[v];
+
+ LY_ARRAY_INCREMENT((*dup)[u].predicates);
+ switch (path[u].pred_type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position-predicate */
+ (*dup)[u].predicates[v].position = pred->position;
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* key-predicate or leaf-list-predicate */
+ (*dup)[u].predicates[v].key = pred->key;
+ pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred->value.realtype)->refcount);
+ break;
+ case LY_PATH_PREDTYPE_NONE:
+ break;
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, struct ly_path_predicate *predicates)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysf_ctx fctx = {.ctx = (struct ly_ctx *)ctx};
+
+ if (!predicates) {
+ return;
+ }
+
+ LY_ARRAY_FOR(predicates, u) {
+ switch (pred_type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ case LY_PATH_PREDTYPE_NONE:
+ /* nothing to free */
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ if (predicates[u].value.realtype) {
+ predicates[u].value.realtype->plugin->free(ctx, &predicates[u].value);
+ lysc_type_free(&fctx, (struct lysc_type *)predicates[u].value.realtype);
+ }
+ break;
+ }
+ }
+ LY_ARRAY_FREE(predicates);
+}
+
+void
+ly_path_free(const struct ly_ctx *ctx, struct ly_path *path)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!path) {
+ return;
+ }
+
+ LY_ARRAY_FOR(path, u) {
+ ly_path_predicates_free(ctx, path[u].pred_type, path[u].predicates);
+ }
+ LY_ARRAY_FREE(path);
+}
diff --git a/src/path.h b/src/path.h
new file mode 100644
index 0000000..ca1c84a
--- /dev/null
+++ b/src/path.h
@@ -0,0 +1,263 @@
+/**
+ * @file path.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Path structure and manipulation routines.
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PATH_H_
+#define LY_PATH_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "tree.h"
+#include "tree_data.h"
+
+struct ly_ctx;
+struct lys_module;
+struct lysc_ext_instance;
+struct lysc_node;
+struct lyxp_expr;
+
+enum ly_path_pred_type {
+ LY_PATH_PREDTYPE_NONE = 0, /**< no predicate */
+ LY_PATH_PREDTYPE_POSITION, /**< position predicate - [2] */
+ LY_PATH_PREDTYPE_LIST, /**< keys predicate - [key1='val1'][key2='val2']... */
+ LY_PATH_PREDTYPE_LEAFLIST /**< leaflist value predicate - [.='value'] */
+};
+
+/**
+ * @brief Structure for simple path predicate.
+ */
+struct ly_path_predicate {
+ union {
+ uint64_t position; /**< position value for the position-predicate */
+
+ struct {
+ const struct lysc_node *key; /**< key node of the predicate, NULL in
+ case of a leaf-list predicate */
+ struct lyd_value value; /**< value representation according to the
+ key's type, its realtype is allocated */
+ };
+ };
+};
+
+/**
+ * @brief Structure for holding one segment of resolved path on schema including
+ * simple predicates. Is used as a [sized array](@ref sizedarrays).
+ */
+struct ly_path {
+ const struct lysc_node *node; /**< Schema node representing the path segment, first node has special meaning:
+ - is a top-level node - path is absolute,
+ - is inner node - path is relative */
+ const struct lysc_ext_instance *ext; /**< Extension instance of @p node, if any */
+ struct ly_path_predicate *predicates; /**< [Sized array](@ref sizedarrays) of the path segment's predicates */
+ enum ly_path_pred_type pred_type; /**< Predicate type (see YANG ABNF) */
+};
+
+/**
+ * @defgroup path_begin_options Path begin options.
+ * @{
+ */
+#define LY_PATH_BEGIN_ABSOLUTE 0x01 /**< path must be absolute */
+#define LY_PATH_BEGIN_EITHER 0x02 /**< path be be either absolute or relative */
+/** @} */
+
+/**
+ * @defgroup path_prefix_options Path prefix options.
+ * @{
+ */
+#define LY_PATH_PREFIX_OPTIONAL 0x10 /**< prefixes in the path are optional */
+#define LY_PATH_PREFIX_MANDATORY 0x20 /**< prefixes in the path are mandatory (XML instance-identifier) */
+#define LY_PATH_PREFIX_STRICT_INHERIT 0x30 /**< prefixes in the path are mandatory in case they differ from the
+ previous prefixes, otherwise they are prohibited (JSON instance-identifier) */
+/** @} */
+
+/**
+ * @defgroup path_pred_options Path predicate options.
+ * @{
+ */
+#define LY_PATH_PRED_KEYS 0x40 /* expected predicate only - [node='value']* */
+#define LY_PATH_PRED_SIMPLE 0x80 /* expected predicates - [node='value']*; [.='value']; [1] */
+#define LY_PATH_PRED_LEAFREF 0xC0 /* expected predicates only leafref - [node=current()/../../../node/node];
+ at least 1 ".." and 1 "node" after */
+/** @} */
+
+/**
+ * @brief Parse path into XPath token structure and perform all additional checks.
+ *
+ * @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] 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).
+ * @param[in] pred Predicate option (@ref path_pred_options).
+ * @param[out] expr Parsed path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
+ ly_bool lref, uint8_t begin, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr);
+
+/**
+ * @brief Parse predicate into XPath token structure and perform all additional checks.
+ *
+ * @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] prefix Prefix option (@ref path_prefix_options).
+ * @param[in] pred Predicate option (@ref path_pred_options).
+ * @param[out] expr Parsed path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
+ size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr);
+
+/**
+ * @defgroup path_oper_options Path operation options.
+ * @{
+ */
+#define LY_PATH_OPER_INPUT 0x01 /**< if any RPC/action is traversed, its input nodes are used */
+#define LY_PATH_OPER_OUTPUT 0x02 /**< if any RPC/action is traversed, its output nodes are used */
+/** @} */
+
+/* lref */
+
+/**
+ * @defgroup path_target_options Path target options.
+ * @{
+ */
+#define LY_PATH_TARGET_SINGLE 0x10 /**< last (target) node must identify an exact instance */
+#define LY_PATH_TARGET_MANY 0x20 /**< last (target) node may identify all instances (of leaf-list/list) */
+/** @} */
+
+/**
+ * @brief Compile path into ly_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] cur_mod Current module of the path (where it was "instantiated"). Used for nodes in schema-nodeid
+ * without a prefix for ::LY_VALUE_SCHEMA and ::LY_VALUE_SCHEMA_RESOLVED format.
+ * @param[in] ctx_node Optional context node.
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find the top-level
+ * node inside the extension instance instead of a module. Note that this is the case not only if the @p ctx_node is NULL,
+ * but also if the relative path starting in @p ctx_node reaches the document root via double dots.
+ * @param[in] expr Parsed path.
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
+ * @param[in] limit_access_tree Whether to limit accessible tree.
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[out] path Compiled path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
+
+/**
+ * @brief Compile path into ly_path structure. Any predicates of a leafref are only checked, not compiled.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Context node.
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find the top-level
+ * node inside the extension instance instead of a module. Note that this is the case not only if the @p ctx_node is NULL,
+ * but also if the relative path starting in @p ctx_node reaches the document root via double dots.
+ * @param[in] expr Parsed path.
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[out] path Compiled path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
+
+/**
+ * @brief Compile predicate into ly_path_predicate structure. Only simple predicates (not leafref) are supported.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] cur_node Optional current (original context) node.
+ * @param[in] cur_mod Current module of the path (where it was "instantiated"). Used for nodes without a prefix
+ * for ::LY_VALUE_SCHEMA and ::LY_VALUE_SCHEMA_RESOLVED format.
+ * @param[in] ctx_node Context node, node for which the predicate is defined.
+ * @param[in] expr Parsed path.
+ * @param[in,out] tok_idx Index in @p expr, is adjusted for parsed tokens.
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[out] predicates Compiled predicates.
+ * @param[out] pred_type Type of the compiled predicate(s).
+ * @return LY_ERR value.
+ */
+LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
+ const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format,
+ void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type);
+
+/**
+ * @brief Resolve at least partially the target defined by ly_path structure. Not supported for leafref!
+ *
+ * @param[in] path Path structure specifying the target.
+ * @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[out] path_idx Last found path segment index, can be NULL, set to 0 if not found.
+ * @param[out] match Last found matching node, can be NULL, set to NULL if not found.
+ * @return LY_ENOTFOUND if no nodes were found,
+ * @return LY_EINCOMPLETE if some node was found but not the last one,
+ * @return LY_SUCCESS when the last node in the path was found,
+ * @return LY_ERR on another error.
+ */
+LY_ERR ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx,
+ struct lyd_node **match);
+
+/**
+ * @brief Resolve the target defined by ly_path structure. Not supported for leafref!
+ *
+ * @param[in] path Path structure specifying the target.
+ * @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[out] match Found matching node, can be NULL, set to NULL if not found.
+ * @return LY_ENOTFOUND if no nodes were found,
+ * @return LY_SUCCESS when the last node in the path was found,
+ * @return LY_ERR on another error.
+ */
+LY_ERR ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match);
+
+/**
+ * @brief Duplicate ly_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] path Path to duplicate.
+ * @param[out] dup Duplicated path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup);
+
+/**
+ * @brief Free ly_path_predicate structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] pred_type Predicate type.
+ * @param[in] predicates Predicates ([sized array](@ref sizedarrays)) to free.
+ */
+void ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type,
+ struct ly_path_predicate *predicates);
+
+/**
+ * @brief Free ly_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] path The structure ([sized array](@ref sizedarrays)) to free.
+ */
+void ly_path_free(const struct ly_ctx *ctx, struct ly_path *path);
+
+#endif /* LY_PATH_H_ */
diff --git a/src/plugins.c b/src/plugins.c
new file mode 100644
index 0000000..d62da1c
--- /dev/null
+++ b/src/plugins.c
@@ -0,0 +1,550 @@
+/**
+ * @file plugins.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Manipulate with the type and extension plugins.
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins.h"
+#include "plugins_internal.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "config.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+#include "set.h"
+
+/*
+ * internal type plugins records
+ */
+extern const struct lyplg_type_record plugins_binary[];
+extern const struct lyplg_type_record plugins_bits[];
+extern const struct lyplg_type_record plugins_boolean[];
+extern const struct lyplg_type_record plugins_decimal64[];
+extern const struct lyplg_type_record plugins_empty[];
+extern const struct lyplg_type_record plugins_enumeration[];
+extern const struct lyplg_type_record plugins_identityref[];
+extern const struct lyplg_type_record plugins_instanceid[];
+extern const struct lyplg_type_record plugins_integer[];
+extern const struct lyplg_type_record plugins_leafref[];
+extern const struct lyplg_type_record plugins_string[];
+extern const struct lyplg_type_record plugins_union[];
+
+/*
+ * yang
+ */
+extern const struct lyplg_type_record plugins_instanceid_keys[];
+
+/*
+ * ietf-inet-types
+ */
+extern const struct lyplg_type_record plugins_ipv4_address[];
+extern const struct lyplg_type_record plugins_ipv4_address_no_zone[];
+extern const struct lyplg_type_record plugins_ipv6_address[];
+extern const struct lyplg_type_record plugins_ipv6_address_no_zone[];
+extern const struct lyplg_type_record plugins_ipv4_prefix[];
+extern const struct lyplg_type_record plugins_ipv6_prefix[];
+
+/*
+ * ietf-yang-types
+ */
+extern const struct lyplg_type_record plugins_date_and_time[];
+extern const struct lyplg_type_record plugins_xpath10[];
+
+/*
+ * ietf-netconf-acm
+ */
+extern const struct lyplg_type_record plugins_node_instanceid[];
+
+/*
+ * internal extension plugins records
+ */
+extern struct lyplg_ext_record plugins_metadata[];
+extern struct lyplg_ext_record plugins_nacm[];
+extern struct lyplg_ext_record plugins_yangdata[];
+extern struct lyplg_ext_record plugins_schema_mount[];
+extern struct lyplg_ext_record plugins_structure[];
+
+static pthread_mutex_t plugins_guard = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * @brief Counter for currently present contexts able to refer to the loaded plugins.
+ *
+ * Plugins are shared among all the created contexts. They are loaded with the creation of the very first context and
+ * unloaded with the destroy of the last context. Therefore, to reload the list of plugins, all the contexts must be
+ * destroyed and with the creation of a first new context after that, the plugins will be reloaded.
+ */
+static uint32_t context_refcount = 0;
+
+/**
+ * @brief Record describing an implemented extension.
+ *
+ * Matches ::lyplg_ext_record and ::lyplg_type_record
+ */
+struct lyplg_record {
+ const char *module; /**< name of the module where the extension/type is defined */
+ const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
+ which is not an optimal approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same pointer to the plugin functions. The
+ only valid use case for the NULL revision is the case the module has no revision. */
+ const char *name; /**< name of the extension/typedef */
+ int8_t plugin[]; /**< specific plugin type's data - ::lyplg_ext or ::lyplg_type */
+};
+
+#ifndef STATIC
+static struct ly_set plugins_handlers = {0};
+#endif
+static struct ly_set plugins_types = {0};
+static struct ly_set plugins_extensions = {0};
+
+/**
+ * @brief Iterate over list of loaded plugins of the given @p type.
+ *
+ * @param[in] type Type of the plugins to iterate.
+ * @param[in,out] index The iterator - set to 0 for the first call.
+ * @return The plugin records, NULL if no more record is available.
+ */
+static struct lyplg_record *
+plugins_iter(enum LYPLG type, uint32_t *index)
+{
+ struct ly_set *plugins;
+
+ assert(index);
+
+ if (type == LYPLG_EXTENSION) {
+ plugins = &plugins_extensions;
+ } else {
+ plugins = &plugins_types;
+ }
+
+ if (*index == plugins->count) {
+ return NULL;
+ }
+
+ *index += 1;
+ return plugins->objs[*index - 1];
+}
+
+static void *
+lyplg_record_find(enum LYPLG type, const char *module, const char *revision, const char *name)
+{
+ uint32_t i = 0;
+ struct lyplg_record *item;
+
+ assert(module);
+ assert(name);
+
+ while ((item = plugins_iter(type, &i)) != NULL) {
+ if (!strcmp(item->module, module) && !strcmp(item->name, name)) {
+ if (item->revision && revision && strcmp(item->revision, revision)) {
+ continue;
+ } else if (!revision && item->revision) {
+ continue;
+ }
+
+ return item;
+ }
+ }
+
+ return NULL;
+}
+
+struct lyplg_type *
+lyplg_type_plugin_find(const char *module, const char *revision, const char *name)
+{
+ struct lyplg_record *record;
+
+ record = lyplg_record_find(LYPLG_TYPE, module, revision, name);
+ return record ? &((struct lyplg_type_record *)record)->plugin : NULL;
+}
+
+struct lyplg_ext_record *
+lyplg_ext_record_find(const char *module, const char *revision, const char *name)
+{
+ return lyplg_record_find(LYPLG_EXTENSION, module, revision, name);
+}
+
+/**
+ * @brief Insert the provided extension plugin records into the internal set of extension plugins for use by libyang.
+ *
+ * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
+ * record.
+ * @return LY_SUCCESS in case of success
+ * @return LY_EINVAL for invalid information in @p recs.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+static LY_ERR
+plugins_insert(enum LYPLG type, const void *recs)
+{
+ if (!recs) {
+ return LY_SUCCESS;
+ }
+
+ if (type == LYPLG_EXTENSION) {
+ const struct lyplg_ext_record *rec = (const struct lyplg_ext_record *)recs;
+
+ for (uint32_t i = 0; rec[i].name; i++) {
+ LY_CHECK_RET(ly_set_add(&plugins_extensions, (void *)&rec[i], 0, NULL));
+ }
+ } else { /* LYPLG_TYPE */
+ const struct lyplg_type_record *rec = (const struct lyplg_type_record *)recs;
+
+ for (uint32_t i = 0; rec[i].name; i++) {
+ LY_CHECK_RET(ly_set_add(&plugins_types, (void *)&rec[i], 0, NULL));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+#ifndef STATIC
+
+static void
+lyplg_close_cb(void *handle)
+{
+ dlclose(handle);
+}
+
+static void
+lyplg_clean_(void)
+{
+ if (--context_refcount) {
+ /* there is still some other context, do not remove the plugins */
+ return;
+ }
+
+ ly_set_erase(&plugins_types, NULL);
+ ly_set_erase(&plugins_extensions, NULL);
+ ly_set_erase(&plugins_handlers, lyplg_close_cb);
+}
+
+#endif
+
+void
+lyplg_clean(void)
+{
+#ifndef STATIC
+ pthread_mutex_lock(&plugins_guard);
+ lyplg_clean_();
+ pthread_mutex_unlock(&plugins_guard);
+#endif
+}
+
+#ifndef STATIC
+
+/**
+ * @brief Just a variadic data to cover extension and type plugins by a single ::plugins_load() function.
+ *
+ * The values are taken from ::LY_PLUGINS_EXTENSIONS and ::LYPLG_TYPES macros.
+ */
+static const struct {
+ const char *id; /**< string identifier: type/extension */
+ const char *apiver_var; /**< expected variable name holding API version value */
+ const char *plugins_var; /**< expected variable name holding plugin records */
+ const char *envdir; /**< environment variable containing directory with the plugins */
+ const char *dir; /**< default directory with the plugins (has less priority than envdir) */
+ uint32_t apiver; /**< expected API version */
+} plugins_load_info[] = {
+ { /* LYPLG_TYPE */
+ .id = "type",
+ .apiver_var = "plugins_types_apiver__",
+ .plugins_var = "plugins_types__",
+ .envdir = "LIBYANG_TYPES_PLUGINS_DIR",
+ .dir = LYPLG_TYPE_DIR,
+ .apiver = LYPLG_TYPE_API_VERSION
+ }, {/* LYPLG_EXTENSION */
+ .id = "extension",
+ .apiver_var = "plugins_extensions_apiver__",
+ .plugins_var = "plugins_extensions__",
+ .envdir = "LIBYANG_EXTENSIONS_PLUGINS_DIR",
+ .dir = LYPLG_EXT_DIR,
+ .apiver = LYPLG_EXT_API_VERSION
+ }
+};
+
+/**
+ * @brief Get the expected plugin objects from the loaded dynamic object and add the defined plugins into the lists of
+ * available extensions/types plugins.
+ *
+ * @param[in] dlhandler Loaded dynamic library handler.
+ * @param[in] pathname Path of the loaded library for logging.
+ * @param[in] type Type of the plugins to get from the dynamic library. Note that a single library can hold both types
+ * and extensions plugins implementations, so this function should be called twice (once for each plugin type) with
+ * different @p type values
+ * @return LY_ERR values.
+ */
+static LY_ERR
+plugins_load(void *dlhandler, const char *pathname, enum LYPLG type)
+{
+ const void *plugins;
+ uint32_t *version;
+
+ /* type plugin */
+ version = dlsym(dlhandler, plugins_load_info[type].apiver_var);
+ if (version) {
+ /* check version ... */
+ if (*version != plugins_load_info[type].apiver) {
+ LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, wrong API version - %d expected, %d found.",
+ plugins_load_info[type].id, pathname, plugins_load_info[type].apiver, *version);
+ return LY_EINVAL;
+ }
+
+ /* ... get types plugins information ... */
+ if (!(plugins = dlsym(dlhandler, plugins_load_info[type].plugins_var))) {
+ char *errstr = dlerror();
+
+ LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, missing %s plugins information (%s).",
+ plugins_load_info[type].id, pathname, plugins_load_info[type].id, errstr);
+ return LY_EINVAL;
+ }
+
+ /* ... and load all the types plugins */
+ LY_CHECK_RET(plugins_insert(type, plugins));
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+plugins_load_module(const char *pathname)
+{
+ LY_ERR ret = LY_SUCCESS;
+ void *dlhandler;
+ uint32_t types_count = 0, extensions_count = 0;
+
+ dlerror(); /* Clear any existing error */
+
+ dlhandler = dlopen(pathname, RTLD_NOW);
+ if (!dlhandler) {
+ LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", pathname, dlerror());
+ return LY_ESYS;
+ }
+
+ if (ly_set_contains(&plugins_handlers, dlhandler, NULL)) {
+ /* the plugin is already loaded */
+ LOGVRB("Plugin \"%s\" already loaded.", pathname);
+
+ /* keep the correct refcount */
+ dlclose(dlhandler);
+ return LY_SUCCESS;
+ }
+
+ /* remember the current plugins lists for recovery */
+ types_count = plugins_types.count;
+ extensions_count = plugins_extensions.count;
+
+ /* type plugin */
+ ret = plugins_load(dlhandler, pathname, LYPLG_TYPE);
+ LY_CHECK_GOTO(ret, error);
+
+ /* extension plugin */
+ ret = plugins_load(dlhandler, pathname, LYPLG_EXTENSION);
+ LY_CHECK_GOTO(ret, error);
+
+ /* remember the dynamic plugin */
+ ret = ly_set_add(&plugins_handlers, dlhandler, 1, NULL);
+ LY_CHECK_GOTO(ret, error);
+
+ return LY_SUCCESS;
+
+error:
+ dlclose(dlhandler);
+
+ /* revert changes in the lists */
+ while (plugins_types.count > types_count) {
+ ly_set_rm_index(&plugins_types, plugins_types.count - 1, NULL);
+ }
+ while (plugins_extensions.count > extensions_count) {
+ ly_set_rm_index(&plugins_extensions, plugins_extensions.count - 1, NULL);
+ }
+
+ return ret;
+}
+
+static LY_ERR
+plugins_insert_dir(enum LYPLG type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *pluginsdir;
+ DIR *dir;
+ ly_bool default_dir = 0;
+
+ /* try to get the plugins directory from environment variable */
+ pluginsdir = getenv(plugins_load_info[type].envdir);
+ if (!pluginsdir) {
+ /* remember that we are going to a default dir and do not print warning if the directory doesn't exist */
+ default_dir = 1;
+ pluginsdir = plugins_load_info[type].dir;
+ }
+
+ dir = opendir(pluginsdir);
+ if (!dir) {
+ /* no directory (or no access to it), no extension plugins */
+ if (!default_dir || (errno != ENOENT)) {
+ LOGWRN(NULL, "Failed to open libyang %s plugins directory \"%s\" (%s).", plugins_load_info[type].id,
+ pluginsdir, strerror(errno));
+ }
+ } else {
+ struct dirent *file;
+
+ while ((file = readdir(dir))) {
+ size_t len;
+ char pathname[PATH_MAX];
+
+ /* required format of the filename is *LYPLG_SUFFIX */
+ len = strlen(file->d_name);
+ if ((len < LYPLG_SUFFIX_LEN + 1) || strcmp(&file->d_name[len - LYPLG_SUFFIX_LEN], LYPLG_SUFFIX)) {
+ continue;
+ }
+
+ /* and construct the filepath */
+ snprintf(pathname, PATH_MAX, "%s/%s", pluginsdir, file->d_name);
+
+ ret = plugins_load_module(pathname);
+ if (ret) {
+ break;
+ }
+ }
+ closedir(dir);
+ }
+
+ return ret;
+}
+
+#endif
+
+LY_ERR
+lyplg_init(void)
+{
+ LY_ERR ret;
+
+ pthread_mutex_lock(&plugins_guard);
+ /* let only the first context to initiate plugins, but let others wait for finishing the initiation */
+ if (context_refcount++) {
+ /* already initiated */
+ pthread_mutex_unlock(&plugins_guard);
+ return LY_SUCCESS;
+ }
+
+ /* internal types */
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_binary), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_bits), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_boolean), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_decimal64), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_empty), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_enumeration), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_identityref), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_instanceid), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_integer), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_leafref), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_string), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_union), error);
+
+ /* yang */
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_instanceid_keys), error);
+
+ /* ietf-inet-types */
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_prefix), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_prefix), error);
+
+ /* ietf-yang-types */
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
+
+ /* ietf-netconf-acm */
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_node_instanceid), error);
+
+ /* internal extensions */
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_metadata), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_nacm), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_yangdata), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_schema_mount), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_structure), error);
+
+#ifndef STATIC
+ /* external types */
+ LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_TYPE), error);
+
+ /* external extensions */
+ LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_EXTENSION), error);
+#endif
+
+ /* initiation done, wake-up possibly waiting threads creating another contexts */
+ pthread_mutex_unlock(&plugins_guard);
+
+ return LY_SUCCESS;
+
+error:
+ /* initiation was not successful - cleanup (and let others to try) */
+#ifndef STATIC
+ lyplg_clean_();
+#endif
+ pthread_mutex_unlock(&plugins_guard);
+
+ if (ret == LY_EINVAL) {
+ /* all the plugins here are internal, invalid record actually means an internal libyang error */
+ ret = LY_EINT;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_add(const char *pathname)
+{
+#ifdef STATIC
+ (void)pathname;
+
+ LOGERR(NULL, LY_EINVAL, "Plugins are not supported in statically built library.");
+ return LY_EINVAL;
+#elif defined (_WIN32)
+ (void)pathname;
+
+ LOGERR(NULL, LY_EINVAL, "Plugins are not (yet) supported on Windows.");
+ return LY_EINVAL;
+#else
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(NULL, pathname, LY_EINVAL);
+
+ /* works only in case a context exists */
+ pthread_mutex_lock(&plugins_guard);
+ if (!context_refcount) {
+ /* no context */
+ pthread_mutex_unlock(&plugins_guard);
+ LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
+ return LY_EDENIED;
+ }
+
+ ret = plugins_load_module(pathname);
+
+ pthread_mutex_unlock(&plugins_guard);
+
+ return ret;
+#endif
+}
diff --git a/src/plugins.h b/src/plugins.h
new file mode 100644
index 0000000..f868879
--- /dev/null
+++ b/src/plugins.h
@@ -0,0 +1,94 @@
+/**
+ * @file plugins.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Plugins manipulation.
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PLUGINS_H_
+#define LY_PLUGINS_H_
+
+#include "log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page howtoPlugins Plugins
+ *
+ * libyang supports two types of plugins to better support generic features of YANG that need some specific code for
+ * their specific instances in YANG modules. This is the case of YANG types, which are derived from YANG built-in types.
+ * The description of a derived type can specify some additional requirements or restriction that cannot be implemented
+ * generically and some special code is needed. The second case for libyang plugins are YANG extensions. For YANG extensions,
+ * most of the specification is hidden in their description (e.g. allowed substatements or place of the extension
+ * instantiation) and libyang is not able to process such a text in a generic way.
+ *
+ * In both cases, libyang provides API to get functionality implementing the specifics of each type or extension.
+ * Furthermore, there are several internal plugins, implementing built-in data types and selected derived types and YANG
+ * extensions. These internal plugins uses the same API and can be taken as examples for implementing user plugins. Internal
+ * plugins are always loaded with the first created [context](@ref howtoContext) and unloaded with destroying the last one.
+ * The external plugins are in the same phase loaded from the default directories specified at compile time via cmake
+ * variables `PLUGINS_DIR` (where the `extensions` and `types` subdirectories are added for each plugin type) or separately
+ * via `PLUGINS_DIR_EXTENSIONS` and `PLUGINS_DIR_TYPES` for each plugin type. The default directories can be replaced runtime
+ * using environment variables `LIBYANG_TYPES_PLUGINS_DIR` and `LIBYANG_EXTENSIONS_PLUGINS_DIR`.
+ *
+ * Order of the plugins determines their priority. libyang searches for the first match with the extension and type, so the
+ * firstly loaded plugin for the specific item is used. Since the internal plugins are loaded always before the external
+ * plugins, the internal plugins cannot be replaced.
+ *
+ * There is also a separate function ::lyplg_add() to add a plugin anytime later. Note, that such a plugin is being used
+ * after it is added with the lowest priority among other already loaded plugins. Also note that since all the plugins are
+ * unloaded with the destruction of the last context, creating a new context after that starts the standard plugins
+ * initiation and the manually added plugins are not loaded automatically.
+ *
+ * The following pages contain description of the API for creating user plugins.
+ *
+ * - @subpage howtoPluginsTypes
+ * - @subpage howtoPluginsExtensions
+ */
+
+/**
+ * @defgroup plugins Plugins
+ * @{
+ *
+ */
+
+/**
+ * @brief Identifiers of the plugin type.
+ */
+enum LYPLG {
+ LYPLG_TYPE, /**< Specific type (typedef) */
+ LYPLG_EXTENSION /**< YANG extension */
+};
+
+/**
+ * @brief Manually load a plugin file.
+ *
+ * Note, that a plugin can be loaded only if there is at least one context. The loaded plugins are connected with the
+ * existence of a context. When all the contexts are destroyed, all the plugins are unloaded.
+ *
+ * @param[in] pathname Path to the plugin file. It can contain types or extensions plugins, both are accepted and correctly
+ * loaded.
+ *
+ * @return LY_SUCCESS if the file contains valid plugin compatible with the library version.
+ * @return LY_EDENIED in case there is no context and the plugin cannot be loaded.
+ * @return LY_EINVAL when pathname is NULL or the plugin contains invalid content for this libyang version.
+ * @return LY_ESYS when the plugin file cannot be loaded.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_add(const char *pathname);
+
+/** @} plugins */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PLUGINS_H_ */
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
new file mode 100644
index 0000000..00970fa
--- /dev/null
+++ b/src/plugins_exts.c
@@ -0,0 +1,680 @@
+/**
+ * @file plugins_exts.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief helper functions for extension plugins
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "plugins_exts.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "dict.h"
+#include "parser_internal.h"
+#include "printer_internal.h"
+#include "schema_compile.h"
+#include "schema_compile_amend.h"
+#include "schema_compile_node.h"
+#include "schema_features.h"
+#include "tree_schema_internal.h"
+
+LIBYANG_API_DEF const struct lysp_module *
+lyplg_ext_parse_get_cur_pmod(const struct lysp_ctx *pctx)
+{
+ return PARSER_CUR_PMOD(pctx);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_parse_extension_instance(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ LY_ERR rc = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_stmt *stmt;
+
+ /* check for invalid substatements */
+ LY_LIST_FOR(ext->child, stmt) {
+ if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) {
+ continue;
+ }
+ LY_ARRAY_FOR(ext->substmts, u) {
+ if (ext->substmts[u].stmt == stmt->kw) {
+ break;
+ }
+ }
+ if (u == LY_ARRAY_COUNT(ext->substmts)) {
+ LOGVAL(PARSER_CTX(pctx), LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.",
+ stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* parse all the known statements */
+ LY_ARRAY_FOR(ext->substmts, u) {
+ LY_LIST_FOR(ext->child, stmt) {
+ if (ext->substmts[u].stmt != stmt->kw) {
+ continue;
+ }
+
+ if ((rc = lys_parse_ext_instance_stmt(pctx, &ext->substmts[u], stmt))) {
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ return rc;
+}
+
+/**
+ * @brief Compile an instance extension statement.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] parsed Parsed ext instance substatement structure.
+ * @param[in] ext Compiled ext instance.
+ * @param[in] substmt Compled ext instance substatement info.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct lysc_ext_instance *ext,
+ struct lysc_ext_substmt *substmt)
+{
+ LY_ERR rc = LY_SUCCESS;
+ ly_bool length_restr = 0;
+ LY_DATA_TYPE basetype;
+
+ /* compilation wthout any storage */
+ if (substmt->stmt == LY_STMT_IF_FEATURE) {
+ ly_bool enabled;
+
+ /* evaluate */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, parsed, &enabled), cleanup);
+ if (!enabled) {
+ /* it is disabled, remove the whole extension instance */
+ rc = LY_ENOT;
+ }
+ }
+
+ if (!substmt->storage || !parsed) {
+ /* nothing to store or nothing parsed to compile */
+ goto cleanup;
+ }
+
+ switch (substmt->stmt) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_USES: {
+ const uint16_t flags;
+ struct lysp_node *pnodes, *pnode;
+ struct lysc_node *node;
+
+ lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
+ pnodes = (struct lysp_node *)parsed;
+
+ /* compile nodes */
+ LY_LIST_FOR(pnodes, pnode) {
+ if (pnode->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ /* manual compile */
+ node = calloc(1, sizeof(struct lysc_node_action_inout));
+ LY_CHECK_ERR_GOTO(!node, LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(rc = lys_compile_node_action_inout(ctx, pnode, node), cleanup);
+ LY_CHECK_GOTO(rc = lys_compile_node_connect(ctx, NULL, node), cleanup);
+ } else {
+ /* ctx->ext substatement storage is used as the document root */
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, NULL, flags, NULL), cleanup);
+ }
+ }
+ break;
+ }
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_KEY:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_UNITS:
+ /* just make a copy */
+ LY_CHECK_GOTO(rc = lydict_insert(ctx->ctx, parsed, 0, substmt->storage), cleanup);
+ break;
+
+ case LY_STMT_BIT:
+ basetype = LY_TYPE_BITS;
+ /* fallthrough */
+ case LY_STMT_ENUM:
+ if (substmt->stmt == LY_STMT_ENUM) {
+ basetype = LY_TYPE_ENUM;
+ }
+
+ /* compile */
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, parsed, basetype, NULL, substmt->storage), cleanup);
+ break;
+
+ case LY_STMT_CONFIG: {
+ uint16_t flags;
+
+ if (!(ctx->compile_opts & LYS_COMPILE_NO_CONFIG)) {
+ memcpy(&flags, &parsed, 2);
+ if (flags & LYS_CONFIG_MASK) {
+ /* explicitly set */
+ flags |= LYS_SET_CONFIG;
+ } else if (ext->parent_stmt & LY_STMT_DATA_NODE_MASK) {
+ /* inherit */
+ flags = ((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_MASK;
+ } else {
+ /* default config */
+ flags = LYS_CONFIG_W;
+ }
+ memcpy(substmt->storage, &flags, 2);
+ } /* else leave zero */
+ break;
+ }
+ case LY_STMT_MUST: {
+ const struct lysp_restr *restrs = parsed;
+
+ /* sized array */
+ COMPILE_ARRAY_GOTO(ctx, restrs, *(struct lysc_must **)substmt->storage, lys_compile_must, rc, cleanup);
+ break;
+ }
+ case LY_STMT_WHEN: {
+ const uint16_t flags;
+ const struct lysp_when *when = parsed;
+
+ /* read compiled status */
+ lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
+
+ /* compile */
+ LY_CHECK_GOTO(rc = lys_compile_when(ctx, when, flags, NULL, NULL, NULL, substmt->storage), cleanup);
+ break;
+ }
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_REQUIRE_INSTANCE:
+ /* just make a copy */
+ memcpy(substmt->storage, &parsed, 1);
+ break;
+
+ case LY_STMT_MANDATORY:
+ case LY_STMT_ORDERED_BY:
+ case LY_STMT_STATUS:
+ /* just make a copy */
+ memcpy(substmt->storage, &parsed, 2);
+ break;
+
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ /* just make a copy */
+ memcpy(substmt->storage, &parsed, 4);
+ break;
+
+ case LY_STMT_POSITION:
+ case LY_STMT_VALUE:
+ /* just make a copy */
+ memcpy(substmt->storage, &parsed, 8);
+ break;
+
+ case LY_STMT_IDENTITY:
+ /* compile */
+ LY_CHECK_GOTO(rc = lys_identity_precompile(ctx, NULL, NULL, parsed, substmt->storage), cleanup);
+ break;
+
+ case LY_STMT_LENGTH:
+ length_restr = 1;
+ /* fallthrough */
+ case LY_STMT_RANGE:
+ /* compile, use uint64 default range */
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, parsed, LY_TYPE_UINT64, length_restr, 0, NULL, substmt->storage),
+ cleanup);
+ break;
+
+ case LY_STMT_PATTERN:
+ /* compile */
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(ctx, parsed, NULL, substmt->storage), cleanup);
+ break;
+
+ case LY_STMT_TYPE: {
+ const uint16_t flags;
+ const char *units;
+ const struct lysp_type *ptype = parsed;
+
+ /* read compiled info */
+ lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
+ lyplg_ext_get_storage(ext, LY_STMT_UNITS, sizeof units, (const void **)&units);
+
+ /* compile */
+ LY_CHECK_GOTO(rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, substmt->storage, &units, NULL), cleanup);
+ break;
+ }
+ case LY_STMT_EXTENSION_INSTANCE: {
+ struct lysp_ext_instance *extps = (struct lysp_ext_instance *)parsed;
+
+ /* compile sized array */
+ COMPILE_EXTS_GOTO(ctx, extps, *(struct lysc_ext_instance **)substmt->storage, ext, rc, cleanup);
+ break;
+ }
+ case LY_STMT_AUGMENT:
+ case LY_STMT_GROUPING:
+ case LY_STMT_BASE:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ case LY_STMT_MODULE:
+ case LY_STMT_PATH:
+ case LY_STMT_PREFIX:
+ case LY_STMT_REFINE:
+ case LY_STMT_REVISION:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_UNIQUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" compilation is not supported.", lyplg_ext_stmt2str(substmt->stmt));
+ rc = LY_EVALID;
+ goto cleanup;
+
+ default:
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension "
+ "(found in \"%s%s%s\") substatement.", lyplg_ext_stmt2str(substmt->stmt), ext->def->name,
+ ext->argument ? " " : "", ext->argument ? ext->argument : "");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+cleanup:
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *extp,
+ struct lysc_ext_instance *ext)
+{
+ LY_ERR rc = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ enum ly_stmt stmtp;
+ const void *storagep;
+ struct ly_set storagep_compiled = {0};
+
+ LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, ctx, extp, ext, LY_EINVAL);
+
+ /* note into the compile context that we are processing extension now */
+ ctx->ext = ext;
+
+ LY_ARRAY_FOR(extp->substmts, u) {
+ stmtp = extp->substmts[u].stmt;
+ storagep = *(void **)extp->substmts[u].storage;
+
+ if (ly_set_contains(&storagep_compiled, storagep, NULL)) {
+ /* this parsed statement has already been compiled (for example, if it is a linked list of parsed nodes) */
+ continue;
+ }
+
+ LY_ARRAY_FOR(ext->substmts, v) {
+ if (stmtp != ext->substmts[v].stmt) {
+ continue;
+ }
+
+ if ((rc = lys_compile_ext_instance_stmt(ctx, storagep, ext, &ext->substmts[v]))) {
+ goto cleanup;
+ }
+
+ /* parsed substatement compiled */
+ break;
+ }
+
+ /* compiled */
+ ly_set_add(&storagep_compiled, storagep, 1, NULL);
+ }
+
+cleanup:
+ ctx->ext = NULL;
+ ly_set_erase(&storagep_compiled, NULL);
+ return rc;
+}
+
+LIBYANG_API_DEF struct ly_ctx *
+lyplg_ext_compile_get_ctx(const struct lysc_ctx *ctx)
+{
+ return ctx->ctx;
+}
+
+LIBYANG_API_DEF uint32_t *
+lyplg_ext_compile_get_options(const struct lysc_ctx *ctx)
+{
+ return &((struct lysc_ctx *)ctx)->compile_opts;
+}
+
+LIBYANG_API_DEF const struct lys_module *
+lyplg_ext_compile_get_cur_mod(const struct lysc_ctx *ctx)
+{
+ return ctx->cur_mod;
+}
+
+LIBYANG_API_DEF struct lysp_module *
+lyplg_ext_compile_get_pmod(const struct lysc_ctx *ctx)
+{
+ return ctx->pmod;
+}
+
+LIBYANG_API_DEF struct ly_out **
+lyplg_ext_print_get_out(const struct lyspr_ctx *ctx)
+{
+ return &((struct lyspr_ctx *)ctx)->out;
+}
+
+LIBYANG_API_DEF uint32_t *
+lyplg_ext_print_get_options(const struct lyspr_ctx *ctx)
+{
+ return &((struct lyspr_ctx *)ctx)->options;
+}
+
+LIBYANG_API_DEF uint16_t *
+lyplg_ext_print_get_level(const struct lyspr_ctx *ctx)
+{
+ return &((struct lyspr_ctx *)ctx)->level;
+}
+
+LIBYANG_API_DECL LY_ERR
+lyplg_ext_sprinter_ctree_add_ext_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_ext_instance *ext,
+ lyplg_ext_sprinter_ctree_override_clb clb)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint32_t i;
+ struct lysc_node *schema;
+
+ LY_CHECK_ARG_RET2(NULL, ctx, ext, LY_EINVAL);
+
+ LY_ARRAY_FOR(ext->substmts, i) {
+ switch (ext->substmts[i].stmt) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ schema = *((struct lysc_node **)ext->substmts[i].storage);
+ if (schema) {
+ rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, schema, clb);
+ return rc;
+ }
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+LIBYANG_API_DECL LY_ERR
+lyplg_ext_sprinter_ptree_add_ext_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_ext_instance *ext,
+ lyplg_ext_sprinter_ptree_override_clb clb)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint32_t i;
+ struct lysp_node *schema;
+
+ LY_CHECK_ARG_RET2(NULL, ctx, ext, LY_EINVAL);
+
+ LY_ARRAY_FOR(ext->substmts, i) {
+ switch (ext->substmts[i].stmt) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ schema = *((struct lysp_node **)ext->substmts[i].storage);
+ if (schema) {
+ rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, schema, clb);
+ return rc;
+ }
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+LIBYANG_API_DECL LY_ERR
+lyplg_ext_sprinter_ctree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_node *nodes,
+ lyplg_ext_sprinter_ctree_override_clb clb)
+{
+ struct lyspr_tree_schema *new;
+
+ LY_CHECK_ARG_RET1(NULL, ctx, LY_EINVAL);
+
+ if (!nodes) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_NEW_RET(NULL, ((struct lyspr_tree_ctx *)ctx)->schemas, new, LY_EMEM);
+ new->compiled = 1;
+ new->ctree = nodes;
+ new->cn_overr = clb;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DECL LY_ERR
+lyplg_ext_sprinter_ptree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_node *nodes,
+ lyplg_ext_sprinter_ptree_override_clb clb)
+{
+ struct lyspr_tree_schema *new;
+
+ LY_CHECK_ARG_RET1(NULL, ctx, LY_EINVAL);
+
+ if (!nodes) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_NEW_RET(NULL, ((struct lyspr_tree_ctx *)ctx)->schemas, new, LY_EMEM);
+ new->compiled = 0;
+ new->ptree = nodes;
+ new->pn_overr = clb;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DECL LY_ERR
+lyplg_ext_sprinter_tree_set_priv(const struct lyspr_tree_ctx *ctx, void *plugin_priv, void (*free_clb)(void *plugin_priv))
+{
+ LY_CHECK_ARG_RET1(NULL, ctx, LY_EINVAL);
+
+ ((struct lyspr_tree_ctx *)ctx)->plugin_priv = plugin_priv;
+ ((struct lyspr_tree_ctx *)ctx)->free_plugin_priv = free_clb;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const char *
+lyplg_ext_stmt2str(enum ly_stmt stmt)
+{
+ if (stmt == LY_STMT_EXTENSION_INSTANCE) {
+ return "extension instance";
+ } else {
+ return lys_stmt_str(stmt);
+ }
+}
+
+LIBYANG_API_DEF enum ly_stmt
+lyplg_ext_nodetype2stmt(uint16_t nodetype)
+{
+ switch (nodetype) {
+ case LYS_CONTAINER:
+ return LY_STMT_CONTAINER;
+ case LYS_CHOICE:
+ return LY_STMT_CHOICE;
+ case LYS_LEAF:
+ return LY_STMT_LEAF;
+ case LYS_LEAFLIST:
+ return LY_STMT_LEAF_LIST;
+ case LYS_LIST:
+ return LY_STMT_LIST;
+ case LYS_ANYXML:
+ return LY_STMT_ANYXML;
+ case LYS_ANYDATA:
+ return LY_STMT_ANYDATA;
+ case LYS_CASE:
+ return LY_STMT_CASE;
+ case LYS_RPC:
+ return LY_STMT_RPC;
+ case LYS_ACTION:
+ return LY_STMT_ACTION;
+ case LYS_NOTIF:
+ return LY_STMT_NOTIFICATION;
+ case LYS_USES:
+ return LY_STMT_USES;
+ case LYS_INPUT:
+ return LY_STMT_INPUT;
+ case LYS_OUTPUT:
+ return LY_STMT_OUTPUT;
+ default:
+ return LY_STMT_NONE;
+ }
+}
+
+LY_ERR
+lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, const void ***storage_p)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ enum ly_stmt match = 0;
+
+ *storage_p = NULL;
+
+ if (!(stmt & LY_STMT_NODE_MASK)) {
+ /* matching a non-node statement */
+ match = stmt;
+ }
+
+ LY_ARRAY_FOR(ext->substmts, u) {
+ if ((match && (ext->substmts[u].stmt == match)) || (!match && (ext->substmts[u].stmt & stmt))) {
+ *storage_p = ext->substmts[u].storage;
+ return LY_SUCCESS;
+ }
+ }
+
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const void **s;
+
+ /* get pointer to the storage, is set even on error */
+ rc = lyplg_ext_get_storage_p(ext, stmt, &s);
+
+ /* assign */
+ if (s) {
+ memcpy(storage, s, storage_size);
+ } else {
+ memset(storage, 0, storage_size);
+ }
+
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lysp_ext_instance *extp = NULL;
+ enum ly_stmt match = 0;
+ const void **s = NULL;
+
+ /* find the parsed ext instance */
+ LY_ARRAY_FOR(ext->module->parsed->exts, u) {
+ extp = &ext->module->parsed->exts[u];
+
+ if (ext->def == extp->def->compiled) {
+ break;
+ }
+ extp = NULL;
+ }
+ assert(extp);
+
+ if (!(stmt & LY_STMT_NODE_MASK)) {
+ /* matching a non-node statement */
+ match = stmt;
+ }
+
+ /* get the substatement */
+ LY_ARRAY_FOR(extp->substmts, u) {
+ if ((match && (extp->substmts[u].stmt == match)) || (!match && (extp->substmts[u].stmt & stmt))) {
+ s = extp->substmts[u].storage;
+ break;
+ }
+ }
+
+ /* assign */
+ if (s) {
+ memcpy(storage, s, storage_size);
+ } else {
+ memset(storage, 0, storage_size);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_get_data(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, void **ext_data, ly_bool *ext_data_free)
+{
+ LY_ERR rc;
+
+ if (!ctx->ext_clb) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Failed to get extension data, no callback set.");
+ return LY_EINVAL;
+ }
+
+ if ((rc = ctx->ext_clb(ext, ctx->ext_clb_data, ext_data, ext_data_free))) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Callback for getting ext data failed.");
+ }
+ return rc;
+}
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
new file mode 100644
index 0000000..f005391
--- /dev/null
+++ b/src/plugins_exts.h
@@ -0,0 +1,1048 @@
+/**
+ * @file plugins_exts.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang support for YANG extensions implementation.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PLUGINS_EXTS_H_
+#define LY_PLUGINS_EXTS_H_
+
+#include "log.h"
+#include "parser_data.h"
+#include "plugins.h"
+#include "tree_data.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+
+struct ly_ctx;
+struct ly_in;
+struct lyd_node;
+struct lysc_ctx;
+struct lysc_ext_substmt;
+struct lysp_ctx;
+struct lyspr_ctx;
+struct lyspr_tree_ctx;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page howtoPluginsExtensions Extension Plugins
+ *
+ * Note that the part of the libyang API here is available only by including a separated `<libyang/plugins_exts.h>` header
+ * file. Also note that the extension plugins API is versioned separately from libyang itself, so backward incompatible
+ * changes can come even without changing libyang major version.
+ *
+ * YANG extensions are very complex. Usually only its description specifies how it is supposed to behave, what are the
+ * allowed substatements, their cardinality or if the standard YANG statements placed inside the extension differs somehow
+ * in their meaning or behavior. libyang provides the Extension plugins API to implement such extensions and add its support
+ * into libyang itself. However we tried our best, the API is not (and it cannot be) so universal and complete to cover all
+ * possibilities. There are definitely use cases which cannot be simply implemented only with this API.
+ *
+ * libyang implements 3 important extensions: [NACM](https://tools.ietf.org/html/rfc8341), [Metadata](@ref howtoDataMetadata)
+ * and [yang-data](@ref howtoDataYangdata). Despite the core implementation in all three cases is done via extension plugin
+ * API, also other parts of the libyang code had to be extended to cover complete scope of the extensions.
+ *
+ * We believe, that the API is capable to allow implementation of very wide range of YANG extensions. However, if you see
+ * limitations for the particular YANG extension, don't hesitate to contact the project developers to discuss all the
+ * options, including updating the API.
+ *
+ * The plugin's functionality is provided to libyang via a set of callbacks specified as an array of ::lyplg_ext_record
+ * structures using the ::LYPLG_EXTENSIONS macro.
+ *
+ * The most important callbacks are ::lyplg_ext.parse and ::lyplg_ext.compile. They are responsible for parsing and
+ * compiling extension instance. This should include validating all the substatements, their values, or placement of
+ * the extension instance itself. If needed, the processed data can be stored in some form into the compiled schema
+ * representation of the extension instance. To make this task as easy as possible, libyang provides several
+ * [parsing](@ref pluginsExtensionsParse) and [compilation](@ref pluginsExtensionsCompile) helper functions to process
+ * known YANG statements exactly as if they were standard YANG statements.
+ *
+ * The data validation callback ::lyplg_ext.validate is used for additional validation of a data nodes that contains the
+ * connected extension instance directly (as a substatement) or indirectly in case of terminal nodes via their type (no
+ * matter if the extension instance is placed directly in the leaf's/leaf-list's type or in the type of the referenced
+ * typedef).
+ *
+ * The ::lyplg_ext.printer_info callback implement printing the compiled extension instance data when the schema (module) is
+ * being printed in the ::LYS_OUT_YANG_COMPILED (info) format. As for compile callback, there are also
+ * [helper functions](@ref pluginsExtensionsSprinterInfo) to access printer's context and to print standard YANG statements
+ * placed in the extension instance by libyang itself.
+ *
+ * The ::lyplg_ext.printer_ctree and ::lyplg_ext.printer_ptree callbacks implement printing of YANG tree diagrams
+ * (RFC 8340) for extension instance data. These callbacks are called for extension instances that have
+ * parents of type ::LY_STMT_MODULE, ::LY_STMT_SUBMODULE. Or these callbacks are called if the printer_tree finds
+ * a compiled/parsed data-node containing an extension instance. The callbacks should then decide which nodes
+ * should be printed within the extension instance. In addition, it is possible to register additional callbacks
+ * to the printer_tree context to override the form of the each node in the extension instance.
+ *
+ * The last callback, ::lyplg_ext.cfree, is supposed to free all the data allocated by the ::lyplg_ext.compile callback.
+ * To free the data created by helper function ::lyplg_ext_compile_extension_instance(), the plugin can used
+ * ::lyplg_ext_cfree_instance_substatements().
+ *
+ * The plugin information contains also the plugin identifier (::lyplg_type.id). This string can serve to identify the
+ * specific plugin responsible to storing data value. In case the user can recognize the id string, it can access the
+ * plugin specific data with the appropriate knowledge of its structure.
+ *
+ * Logging information from an extension plugin is possible via ::lyplg_ext_parse_log() and ::lyplg_ext_compile_log() functions.
+ */
+
+/**
+ * @defgroup pluginsExtensions Plugins: Extensions
+ *
+ * Structures and functions to for libyang plugins implementing specific YANG extensions defined in YANG modules. For more
+ * information, see @ref howtoPluginsTypes.
+ *
+ * This part of libyang API is available by including `<libyang/plugins_ext.h>` header file.
+ *
+ * @{
+ */
+
+/**
+ * @brief Extensions API version
+ */
+#define LYPLG_EXT_API_VERSION 6
+
+/**
+ * @brief Mask for an operation statement.
+ *
+ * This mask matches action and RPC.
+ */
+#define LY_STMT_OP_MASK (LY_STMT_ACTION | LY_STMT_RPC)
+
+/**
+ * @brief Mask for a data node statement.
+ *
+ * This mask matches anydata, anyxml, case, choice, container, leaf, leaf-list, and list.
+ */
+#define LY_STMT_DATA_NODE_MASK (LY_STMT_ANYDATA | LY_STMT_ANYXML | LY_STMT_CASE | LY_STMT_CHOICE | LY_STMT_CONTAINER |\
+ LY_STMT_LEAF | LY_STMT_LEAF_LIST | LY_STMT_LIST)
+
+/**
+ * @brief Mask for a node statement.
+ *
+ * This mask matches notification, input, output, action, RPC, anydata, anyxml, augment, case, choice, container,
+ * grouping, leaf, leaf-list, list, and uses.
+ */
+#define LY_STMT_NODE_MASK 0xFFFF
+
+/**
+ * @brief List of YANG statements
+ *
+ * Their description mentions what types are stored for each statement. Note that extension instance storage
+ * always stores a pointer to the type, not the type itself.
+ */
+enum ly_stmt {
+ LY_STMT_NONE = 0,
+
+ LY_STMT_NOTIFICATION = 0x0001, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_notif *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_notif *` */
+ LY_STMT_INPUT = 0x0002, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_action_inout *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_action_inout *` */
+ LY_STMT_OUTPUT = 0x0004, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_action_inout *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_action_inout *` */
+ LY_STMT_ACTION = 0x0008, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_action *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_action *` */
+ LY_STMT_RPC = 0x0010, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_action *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_action *` */
+ LY_STMT_ANYDATA = 0x0020, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_anydata *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_anydata *` */
+ LY_STMT_ANYXML = 0x0040, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_anydata *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_anydata *` */
+ LY_STMT_AUGMENT = 0x0080, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_augment *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_CASE = 0x0100, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_case *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_case *` */
+ LY_STMT_CHOICE = 0x0200, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_choice *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_choice *` */
+ LY_STMT_CONTAINER = 0x0400, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_container *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_container *` */
+ LY_STMT_GROUPING = 0x0800, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_grp *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_LEAF = 0x1000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_leaf *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_leaf *` */
+ LY_STMT_LEAF_LIST = 0x2000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_leaflist *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_leaflist *` */
+ LY_STMT_LIST = 0x4000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_list *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node_list *` */
+ LY_STMT_USES = 0x8000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_node_uses *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_node *` */
+
+ LY_STMT_ARGUMENT = 0x10000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_ext *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_ext *` */
+ LY_STMT_BASE = 0x20000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char **`[]
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_ident *` */
+ LY_STMT_BELONGS_TO = 0x30000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_submodule *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_BIT = 0x40000, /**< ::lysp_ext_substmt.storage - `struct lysp_type_enum *`[]
+ ::lysp_ext_instance.parent - `struct lysp_type_enum *`
+ ::lysc_ext_substmt.storage - `struct lysc_type_bitenum_item *`[]
+ ::lysc_ext_instance.parent - `struct lysc_type_bitenum_item *` */
+ LY_STMT_CONFIG = 0x50000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `uint16_t *`
+ ::lysc_ext_substmt.storage - `uint16_t *`
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_CONTACT = 0x60000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_(sub)module *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_DEFAULT = 0x70000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_qname *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_node *`, `struct lysc_type *` (typedef) */
+ LY_STMT_DESCRIPTION = 0x80000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - compiled parent statement */
+ LY_STMT_DEVIATE = 0x90000, /**< ::lysp_ext_substmt.storage - `struct lysp_deviate *`[]
+ ::lysp_ext_instance.parent - `struct lysp_deviate *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - not compiled */
+ LY_STMT_DEVIATION = 0xA0000, /**< ::lysp_ext_substmt.storage - `struct lysp_deviation *`[]
+ ::lysp_ext_instance.parent - `struct lysp_deviation *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_ENUM = 0xB0000, /**< ::lysp_ext_substmt.storage - `struct lysp_type_enum *`[]
+ ::lysp_ext_instance.parent - `struct lysp_type_enum *`
+ ::lysc_ext_substmt.storage - `struct lysc_type_bitenum_item *`[]
+ ::lysc_ext_instance.parent - `struct lysc_type_bitenum_item *` */
+ LY_STMT_ERROR_APP_TAG = 0xC0000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - compiled restriction structure */
+ LY_STMT_ERROR_MESSAGE = 0xD0000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - compiled restriction structure */
+ LY_STMT_EXTENSION = 0xE0000, /**< ::lysp_ext_substmt.storage - `struct lysp_ext *`[]
+ ::lysp_ext_instance.parent - `struct lysp_ext *`
+ ::lysc_ext_substmt.storage - not compiled explicitly
+ ::lysc_ext_instance.parent - `struct lysc_ext *` */
+ LY_STMT_EXTENSION_INSTANCE = 0xF0000, /**< ::lysp_ext_substmt.storage - `struct lysp_ext_instance *`[]
+ ::lysc_ext_substmt.storage - `struct lysc_ext_instance *`[] */
+ LY_STMT_FEATURE = 0x100000, /**< ::lysp_ext_substmt.storage - `struct lysp_feature *`[]
+ ::lysp_ext_instance.parent - `struct lysp_feature *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - not compiled */
+ LY_STMT_FRACTION_DIGITS = 0x110000, /**< ::lysp_ext_substmt.storage - `uint8_t *`
+ ::lysp_ext_instance.parent - `struct lysp_type *`
+ ::lysc_ext_substmt.storage - `uint8_t *`
+ ::lysc_ext_instance.parent - `struct lysc_type *` */
+ LY_STMT_IDENTITY = 0x120000, /**< ::lysp_ext_substmt.storage - `struct lysp_ident *`[]
+ ::lysp_ext_instance.parent - `struct lysp_ident *`
+ ::lysc_ext_substmt.storage - `struct lysc_ident *`[]
+ ::lysc_ext_instance.parent - `struct lysc_ident *` */
+ LY_STMT_IF_FEATURE = 0x130000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_qname *`[]
+ ::lysc_ext_substmt.storage - no storage, evaluated when compiled
+ ::lysc_ext_instance.parent - compiled parent statement */
+ LY_STMT_IMPORT = 0x140000, /**< ::lysp_ext_substmt.storage - `struct lysp_import *`[]
+ ::lysp_ext_instance.parent - `struct lysp_import *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - not compiled */
+ LY_STMT_INCLUDE = 0x150000, /**< ::lysp_ext_substmt.storage - `struct lysp_include *`[]
+ ::lysp_ext_instance.parent - `struct lysp_include *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - not compiled */
+ LY_STMT_KEY = 0x160000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_node_list *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_node_list *` */
+ LY_STMT_LENGTH = 0x170000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_range *` */
+ LY_STMT_MANDATORY = 0x180000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `uint16_t *`
+ ::lysc_ext_substmt.storage - `uint16_t *`
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_MAX_ELEMENTS = 0x190000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `uint32_t *`
+ ::lysc_ext_substmt.storage - `uint32_t *`
+ ::lysc_ext_instance.parent - `struct lysc_node_list *` */
+ LY_STMT_MIN_ELEMENTS = 0x1A0000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `uint32_t *`
+ ::lysc_ext_substmt.storage - `uint32_t *`
+ ::lysc_ext_instance.parent - `struct lysc_node_list *` */
+ LY_STMT_MODIFIER = 0x1B0000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_pattern *` */
+ LY_STMT_MODULE = 0x1C0000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_module *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_MUST = 0x1D0000, /**< ::lysp_ext_substmt.storage - `struct lysp_restr *`[]
+ ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage - `struct lysc_must *`[]
+ ::lysc_ext_instance.parent - `struct lysc_must *` */
+ LY_STMT_NAMESPACE = 0x1E0000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_module *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_ORDERED_BY = 0x1F0000, /**< ::lysp_ext_substmt.storage - `uint16_t *`
+ ::lysp_ext_instance.parent - `struct lysp_node *`
+ ::lysc_ext_substmt.storage - `uint16_t *`
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_ORGANIZATION = 0x200000, /**< ::lysp_ext_substmt.storage - `const char *`
+ ::lysp_ext_instance.parent - `struct lysp_(sub)module *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_PATH = 0x210000, /**< ::lysp_ext_substmt.storage - `struct lyxp_expr *`
+ ::lysp_ext_instance.parent - `struct lysp_type *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_type *` */
+ LY_STMT_PATTERN = 0x220000, /**< ::lysp_ext_substmt.storage - `struct lysp_restr *`[]
+ ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage - `struct lysc_pattern **`[]
+ ::lysc_ext_instance.parent - `struct lysc_pattern *` */
+ LY_STMT_POSITION = 0x230000, /**< ::lysp_ext_substmt.storage - `int64_t *`
+ ::lysp_ext_instance.parent - `struct lysp_type_enum *`
+ ::lysc_ext_substmt.storage - `int64_t *`
+ ::lysc_ext_instance.parent - `struct lysc_type_bitenum_item *` */
+ LY_STMT_PREFIX = 0x240000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_PRESENCE = 0x250000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_node_container *` */
+ LY_STMT_RANGE = 0x260000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_restr *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_range *` */
+ LY_STMT_REFERENCE = 0x270000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - compiled parent statement */
+ LY_STMT_REFINE = 0x280000, /**< ::lysp_ext_substmt.storage - `struct lysp_refine *`[]
+ ::lysp_ext_instance.parent - `struct lysp_refine *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_node *` */
+ LY_STMT_REQUIRE_INSTANCE = 0x290000, /**< ::lysp_ext_substmt.storage - `uint8_t *`
+ ::lysp_ext_instance.parent - `struct lysp_type *`
+ ::lysc_ext_substmt.storage - `uint8_t *`
+ ::lysc_ext_instance.parent - `struct lysc_type *` */
+ LY_STMT_REVISION = 0x2A0000, /**< ::lysp_ext_substmt.storage - `struct lysp_revision *`[]
+ ::lysp_ext_instance.parent - `struct lysp_revision *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - not compiled */
+ LY_STMT_REVISION_DATE = 0x2B0000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - not compiled */
+ LY_STMT_STATUS = 0x2C0000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `uint16_t *`
+ ::lysc_ext_substmt.storage - `uint16_t *`
+ ::lysc_ext_instance.parent - compiled parent statement */
+ LY_STMT_SUBMODULE = 0x2D0000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_submodule *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_TYPE = 0x2E0000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_type *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_type *` */
+ LY_STMT_TYPEDEF = 0x2F0000, /**< ::lysp_ext_substmt.storage - `struct lysp_tpdf *`[]
+ ::lysp_ext_instance.parent - `struct lysp_tpdf *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_type *` */
+ LY_STMT_UNIQUE = 0x300000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_qname *`[]
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_node_list *` */
+ LY_STMT_UNITS = 0x310000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `const char *`
+ ::lysc_ext_substmt.storage - `const char *`
+ ::lysc_ext_instance.parent - `struct lysc_node *`, `struct lysc_type *` (typedef) */
+ LY_STMT_VALUE = 0x320000, /**< ::lysp_ext_substmt.storage - `int64_t *`
+ ::lysp_ext_instance.parent - `struct lysp_type_enum *`
+ ::lysc_ext_substmt.storage - `int64_t *`
+ ::lysc_ext_instance.parent - `struct lysc_type_bitenum_item *` */
+ LY_STMT_WHEN = 0x330000, /**< ::lysp_ext_substmt.storage and ::lysp_ext_instance.parent - `struct lysp_when *`
+ ::lysc_ext_substmt.storage and ::lysc_ext_instance.parent - `struct lysc_when *` */
+ LY_STMT_YANG_VERSION = 0x340000, /**< ::lysp_ext_substmt.storage - `uint8_t *`
+ ::lysp_ext_instance.parent - `struct lysp_(sub)module *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_module *` */
+ LY_STMT_YIN_ELEMENT = 0x350000, /**< ::lysp_ext_substmt.storage - `uint16_t *`
+ ::lysp_ext_instance.parent - `struct lysp_ext *`
+ ::lysc_ext_substmt.storage - not compiled
+ ::lysc_ext_instance.parent - `struct lysc_ext *` */
+
+ /* separated from the list of statements
+ * the following tokens are part of the syntax and parsers have to work
+ * with them, but they are not a standard YANG statements
+ */
+ LY_STMT_SYNTAX_SEMICOLON,
+ LY_STMT_SYNTAX_LEFT_BRACE,
+ LY_STMT_SYNTAX_RIGHT_BRACE,
+
+ /*
+ * YIN-specific tokens, still they are part of the syntax, but not the standard statements
+ */
+ LY_STMT_ARG_TEXT,
+ LY_STMT_ARG_VALUE
+};
+
+/**
+ * @brief Structure representing a generic parsed YANG substatement in an extension instance.
+ */
+struct lysp_stmt {
+ const char *stmt; /**< identifier of the statement */
+ const char *arg; /**< statement's argument */
+ LY_VALUE_FORMAT format; /**< prefix format of the identifier/argument (::LY_VALUE_XML is YIN format) */
+ void *prefix_data; /**< Format-specific data for prefix resolution (see ly_resolve_prefix()) */
+
+ struct lysp_stmt *next; /**< link to the next statement */
+ struct lysp_stmt *child; /**< list of the statement's substatements (linked list) */
+ uint16_t flags; /**< statement flags, can be set to LYS_YIN_ATTR */
+ enum ly_stmt kw; /**< numeric respresentation of the stmt value */
+};
+
+/**
+ * @brief Structure representing a parsed known YANG substatement in an extension instance.
+ */
+struct lysp_ext_substmt {
+ enum ly_stmt stmt; /**< parsed substatement */
+ void *storage; /**< pointer to the parsed storage of the statement according to the specific
+ lys_ext_substmt::stmt */
+};
+
+/**
+ * @brief YANG extension parsed instance.
+ */
+struct lysp_ext_instance {
+ const char *name; /**< extension identifier, including possible prefix */
+ const char *argument; /**< optional value of the extension's argument */
+ LY_VALUE_FORMAT format; /**< prefix format of the extension name/argument (::LY_VALUE_XML is YIN format) */
+ void *prefix_data; /**< format-specific data for prefix resolution (see ly_resolve_prefix()) */
+ struct lysp_ext *def; /**< pointer to the extension definition */
+
+ void *parent; /**< pointer to the parent statement holding the extension instance(s), use
+ ::lysp_ext_instance#parent_stmt to access the value/structure */
+ enum ly_stmt parent_stmt; /**< type of the parent statement */
+ LY_ARRAY_COUNT_TYPE parent_stmt_index; /**< index of the stamenet in case the parent does not point to the parent
+ statement directly and it is an array */
+ uint16_t flags; /**< ::LYS_INTERNAL value (@ref snodeflags) */
+
+ const struct lyplg_ext_record *record; /**< extension definition plugin record, if any */
+ struct lysp_ext_substmt *substmts; /**< list of supported known YANG statements with the pointer to their
+ parsed data ([sized array](@ref sizedarrays)) */
+ void *parsed; /**< private plugin parsed data */
+ struct lysp_stmt *child; /**< list of generic (unknown) YANG statements */
+};
+
+/**
+ * @brief Structure representing a compiled known YANG substatement in an extension instance.
+ */
+struct lysc_ext_substmt {
+ enum ly_stmt stmt; /**< compiled substatement */
+ void *storage; /**< pointer to the compiled storage of the statement according to the specific
+ lys_ext_substmt::stmt */
+};
+
+/**
+ * @brief YANG extension compiled instance.
+ */
+struct lysc_ext_instance {
+ struct lysc_ext *def; /**< pointer to the extension definition */
+ const char *argument; /**< optional value of the extension's argument */
+ struct lys_module *module; /**< module where the extension instantiated is defined */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+
+ void *parent; /**< pointer to the parent element holding the extension instance(s), use
+ ::lysc_ext_instance#parent_stmt to access the value/structure */
+ enum ly_stmt parent_stmt; /**< type of the parent statement */
+ LY_ARRAY_COUNT_TYPE parent_stmt_index; /**< index of the stamenet in case the parent does not point to the parent
+ statement directly and it is an array */
+
+ struct lysc_ext_substmt *substmts; /**< list of supported known YANG statements with the pointer to their
+ compiled data ([sized array](@ref sizedarrays)) */
+ void *compiled; /**< private plugin compiled data */
+};
+
+/**
+ * @brief Macro to define plugin information in external plugins
+ *
+ * Use as follows:
+ * LYPLG_EXTENSIONS = {{<filled information of ::lyplg_ext_record>}, ..., {0}};
+ */
+#define LYPLG_EXTENSIONS \
+ uint32_t plugins_extensions_apiver__ = LYPLG_EXT_API_VERSION; \
+ const struct lyplg_ext_record plugins_extensions__[]
+
+/**
+ * @defgroup pluginsExtensionsParse Plugins: Extensions parsing support
+ * @ingroup pluginsExtensions
+ *
+ * Implementing extension plugin parse callback.
+ *
+ * @{
+ */
+
+/**
+ * @brief Callback for parsing extension instance substatements.
+ *
+ * All known YANG substatements can easily be parsed using ::lyplg_ext_parse_extension_instance.
+ *
+ * @param[in] pctx Parse context.
+ * @param[in,out] ext Parsed extension instance data.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the extension instance is not supported and should be removed.
+ * @return LY_ERR error on error.
+ */
+typedef LY_ERR (*lyplg_ext_parse_clb)(struct lysp_ctx *pctx, struct lysp_ext_instance *ext);
+
+/**
+ * @brief Log a message from an extension plugin using the parsed extension instance.
+ *
+ * @param[in] pctx Parse context to use.
+ * @param[in] ext Parsed extensiopn instance.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] err_no Error type code.
+ * @param[in] format Format string to print.
+ * @param[in] ... Format variable parameters.
+ */
+LIBYANG_API_DECL void lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance *ext,
+ LY_LOG_LEVEL level, LY_ERR err_no, const char *format, ...);
+
+/**
+ * @brief Get current parsed module from a parse context.
+ *
+ * @param[in] pctx Parse context.
+ * @return Current (local) parse mod.
+ */
+LIBYANG_API_DECL const struct lysp_module *lyplg_ext_parse_get_cur_pmod(const struct lysp_ctx *pctx);
+
+/**
+ * @brief Parse substatements of an extension instance.
+ *
+ * Uses standard libyang schema compiler to transform YANG statements into the parsed schema structures. The plugins are
+ * supposed to use this function when the extension instance's substatements can be parsed in a standard way.
+ *
+ * @param[in] pctx Parse context.
+ * @param[in,out] ext Parsed extension instance with the prepared ::lysp_ext_instance.substmts array, which will be
+ * updated by storing the parsed data.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_parse_extension_instance(struct lysp_ctx *pctx, struct lysp_ext_instance *ext);
+
+/** @} pluginsExtensionsParse */
+
+/**
+ * @defgroup pluginsExtensionsCompile Plugins: Extensions compilation support
+ * @ingroup pluginsExtensions
+ *
+ * Implementing extension plugin compile callback.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup scflags Schema compile flags
+ *
+ * Flags to modify schema compilation process and change the way how the particular statements are being compiled. *
+ * @{
+ */
+#define LYS_COMPILE_GROUPING 0x01 /**< Compiling (validation) of a non-instantiated grouping.
+ In this case not all the restrictions are checked since they can
+ be valid only in the real placement of the grouping. This is
+ the case of any restriction that needs to look out of the statements
+ themselves, since the context is not known. */
+#define LYS_COMPILE_DISABLED 0x02 /**< Compiling a disabled subtree (by its if-features). Meaning
+ it will be removed at the end of compilation and should not be
+ added to any unres sets. */
+#define LYS_COMPILE_NO_CONFIG 0x04 /**< ignore config statements, neither inherit config value */
+#define LYS_COMPILE_NO_DISABLED 0x08 /**< ignore if-feature statements */
+
+#define LYS_COMPILE_RPC_INPUT (LYS_IS_INPUT | LYS_COMPILE_NO_CONFIG) /**< Internal option when compiling schema tree of RPC/action input */
+#define LYS_COMPILE_RPC_OUTPUT (LYS_IS_OUTPUT | LYS_COMPILE_NO_CONFIG) /**< Internal option when compiling schema tree of RPC/action output */
+#define LYS_COMPILE_NOTIFICATION (LYS_IS_NOTIF | LYS_COMPILE_NO_CONFIG) /**< Internal option when compiling schema tree of Notification */
+
+/** @} scflags */
+
+/**
+ * @brief Callback to compile extension from the lysp_ext_instance to the lysc_ext_instance. The later structure is generally prepared
+ * and only the extension specific data are supposed to be added (if any).
+ *
+ * The parsed generic statements can be processed by the callback on its own or the ::lyplg_ext_compile_extension_instance()
+ * function can be used to let the compilation to libyang following the standard rules for processing the YANG statements.
+ *
+ * @param[in] cctx Current compile context.
+ * @param[in] extp Parsed extension instance data.
+ * @param[in,out] ext Prepared compiled extension instance structure where an addition, extension-specific, data are
+ * supposed to be placed for later use (data validation or use of external tool).
+ * @return LY_SUCCESS in case of success.
+ * @return LY_ENOT in case the extension instance is not supported and should be removed.
+ * @return LY_ERR error on error.
+ */
+typedef LY_ERR (*lyplg_ext_compile_clb)(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp,
+ struct lysc_ext_instance *ext);
+
+/**
+ * @brief Log a message from an extension plugin using the compiled extension instance.
+ *
+ * @param[in] cctx Optional compile context to generate the path from.
+ * @param[in] ext Compiled extension instance.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] err_no Error type code.
+ * @param[in] format Format string to print.
+ */
+LIBYANG_API_DECL void lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instance *ext,
+ LY_LOG_LEVEL level, LY_ERR err_no, const char *format, ...);
+
+/**
+ * @brief Log a message from an extension plugin using the compiled extension instance with an explicit error path.
+ *
+ * @param[in] path Log error path to use.
+ * @param[in] ext Compiled extension instance.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] err_no Error type code.
+ * @param[in] format Format string to print.
+ */
+LIBYANG_API_DECL void lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext,
+ LY_LOG_LEVEL level, LY_ERR err_no, const char *format, ...);
+
+/**
+ * @brief YANG schema compilation context getter for libyang context.
+ *
+ * @param[in] ctx YANG schema compilation context.
+ * @return libyang context connected with the compilation context.
+ */
+LIBYANG_API_DECL struct ly_ctx *lyplg_ext_compile_get_ctx(const struct lysc_ctx *ctx);
+
+/**
+ * @brief YANG schema compilation context getter for compilation options.
+ *
+ * @param[in] ctx YANG schema compilation context.
+ * @return pointer to the compilation options to allow modifying them with @ref scflags values.
+ */
+LIBYANG_API_DECL uint32_t *lyplg_ext_compile_get_options(const struct lysc_ctx *ctx);
+
+/**
+ * @brief YANG schema compilation context getter for current module.
+ *
+ * @param[in] ctx YANG schema compilation context.
+ * @return current module.
+ */
+LIBYANG_API_DECL const struct lys_module *lyplg_ext_compile_get_cur_mod(const struct lysc_ctx *ctx);
+
+/**
+ * @brief YANG schema compilation context getter for currently processed module.
+ *
+ * @param[in] ctx YANG schema compilation context.
+ * @return Currently processed module.
+ */
+LIBYANG_API_DECL struct lysp_module *lyplg_ext_compile_get_pmod(const struct lysc_ctx *ctx);
+
+/**
+ * @brief Compile substatements of an extension instance.
+ *
+ * Uses standard libyang schema compiler to transform YANG statements into the compiled schema structures. The plugins are
+ * supposed to use this function when the extension instance's substatements are supposed to be compiled in a standard way
+ * (or if just the @ref scflags are enough to modify the compilation process).
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] extp Parsed representation of the extension instance being processed.
+ * @param[in,out] ext Compiled extension instance with the prepared ::lysc_ext_instance.substmts array, which will be updated
+ * by storing the compiled data.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID if compilation of the substatements fails.
+ * @return LY_ENOT if the extension is disabled (by if-feature) and should be ignored.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *extp,
+ struct lysc_ext_instance *ext);
+
+/** @} pluginsExtensionsCompile */
+
+/**
+ * @defgroup pluginsExtensionsSprinterInfo Plugins: Extensions schema info printer support
+ * @ingroup pluginsExtensions
+ *
+ * Implementing extension plugin schema info printer callback.
+ *
+ * @{
+ */
+
+/**
+ * @brief Callback to print the compiled extension instance's private data in the INFO format.
+ *
+ * @param[in] ctx YANG printer context to provide output handler and other information for printing.
+ * @param[in] ext The compiled extension instance, mainly to access the extensions.
+ * @param[in,out] flag Flag to be shared with the caller regarding the opening brackets - 0 if the '{' not yet printed,
+ * 1 otherwise.
+ * @return LY_SUCCESS when everything was fine, other LY_ERR values in case of failure
+ */
+typedef LY_ERR (*lyplg_ext_sprinter_info_clb)(struct lyspr_ctx *ctx, struct lysc_ext_instance *ext, ly_bool *flag);
+
+/**
+ * @brief YANG printer context getter for output handler.
+ *
+ * @param[in] ctx YANG printer context.
+ * @return Output handler where the data are being printed. Note that the address of the handler pointer in the context is
+ * returned to allow to modify the handler.
+ */
+LIBYANG_API_DECL struct ly_out **lyplg_ext_print_get_out(const struct lyspr_ctx *ctx);
+
+/**
+ * @brief YANG printer context getter for printer options.
+ *
+ * @param[in] ctx YANG printer context.
+ * @return pointer to the printer options to allow modifying them with @ref schemaprinterflags values.
+ */
+LIBYANG_API_DECL uint32_t *lyplg_ext_print_get_options(const struct lyspr_ctx *ctx);
+
+/**
+ * @brief YANG printer context getter for printer indentation level.
+ *
+ * @param[in] ctx YANG printer context.
+ * @return pointer to the printer's indentation level to allow modifying its value.
+ */
+LIBYANG_API_DECL uint16_t *lyplg_ext_print_get_level(const struct lyspr_ctx *ctx);
+
+/**
+ * @brief Print substatements of an extension instance in info format (compiled YANG).
+ *
+ * Generic function to access YANG printer functions from the extension plugins (::lyplg_ext_sprinter_info_clb).
+ *
+ * @param[in] ctx YANG printer context to provide output handler and other information for printing.
+ * @param[in] ext The compiled extension instance to access the extensions and substatements data.
+ * @param[in,out] flag Flag to be shared with the caller regarding the opening brackets - 0 if the '{' not yet printed,
+ * 1 otherwise.
+ */
+LIBYANG_API_DECL void lyplg_ext_print_info_extension_instance(struct lyspr_ctx *ctx, const struct lysc_ext_instance *ext,
+ ly_bool *flag);
+
+/** @} pluginsExtensionsSprinterInfo */
+
+/**
+ * @defgroup pluginsExtensionsSprinterTree Plugins: Extensions schema parsed and compiled tree printer support
+ * @ingroup pluginsExtensions
+ *
+ * Implementing extension plugin schema parsed and compiled tree printer callback.
+ *
+ * @{
+ */
+
+/**
+ * @brief Callback to print parent node of @p ext or to print the contents of the extension.
+ *
+ * Function is called in two different cases. If the printer_tree needs the tree-diagram form of a parent node,
+ * then @p ctx is set to NULL. In the second case, if printer_tree needs to print the contents of the extension,
+ * then @p ctx is set and function must prepare the nodes that should be printed using the
+ * lyplg_ext_sprinter_tree* functions.
+ *
+ * @param[in] ext Extension instance.
+ * @param[in,out] ctx Context for the tree printer. Extension contents can be inserted into it by functions
+ * lyplg_ext_sprinter_ctree_add_ext_nodes(), lyplg_ext_sprinter_ctree_add_nodes() or by their ptree alternatives.
+ * It parameter is set to NULL, then @p flags and @p add_opts are used by printer_tree.
+ * @param[out] flags Optional override tree-diagram \<flags\> in a parent node. If @p ctx is set, ignore this parameter.
+ * @param[out] add_opts Additional tree-diagram \<opts\> string in a parent node which is printed before \<opts\>. If @p ctx
+ * is set, ignore this parameter.
+ * @return LY_ERR value.
+ */
+typedef LY_ERR (*lyplg_ext_sprinter_ctree_clb)(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **flags, const char **add_opts);
+
+/**
+ * @brief Callback for rewriting the tree-diagram form of a specific node.
+ *
+ * If this callback is set, then it is called for each node that belongs to the extension instance.
+ *
+ * @param[in] node Node whose tree-diagram form can be modified by the function.
+ * @param[in,out] plugin_priv Private context set by plugin.
+ * @param[out] skip Flag set to 1 removes the node from printed diagram.
+ * @param[out] flags Override tree-diagram \<flags\> string in the @p node.
+ * @param[out] add_opts Additional tree-diagram \<opts\> string in the @p node which is printed before \<opts\>.
+ * @return LY_ERR value.
+ */
+typedef LY_ERR (*lyplg_ext_sprinter_ctree_override_clb)(const struct lysc_node *node, const void *plugin_priv,
+ ly_bool *skip, const char **flags, const char **add_opts);
+
+/**
+ * @brief Registration of printing a group of nodes, which is already in the extension.
+ *
+ * @param[in] ctx Context of printer_tree in which the group of nodes is saved and later printed.
+ * @param[in] ext Extension in which the group of nodes will be searched.
+ * @param[in] clb Override function that will be applied to each delivered node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_sprinter_ctree_add_ext_nodes(const struct lyspr_tree_ctx *ctx,
+ struct lysc_ext_instance *ext, lyplg_ext_sprinter_ctree_override_clb clb);
+
+/**
+ * @brief Registration of printing the group of nodes which were defined in the plugin.
+ *
+ * @param[in] ctx Context of printer_tree in which the group of nodes is saved and later printed.
+ * @param[in] nodes Points to the first node in group.
+ * @param[in] clb Override function that will be applied to each delivered node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_sprinter_ctree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_node *nodes,
+ lyplg_ext_sprinter_ctree_override_clb clb);
+
+/**
+ * @brief Registration of plugin-private data defined by the plugin that is shared between override_clb calls.
+ *
+ * @param[in] ctx Context of printer_tree in which plugin-private data will be saved.
+ * @param[in] plugin_priv Plugin-private data shared between oberride_clb calls.
+ * @param[in] free_clb Release function for @p plugin_priv.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_sprinter_tree_set_priv(const struct lyspr_tree_ctx *ctx, void *plugin_priv,
+ void (*free_clb)(void *plugin_priv));
+
+/**
+ * @copydoc lyplg_ext_sprinter_ctree_clb
+ */
+typedef LY_ERR (*lyplg_ext_sprinter_ptree_clb)(struct lysp_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **flags, const char **add_opts);
+
+/**
+ * @copydoc lyplg_ext_sprinter_ctree_override_clb
+ */
+typedef LY_ERR (*lyplg_ext_sprinter_ptree_override_clb)(const struct lysp_node *node, const void *plugin_priv,
+ ly_bool *skip, const char **flags, const char **add_opts);
+
+/**
+ * @copydoc lyplg_ext_sprinter_ctree_add_ext_nodes
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_sprinter_ptree_add_ext_nodes(const struct lyspr_tree_ctx *ctx,
+ struct lysp_ext_instance *ext, lyplg_ext_sprinter_ptree_override_clb clb);
+
+/**
+ * @copydoc lyplg_ext_sprinter_ctree_add_nodes
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_sprinter_ptree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_node *nodes,
+ lyplg_ext_sprinter_ptree_override_clb clb);
+
+/** @} pluginsExtensionsSprinterTree */
+
+/*
+ * data node
+ */
+
+/**
+ * @brief Callback called for all data nodes connected to the extension instance.
+ *
+ * Can be used for additional data node validation. Is called only after the whole data tree is created and standard
+ * validation succeeds. Not called when parsing data and ::LYD_PARSE_ONLY is used.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] node Data node to process.
+ * @param[in] validate_options Options used for the validation phase, see @ref datavalidationoptions.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+typedef LY_ERR (*lyplg_ext_data_node_clb)(struct lysc_ext_instance *ext, struct lyd_node *node, uint32_t validate_options);
+
+/*
+ * snode
+ */
+
+/**
+ * @brief Callback for getting a schema node for a new YANG instance data described by an extension instance.
+ * Needed only if the extension instance supports some nested standard YANG data.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] parent Parsed parent data node. Set if @p sparent is NULL.
+ * @param[in] sparent Schema parent node. Set if @p parent is NULL.
+ * @param[in] prefix Element prefix, if any.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of @p prefix.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] snode Schema node to use for parsing the node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the data are not described by @p ext.
+ * @return LY_ERR on error.
+ */
+typedef LY_ERR (*lyplg_ext_data_snode_clb)(struct lysc_ext_instance *ext, const struct lyd_node *parent,
+ const struct lysc_node *sparent, const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data,
+ const char *name, size_t name_len, const struct lysc_node **snode);
+
+/*
+ * validate
+ */
+
+/**
+ * @brief Callback for validating parsed YANG instance data described by an extension instance.
+ *
+ * This callback is used only for nested data definition (with a standard YANG schema parent).
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] sibling First sibling with schema node returned by ::lyplg_ext_data_snode_clb.
+ * @param[in] dep_tree Tree to be used for validating references from the operation subtree, if operation.
+ * @param[in] data_type Validated data type, can be ::LYD_TYPE_DATA_YANG, ::LYD_TYPE_RPC_YANG, ::LYD_TYPE_NOTIF_YANG,
+ * or ::LYD_TYPE_REPLY_YANG.
+ * @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[out] diff Optional diff with any changes made by the validation.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+typedef LY_ERR (*lyplg_ext_data_validate_clb)(struct lysc_ext_instance *ext, struct lyd_node *sibling,
+ const struct lyd_node *dep_tree, enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff);
+
+/*
+ * parse free
+ */
+
+/**
+ * @brief Callback to free the extension-specific data created by its parsing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in,out] ext Parsed extension structure to free.
+ */
+typedef void (*lyplg_ext_parse_free_clb)(const struct ly_ctx *ctx, struct lysp_ext_instance *ext);
+
+/**
+ * @brief Free the extension instance's data parsed with ::lyplg_ext_parse_extension_instance().
+ *
+ * @param[in] ctx libyang context
+ * @param[in] substmts Extension instance substatements to free.
+ */
+LIBYANG_API_DECL void lyplg_ext_pfree_instance_substatements(const struct ly_ctx *ctx, struct lysp_ext_substmt *substmts);
+
+/*
+ * compile free
+ */
+
+/**
+ * @brief Callback to free the extension-specific data created by its compilation.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in,out] ext Compiled extension structure to free.
+ */
+typedef void (*lyplg_ext_compile_free_clb)(const struct ly_ctx *ctx, struct lysc_ext_instance *ext);
+
+/**
+ * @brief Free the extension instance's data compiled with ::lyplg_ext_compile_extension_instance().
+ *
+ * @param[in] ctx libyang context
+ * @param[in] substmts Extension instance substatements to free.
+ */
+LIBYANG_API_DECL void lyplg_ext_cfree_instance_substatements(const struct ly_ctx *ctx, struct lysc_ext_substmt *substmts);
+
+/**
+ * @brief Extension plugin implementing various aspects of a YANG extension
+ */
+struct lyplg_ext {
+ const char *id; /**< plugin identification (mainly for distinguish incompatible versions
+ of the plugins for external tools) */
+ lyplg_ext_parse_clb parse; /**< callback to parse the extension instance substatements */
+ lyplg_ext_compile_clb compile; /**< callback to compile extension instance from the parsed data */
+ lyplg_ext_sprinter_info_clb printer_info; /**< callback to print the compiled content (info format) of the extension
+ instance */
+ lyplg_ext_sprinter_ctree_clb printer_ctree; /**< callback to print tree format of compiled node containing the
+ compiled content of the extension instance */
+ lyplg_ext_sprinter_ptree_clb printer_ptree; /**< callback to print tree format of parsed node containing the
+ parsed content of the extension instance */
+ lyplg_ext_data_node_clb node; /**< callback to validate most relevant data instance for the extension
+ instance */
+ lyplg_ext_data_snode_clb snode; /**< callback to get schema node for nested YANG data */
+ lyplg_ext_data_validate_clb validate; /**< callback to validate parsed data instances according to the extension
+ definition */
+
+ lyplg_ext_parse_free_clb pfree; /**< free the extension-specific data created by its parsing */
+ lyplg_ext_compile_free_clb cfree; /**< free the extension-specific data created by its compilation */
+};
+
+struct lyplg_ext_record {
+ /* plugin identification */
+ const char *module; /**< name of the module where the extension is defined */
+ const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
+ which is not an optimal approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same pointer to the plugin functions. The
+ only valid use case for the NULL revision is the case the module has no revision. */
+ const char *name; /**< YANG name of the extension */
+
+ /* runtime data */
+ struct lyplg_ext plugin; /**< data to utilize plugin implementation */
+};
+
+/**
+ * @brief Stringify statement identifier.
+ *
+ * @param[in] stmt The statement identifier to stringify.
+ * @return Constant string representation of the given @p stmt.
+ */
+LIBYANG_API_DECL const char *lyplg_ext_stmt2str(enum ly_stmt stmt);
+
+/**
+ * @brief Convert nodetype to statement identifier
+ *
+ * @param[in] nodetype Nodetype to convert.
+ * @return Statement identifier representing the given @p nodetype.
+ */
+LIBYANG_API_DECL enum ly_stmt lyplg_ext_nodetype2stmt(uint16_t nodetype);
+
+/**
+ * @brief Get compiled ext instance storage for a specific statement.
+ *
+ * @param[in] ext Compiled ext instance.
+ * @param[in] stmt Compiled statement. Can be a mask when the first match is returned, it is expected the storage is
+ * the same for all the masked statements.
+ * @param[in] storage_size Size of the value at @p storage address (dereferenced).
+ * @param[out] storage Compiled ext instance substatement storage, NULL if was not compiled.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the substatement is not supported.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size,
+ const void **storage);
+
+/**
+ * @brief Get parsed ext instance storage for a specific statement.
+ *
+ * @param[in] ext Compiled ext instance.
+ * @param[in] stmt Parsed statement. Can be a mask when the first match is returned, it is expected the storage is
+ * the same for all the masked statements.
+ * @param[in] storage_size Size of the value at @p storage address (dereferenced).
+ * @param[out] storage Parsed ext instance substatement storage, NULL if was not parsed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the substatement is not supported.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt,
+ uint32_t storage_size, const void **storage);
+
+/**
+ * @brief Get specific run-time extension instance data from a callback set by ::ly_ctx_set_ext_data_clb().
+ *
+ * @param[in] ctx Context with the callback.
+ * @param[in] ext Compiled extension instance.
+ * @param[out] ext_data Provided extension instance data.
+ * @param[out] ext_data_free Whether the extension instance should free @p ext_data or not.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_get_data(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, void **ext_data,
+ ly_bool *ext_data_free);
+
+/**
+ * @brief Insert extension instance data into a parent.
+ *
+ * @param[in] parent Parent node to insert into.
+ * @param[in] first First top-level sibling node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_insert(struct lyd_node *parent, struct lyd_node *first);
+
+/**
+ * @brief Expand parent-reference xpath expressions
+ *
+ * @param[in] ext Context allocated for extension.
+ * @param[out] refs Set of schema node matching parent-reference XPaths.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, struct ly_set **refs);
+
+/**
+ * @brief Allocate a new context for a particular instance of the yangmnt:mount-point extension.
+ * Caller is responsible for destroying the resulting context.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[out] ctx A context with modules loaded from the list found in
+ * the extension data.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx);
+
+/** @} pluginsExtensions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PLUGINS_EXTS_H_ */
diff --git a/src/plugins_exts/metadata.c b/src/plugins_exts/metadata.c
new file mode 100644
index 0000000..9567e07
--- /dev/null
+++ b/src/plugins_exts/metadata.c
@@ -0,0 +1,243 @@
+/**
+ * @file metadata.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang extension plugin - Metadata (RFC 7952)
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "metadata.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+#include "plugins_exts.h"
+
+struct lysp_ext_metadata {
+ struct lysp_type *type; /**< type of the metadata (mandatory) */
+ const char *units; /**< units of the leaf's type */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
+};
+
+struct lysc_ext_metadata {
+ struct lysc_type *type; /**< type of the metadata (mandatory) */
+ const char *units; /**< units of the leaf's type */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
+};
+
+/**
+ * @brief Parse annotation extension instances.
+ *
+ * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
+ */
+static LY_ERR
+annotation_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ LY_ERR r;
+ struct lysp_ext_metadata *ann_pdata;
+ struct lysp_module *pmod;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* annotations can appear only at the top level of a YANG module or submodule */
+ if ((ext->parent_stmt != LY_STMT_MODULE) && (ext->parent_stmt != LY_STMT_SUBMODULE)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is allowed only at the top level of a YANG module or "
+ "submodule, but it is placed in \"%s\" statement.", ext->name, lyplg_ext_stmt2str(ext->parent_stmt));
+ return LY_EVALID;
+ }
+
+ pmod = ext->parent;
+
+ /* check for duplication */
+ LY_ARRAY_FOR(pmod->exts, u) {
+ if ((&pmod->exts[u] != ext) && (pmod->exts[u].name == ext->name) && !strcmp(pmod->exts[u].argument, ext->argument)) {
+ /* duplication of the same annotation extension in a single module */
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is instantiated multiple times.", ext->name);
+ return LY_EVALID;
+ }
+ }
+
+ /* parse annotation substatements */
+ ext->parsed = ann_pdata = calloc(1, sizeof *ann_pdata);
+ if (!ann_pdata) {
+ goto emem;
+ }
+ LY_ARRAY_CREATE_GOTO(lyplg_ext_parse_get_cur_pmod(pctx)->mod->ctx, ext->substmts, 6, r, emem);
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_IF_FEATURE;
+ ext->substmts[0].storage = &ann_pdata->iffeatures;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_UNITS;
+ ext->substmts[1].storage = &ann_pdata->units;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_STATUS;
+ ext->substmts[2].storage = &ann_pdata->flags;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[3].stmt = LY_STMT_TYPE;
+ ext->substmts[3].storage = &ann_pdata->type;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[4].stmt = LY_STMT_DESCRIPTION;
+ ext->substmts[4].storage = &ann_pdata->dsc;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[5].stmt = LY_STMT_REFERENCE;
+ ext->substmts[5].storage = &ann_pdata->ref;
+
+ if ((r = lyplg_ext_parse_extension_instance(pctx, ext))) {
+ return r;
+ }
+
+ /* check for mandatory substatements */
+ if (!ann_pdata->type) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Missing mandatory keyword \"type\" as a child of \"%s %s\".",
+ ext->name, ext->argument);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+
+emem:
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+/**
+ * @brief Compile annotation extension instances.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
+ */
+static LY_ERR
+annotation_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp, struct lysc_ext_instance *ext)
+{
+ LY_ERR ret;
+ struct lysc_ext_metadata *ann_cdata;
+
+ /* compile annotation substatements */
+ ext->compiled = ann_cdata = calloc(1, sizeof *ann_cdata);
+ if (!ann_cdata) {
+ goto emem;
+ }
+ LY_ARRAY_CREATE_GOTO(lysc_ctx_get_ctx(cctx), ext->substmts, 6, ret, emem);
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_IF_FEATURE;
+ ext->substmts[0].storage = NULL;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_UNITS;
+ ext->substmts[1].storage = &ann_cdata->units;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_STATUS;
+ ext->substmts[2].storage = &ann_cdata->flags;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[3].stmt = LY_STMT_TYPE;
+ ext->substmts[3].storage = &ann_cdata->type;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[4].stmt = LY_STMT_DESCRIPTION;
+ ext->substmts[4].storage = &ann_cdata->dsc;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[5].stmt = LY_STMT_REFERENCE;
+ ext->substmts[5].storage = &ann_cdata->ref;
+
+ ret = lyplg_ext_compile_extension_instance(cctx, extp, ext);
+ return ret;
+
+emem:
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+/**
+ * @brief INFO printer
+ *
+ * Implementation of ::lyplg_ext_sprinter_info_clb set as ::lyext_plugin::printer_info
+ */
+static LY_ERR
+annotation_printer_info(struct lyspr_ctx *ctx, struct lysc_ext_instance *ext, ly_bool *flag)
+{
+ lyplg_ext_print_info_extension_instance(ctx, ext, flag);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Free parsed annotation extension instance data.
+ *
+ * Implementation of ::lyplg_ext_parse_free_clb callback set as ::lyext_plugin::pfree.
+ */
+static void
+annotation_pfree(const struct ly_ctx *ctx, struct lysp_ext_instance *ext)
+{
+ if (!ext->substmts) {
+ return;
+ }
+
+ lyplg_ext_pfree_instance_substatements(ctx, ext->substmts);
+ free(ext->parsed);
+}
+
+/**
+ * @brief Free compiled annotation extension instance data.
+ *
+ * Implementation of ::lyplg_ext_compile_free_clb callback set as ::lyext_plugin::cfree.
+ */
+static void
+annotation_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+ if (!ext->substmts) {
+ return;
+ }
+
+ lyplg_ext_cfree_instance_substatements(ctx, ext->substmts);
+ free(ext->compiled);
+}
+
+/**
+ * @brief Plugin descriptions for the Metadata's annotation extension
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_EXTENSIONS = {
+ */
+const struct lyplg_ext_record plugins_metadata[] = {
+ {
+ .module = "ietf-yang-metadata",
+ .revision = "2016-08-05",
+ .name = "annotation",
+
+ .plugin.id = "ly2 metadata v1",
+ .plugin.parse = annotation_parse,
+ .plugin.compile = annotation_compile,
+ .plugin.printer_info = annotation_printer_info,
+ .plugin.printer_ctree = NULL,
+ .plugin.printer_ptree = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = annotation_pfree,
+ .plugin.cfree = annotation_cfree,
+ },
+ {0} /* terminating zeroed record */
+};
diff --git a/src/plugins_exts/metadata.h b/src/plugins_exts/metadata.h
new file mode 100644
index 0000000..59ea2bf
--- /dev/null
+++ b/src/plugins_exts/metadata.h
@@ -0,0 +1,66 @@
+/**
+ * @file metadata.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-yang-metadata API
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PLUGINS_EXTS_METADATA_H_
+#define LY_PLUGINS_EXTS_METADATA_H_
+
+#include "plugins_exts.h"
+#include "tree_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Metadata structure.
+ *
+ * The structure provides information about metadata of a data element. Such attributes must map to
+ * annotations as specified in RFC 7952. The only exception is the filter type (in NETCONF get operations)
+ * and edit-config's operation attributes. In XML, they are represented as standard XML attributes. In JSON,
+ * they are represented as JSON elements starting with the '@' character (for more information, see the
+ * YANG metadata RFC.
+ *
+ */
+struct lyd_meta {
+ struct lyd_node *parent; /**< data node where the metadata is placed */
+ struct lyd_meta *next; /**< pointer to the next metadata of the same element */
+ struct lysc_ext_instance *annotation; /**< pointer to the annotation's definition */
+ const char *name; /**< metadata name */
+ struct lyd_value value; /**< metadata value representation */
+};
+
+/**
+ * @brief Get the (canonical) value of a metadata node.
+ *
+ * @param[in] meta Metadata node to use.
+ * @return Canonical value.
+ */
+static inline const char *
+lyd_get_meta_value(const struct lyd_meta *meta)
+{
+ if (meta) {
+ const struct lyd_value *value = &meta->value;
+
+ return value->_canonical ? value->_canonical : lyd_value_get_canonical(meta->annotation->module->ctx, value);
+ }
+
+ return NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PLUGINS_EXTS_METADATA_H_ */
diff --git a/src/plugins_exts/nacm.c b/src/plugins_exts/nacm.c
new file mode 100644
index 0000000..5ab8daa
--- /dev/null
+++ b/src/plugins_exts/nacm.c
@@ -0,0 +1,223 @@
+/**
+ * @file nacm.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang extension plugin - NACM (RFC 6536)
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat.h"
+#include "libyang.h"
+#include "plugins_exts.h"
+
+struct nacm_dfs_arg {
+ struct lysc_ext_instance *ext;
+ struct lysc_node *parent;
+};
+
+/**
+ * @brief DFS callback implementation for inheriting the NACM extension.
+ */
+static LY_ERR
+nacm_inherit_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue)
+{
+ LY_ERR ret;
+ struct nacm_dfs_arg *arg = data;
+ struct lysc_ext_instance *inherited;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* ignore the parent from which we inherit and input/output nodes */
+ if ((node != arg->parent) && !(node->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
+ /* check that the node does not have its own NACM extension instance */
+ LY_ARRAY_FOR(node->exts, u) {
+ if (node->exts[u].def == arg->ext->def) {
+ /* the child already have its own NACM flag, so skip the subtree */
+ *dfs_continue = 1;
+ return LY_SUCCESS;
+ }
+ }
+
+ /* duplicate this one to inherit it to the child */
+ LY_ARRAY_NEW_GOTO(node->module->ctx, node->exts, inherited, ret, emem);
+
+ inherited->def = arg->ext->def;
+ inherited->parent = node;
+ inherited->parent_stmt = lyplg_ext_nodetype2stmt(node->nodetype);
+ if (arg->ext->argument) {
+ if ((ret = lydict_insert(node->module->ctx, arg->ext->argument, 0, &inherited->argument))) {
+ return ret;
+ }
+ }
+ /* copy the pointer to the static variables */
+ inherited->compiled = arg->ext->compiled;
+ }
+
+ return LY_SUCCESS;
+
+emem:
+ lyplg_ext_compile_log(NULL, arg->ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return ret;
+}
+
+/**
+ * @brief Parse NACM extension instances.
+ *
+ * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
+ */
+static LY_ERR
+nacm_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ struct lysp_node *parent = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* check that the extension is instantiated at an allowed place - data node */
+ if (!(ext->parent_stmt & LY_STMT_NODE_MASK)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLWRN, 0, "Extension %s is allowed only in a data nodes, but it is placed in "
+ "\"%s\" statement.", ext->name, lyplg_ext_stmt2str(ext->parent_stmt));
+ return LY_ENOT;
+ }
+
+ parent = ext->parent;
+ if (!(parent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_ANYDATA |
+ LYS_CASE | LYS_RPC | LYS_ACTION | LYS_NOTIF)) || (!strcmp(strchr(ext->name, ':') + 1, "default-deny-write") &&
+ (parent->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)))) {
+ /* note LYS_AUGMENT and LYS_USES is not in the list since they are not present in the compiled tree. Instead, libyang
+ * passes all their extensions to their children nodes */
+ lyplg_ext_parse_log(pctx, ext, LY_LLWRN, 0, "Extension %s is not allowed in %s statement.", ext->name,
+ lys_nodetype2str(parent->nodetype));
+ return LY_ENOT;
+ }
+
+ /* check for duplication */
+ LY_ARRAY_FOR(parent->exts, u) {
+ if ((&parent->exts[u] != ext) && parent->exts[u].record && (parent->exts[u].record->plugin.id == ext->record->plugin.id)) {
+ /* duplication of a NACM extension on a single node
+ * We check for all NACM plugins since we want to catch even the situation that there is default-deny-all
+ * AND default-deny-write */
+ if (parent->exts[u].name == ext->name) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is instantiated multiple times.", ext->name);
+ } else {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID,
+ "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.");
+ }
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile NACM extension instances.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
+ */
+static LY_ERR
+nacm_compile(struct lysc_ctx *UNUSED(cctx), const struct lysp_ext_instance *UNUSED(extp), struct lysc_ext_instance *ext)
+{
+ struct nacm_dfs_arg dfs_arg;
+
+ static const uint8_t nacm_deny_all = 1;
+ static const uint8_t nacm_deny_write = 2;
+
+ /* store the NACM flag */
+ if (!strcmp(ext->def->name, "default-deny-write")) {
+ ext->compiled = (void *)&nacm_deny_write;
+ } else if (!strcmp(ext->def->name, "default-deny-all")) {
+ ext->compiled = (void *)&nacm_deny_all;
+ } else {
+ return LY_EINT;
+ }
+
+ /* inherit the extension instance to all the children nodes */
+ dfs_arg.ext = ext;
+ dfs_arg.parent = ext->parent;
+ return lysc_tree_dfs_full(ext->parent, nacm_inherit_clb, &dfs_arg);
+}
+
+/**
+ * @brief Plugin descriptions for the NACM's default-deny-write and default-deny-all extensions
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_EXTENSIONS = {
+ */
+const struct lyplg_ext_record plugins_nacm[] = {
+ {
+ .module = "ietf-netconf-acm",
+ .revision = "2012-02-22",
+ .name = "default-deny-write",
+
+ .plugin.id = "ly2 NACM v1",
+ .plugin.parse = nacm_parse,
+ .plugin.compile = nacm_compile,
+ .plugin.printer_info = NULL,
+ .plugin.printer_ctree = NULL,
+ .plugin.printer_ptree = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = NULL,
+ .plugin.cfree = NULL
+ }, {
+ .module = "ietf-netconf-acm",
+ .revision = "2018-02-14",
+ .name = "default-deny-write",
+
+ .plugin.id = "ly2 NACM v1",
+ .plugin.parse = nacm_parse,
+ .plugin.compile = nacm_compile,
+ .plugin.printer_info = NULL,
+ .plugin.printer_ctree = NULL,
+ .plugin.printer_ptree = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = NULL,
+ .plugin.cfree = NULL
+ }, {
+ .module = "ietf-netconf-acm",
+ .revision = "2012-02-22",
+ .name = "default-deny-all",
+
+ .plugin.id = "ly2 NACM v1",
+ .plugin.parse = nacm_parse,
+ .plugin.compile = nacm_compile,
+ .plugin.printer_info = NULL,
+ .plugin.printer_ctree = NULL,
+ .plugin.printer_ptree = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = NULL,
+ .plugin.cfree = NULL
+ }, {
+ .module = "ietf-netconf-acm",
+ .revision = "2018-02-14",
+ .name = "default-deny-all",
+
+ .plugin.id = "ly2 NACM v1",
+ .plugin.parse = nacm_parse,
+ .plugin.compile = nacm_compile,
+ .plugin.printer_info = NULL,
+ .plugin.printer_ctree = NULL,
+ .plugin.printer_ptree = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = NULL,
+ .plugin.cfree = NULL
+ },
+ {0} /* terminating zeroed item */
+};
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
new file mode 100644
index 0000000..9800760
--- /dev/null
+++ b/src/plugins_exts/schema_mount.c
@@ -0,0 +1,1332 @@
+/**
+ * @file schema_mount.c
+ * @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz>
+ * @brief libyang extension plugin - Schema Mount (RFC 8528)
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "dict.h"
+#include "libyang.h"
+#include "log.h"
+#include "parser_data.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Internal schema mount data structure for holding all the contexts of parsed data.
+ */
+struct lyplg_ext_sm {
+ pthread_mutex_t lock; /**< lock for accessing this shared structure */
+
+ struct lyplg_ext_sm_shared {
+ struct {
+ struct ly_ctx *ctx; /**< context shared between all data of this mount point */
+ const char *mount_point; /**< mount point name */
+ const char *content_id; /**< yang-library content-id (alternatively module-set-id),
+ stored in the dictionary of the ext instance context */
+ } *schemas; /**< array of shared schema schemas */
+ uint32_t schema_count; /**< length of schemas array */
+ uint32_t ref_count; /**< number of references to this structure (mount-points with the same name
+ in the module) */
+ } *shared; /**< shared schema mount points */
+
+ struct lyplg_ext_sm_inln {
+ struct {
+ struct ly_ctx *ctx; /**< context created for inline schema data, may be reused if possible */
+ } *schemas; /**< array of inline schemas */
+ uint32_t schema_count; /**< length of schemas array */
+ } inln; /**< inline mount points */
+};
+
+struct sprinter_tree_priv {
+ struct ly_ctx *ext_ctx;
+ struct ly_set *refs;
+};
+
+#define EXT_LOGERR_MEM_RET(cctx, ext) \
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \
+ return LY_EMEM
+
+#define EXT_LOGERR_MEM_GOTO(cctx, ext, rc, label) \
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \
+ rc = LY_EMEM; \
+ goto label
+
+#define EXT_LOGERR_INT_RET(cctx, ext) \
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EINT, "Internal error (%s:%d).", __FILE__, __LINE__); \
+ return LY_EINT
+
+/**
+ * @brief Check if given mount point is unique among its siblings
+ *
+ * @param[in] pctx Parse context.
+ * @param[in] ext Parsed extension instance.
+ * @return LY_SUCCESS if is unique;
+ * @return LY_EINVAL otherwise.
+ */
+static LY_ERR
+schema_mount_parse_unique_mp(struct lysp_ctx *pctx, const struct lysp_ext_instance *ext)
+{
+ struct lysp_ext_instance *exts;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node *parent;
+
+ /* check if it is the only instance of the mount-point among its siblings */
+ parent = ext->parent;
+ exts = parent->exts;
+ LY_ARRAY_FOR(exts, u) {
+ if (&exts[u] == ext) {
+ continue;
+ }
+
+ if (!strcmp(exts[u].name, ext->name)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Multiple extension \"%s\" instances.", ext->name);
+ return LY_EINVAL;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Schema mount parse.
+ * Checks if it can be a valid extension instance for yang schema mount.
+ *
+ * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
+ */
+static LY_ERR
+schema_mount_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ /* check YANG version 1.1 */
+ if (lyplg_ext_parse_get_cur_pmod(pctx)->version != LYS_VERSION_1_1) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance not allowed in YANG version 1 module.",
+ ext->name);
+ return LY_EINVAL;
+ }
+
+ /* check parent nodetype */
+ if ((ext->parent_stmt != LY_STMT_CONTAINER) && (ext->parent_stmt != LY_STMT_LIST)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance allowed only in container or list statement.",
+ ext->name);
+ return LY_EINVAL;
+ }
+
+ /* check uniqueness */
+ if (schema_mount_parse_unique_mp(pctx, ext)) {
+ return LY_EINVAL;
+ }
+
+ /* nothing to actually parse */
+ return LY_SUCCESS;
+}
+
+struct lyplg_ext_sm_shared_cb_data {
+ const struct lysc_ext_instance *ext;
+ struct lyplg_ext_sm_shared *sm_shared;
+};
+
+static LY_ERR
+schema_mount_compile_mod_dfs_cb(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue))
+{
+ struct lyplg_ext_sm_shared_cb_data *cb_data = data;
+ struct lyplg_ext_sm *sm_data;
+ struct lysc_ext_instance *exts;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (node == cb_data->ext->parent) {
+ /* parent of the current compiled extension, skip */
+ return LY_SUCCESS;
+ }
+
+ /* find the same mount point */
+ exts = node->exts;
+ LY_ARRAY_FOR(exts, u) {
+ if (!strcmp(exts[u].def->module->name, "ietf-yang-schema-mount") && !strcmp(exts[u].def->name, "mount-point") &&
+ (exts[u].argument == cb_data->ext->argument)) {
+ /* same mount point, break the DFS search */
+ sm_data = exts[u].compiled;
+ cb_data->sm_shared = sm_data->shared;
+ return LY_EEXIST;
+ }
+ }
+
+ /* not found, continue search */
+ return LY_SUCCESS;
+}
+
+static struct lyplg_ext_sm_shared *
+schema_mount_compile_find_shared(const struct lys_module *mod, const struct lysc_ext_instance *ext)
+{
+ struct lyplg_ext_sm_shared_cb_data cb_data;
+ LY_ERR r;
+
+ /* prepare cb_data */
+ cb_data.ext = ext;
+ cb_data.sm_shared = NULL;
+
+ /* try to find the same mount point */
+ r = lysc_module_dfs_full(mod, schema_mount_compile_mod_dfs_cb, &cb_data);
+ (void)r;
+ assert((!r && !cb_data.sm_shared) || ((r == LY_EEXIST) && cb_data.sm_shared));
+
+ return cb_data.sm_shared;
+}
+
+/**
+ * @brief Schema mount compile.
+ * Checks if it can be a valid extension instance for yang schema mount.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
+ */
+static LY_ERR
+schema_mount_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *UNUSED(extp), struct lysc_ext_instance *ext)
+{
+ const struct lysc_node *node;
+ struct lyplg_ext_sm *sm_data;
+
+ /* init internal data */
+ sm_data = calloc(1, sizeof *sm_data);
+ if (!sm_data) {
+ EXT_LOGERR_MEM_RET(cctx, ext);
+ }
+ pthread_mutex_init(&sm_data->lock, NULL);
+ ext->compiled = sm_data;
+
+ /* find the owner module */
+ node = ext->parent;
+ while (node->parent) {
+ node = node->parent;
+ }
+
+ /* reuse/init shared schema */
+ sm_data->shared = schema_mount_compile_find_shared(node->module, ext);
+ if (sm_data->shared) {
+ ++sm_data->shared->ref_count;
+ } else {
+ sm_data->shared = calloc(1, sizeof *sm_data->shared);
+ if (!sm_data->shared) {
+ free(sm_data);
+ EXT_LOGERR_MEM_RET(cctx, ext);
+ }
+ sm_data->shared->ref_count = 1;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Learn details about the current mount point.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] ext_data Extension data retrieved by the callback.
+ * @param[out] config Whether the whole schema should keep its config or be set to false.
+ * @param[out] shared Optional flag whether the schema is shared or inline.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_get_smount(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool *config,
+ ly_bool *shared)
+{
+ struct lyd_node *mpoint, *node;
+ char *path = NULL;
+ LY_ERR r;
+
+ /* find the mount point */
+ if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']", ext->module->name,
+ ext->argument) == -1) {
+ EXT_LOGERR_MEM_RET(NULL, ext);
+ }
+ r = ext_data ? lyd_find_path(ext_data, path, 0, &mpoint) : LY_ENOTFOUND;
+ free(path);
+ if (r) {
+ /* missing mount-point, cannot be data for this extension (https://datatracker.ietf.org/doc/html/rfc8528#page-10) */
+ return LY_ENOT;
+ }
+
+ /* check config */
+ *config = 1;
+ if (!lyd_find_path(mpoint, "config", 0, &node) && !strcmp(lyd_get_value(node), "false")) {
+ *config = 0;
+ }
+ assert((ext->parent_stmt == LY_STMT_CONTAINER) || (ext->parent_stmt == LY_STMT_LIST));
+ if (((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_R) {
+ *config = 0;
+ }
+
+ if (shared) {
+ /* check schema-ref */
+ if (lyd_find_path(mpoint, "shared-schema", 0, NULL)) {
+ if (lyd_find_path(mpoint, "inline", 0, NULL)) {
+ EXT_LOGERR_INT_RET(NULL, ext);
+ }
+ *shared = 0;
+ } else {
+ *shared = 1;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create schema (context) based on retrieved extension data.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] ext_data Extension data retrieved by the callback.
+ * @param[in] config Whether the whole schema should keep its config or be set to false.
+ * @param[out] ext_ctx Schema to use for parsing the data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_create_ctx(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
+ struct ly_ctx **ext_ctx)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const char * const *searchdirs;
+ char *sdirs = NULL;
+ const struct lys_module *mod;
+ struct lysc_node *root, *node;
+ uint32_t i, idx = 0;
+
+ /* get searchdirs from the current context */
+ searchdirs = ly_ctx_get_searchdirs(ext->module->ctx);
+
+ if (searchdirs) {
+ /* append them all into a single string */
+ for (i = 0; searchdirs[i]; ++i) {
+ if ((rc = ly_strcat(&sdirs, "%s" PATH_SEPARATOR, searchdirs[i]))) {
+ goto cleanup;
+ }
+ }
+ }
+
+ /* create the context based on the data */
+ if ((rc = ly_ctx_new_yldata(sdirs, ext_data, ly_ctx_get_options(ext->module->ctx), ext_ctx))) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Failed to create context for the schema-mount data.");
+ goto cleanup;
+ }
+
+ if (!config) {
+ /* manually change the config of all schema nodes in all the modules */
+ while ((mod = ly_ctx_get_module_iter(*ext_ctx, &idx))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ LY_LIST_FOR(mod->compiled->data, root) {
+ LYSC_TREE_DFS_BEGIN(root, node) {
+ node->flags &= ~LYS_CONFIG_W;
+ node->flags |= LYS_CONFIG_R;
+
+ LYSC_TREE_DFS_END(root, node);
+ }
+ }
+ }
+ }
+
+cleanup:
+ free(sdirs);
+ return rc;
+}
+
+/**
+ * @brief Get ietf-yang-library context-id from its data.
+ *
+ * @param[in] ext Compiled extension instance for logging.
+ * @param[in] ext_data Extension data retrieved by the callback with the yang-library data.
+ * @param[out] content_id Content ID in @p ext_data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_get_content_id(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const char **content_id)
+{
+ struct lyd_node *node = NULL;
+
+ *content_id = NULL;
+
+ /* get yang-library content-id or module-set-id */
+ if (ext_data) {
+ lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, &node);
+ if (!node) {
+ lyd_find_path(ext_data, "/ietf-yang-library:modules-state/module-set-id", 0, &node);
+ }
+ if (node) {
+ *content_id = lyd_get_value(node);
+ }
+ }
+
+ if (!*content_id) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID,
+ "Missing \"content-id\" or \"module-set-id\" in ietf-yang-library data.");
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get schema (context) for a shared-schema mount point.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] ext_data Extension data retrieved by the callback.
+ * @param[in] config Whether the whole schema should keep its config or be set to false.
+ * @param[out] ext_ctx Schema to use for parsing the data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_get_ctx_shared(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
+ const struct ly_ctx **ext_ctx)
+{
+ struct lyplg_ext_sm *sm_data = ext->compiled;
+ LY_ERR rc = LY_SUCCESS, r;
+ struct ly_ctx *new_ctx = NULL;
+ uint32_t i;
+ const char *content_id;
+ void *mem;
+
+ assert(sm_data && sm_data->shared);
+
+ /* get yang-library content-id or module-set-id */
+ if ((r = schema_mount_get_content_id(ext, ext_data, &content_id))) {
+ return r;
+ }
+
+ /* LOCK */
+ if ((r = pthread_mutex_lock(&sm_data->lock))) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
+ return LY_ESYS;
+ }
+
+ /* try to find this mount point */
+ for (i = 0; i < sm_data->shared->schema_count; ++i) {
+ if (ext->argument == sm_data->shared->schemas[i].mount_point) {
+ break;
+ }
+ }
+
+ if (i < sm_data->shared->schema_count) {
+ /* schema exists already */
+ if (strcmp(content_id, sm_data->shared->schemas[i].content_id)) {
+ lyplg_ext_compile_log_path("/ietf-yang-library:yang-library/content-id", ext, LY_LLERR, LY_EVALID,
+ "Shared-schema yang-library content-id \"%s\" differs from \"%s\" used previously.",
+ content_id, sm_data->shared->schemas[i].content_id);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ } else {
+ /* no schema found, create it */
+ if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
+ rc = r;
+ goto cleanup;
+ }
+
+ /* new entry */
+ mem = realloc(sm_data->shared->schemas, (i + 1) * sizeof *sm_data->shared->schemas);
+ if (!mem) {
+ ly_ctx_destroy(new_ctx);
+ EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup);
+ }
+ sm_data->shared->schemas = mem;
+ ++sm_data->shared->schema_count;
+
+ /* fill entry */
+ sm_data->shared->schemas[i].ctx = new_ctx;
+ sm_data->shared->schemas[i].mount_point = ext->argument;
+ lydict_insert(ext->module->ctx, content_id, 0, &sm_data->shared->schemas[i].content_id);
+ }
+
+ /* use the context */
+ *ext_ctx = sm_data->shared->schemas[i].ctx;
+
+cleanup:
+ /* UNLOCK */
+ pthread_mutex_unlock(&sm_data->lock);
+
+ return rc;
+}
+
+/**
+ * @brief Check whether ietf-yang-library data describe an existing context meaning whether it includes
+ * at least exactly all the mentioned modules.
+ *
+ * @param[in] ext Compiled extension instance for logging.
+ * @param[in] ext_data Extension data retrieved by the callback with the yang-library data.
+ * @param[in] ctx Context to consider.
+ * @return LY_SUCCESS if the context matches.
+ * @return LY_ENOT if the context differs.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+schema_mount_ctx_match(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const struct ly_ctx *ctx)
+{
+ struct ly_set *impl_mods = NULL, *imp_mods = NULL;
+ struct lyd_node *node;
+ const struct lys_module *mod;
+ const char *name, *revision;
+ LY_ERR rc = LY_ENOT, r;
+ uint32_t i;
+
+ /* collect all the implemented and imported modules, we do not really care about content-id */
+ if (!lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, NULL)) {
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/module", &impl_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/import-only-module", &imp_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ } else {
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='implement']", &impl_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='import']", &imp_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ }
+
+ if (!impl_mods->count) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID, "No implemented modules included in ietf-yang-library data.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* check all the implemented modules */
+ for (i = 0; i < impl_mods->count; ++i) {
+ lyd_find_path(impl_mods->dnodes[i], "name", 0, &node);
+ name = lyd_get_value(node);
+
+ lyd_find_path(impl_mods->dnodes[i], "revision", 0, &node);
+ if (node && strlen(lyd_get_value(node))) {
+ revision = lyd_get_value(node);
+ } else {
+ revision = NULL;
+ }
+
+ if (!(mod = ly_ctx_get_module(ctx, name, revision)) || !mod->implemented) {
+ /* unsuitable module */
+ goto cleanup;
+ }
+ }
+
+ /* check all the imported modules */
+ for (i = 0; i < imp_mods->count; ++i) {
+ lyd_find_path(imp_mods->dnodes[i], "name", 0, &node);
+ name = lyd_get_value(node);
+
+ lyd_find_path(imp_mods->dnodes[i], "revision", 0, &node);
+ if (node && strlen(lyd_get_value(node))) {
+ revision = lyd_get_value(node);
+ } else {
+ revision = NULL;
+ }
+
+ if (!ly_ctx_get_module(ctx, name, revision)) {
+ /* unsuitable module */
+ goto cleanup;
+ }
+ }
+
+ /* context matches and can be reused */
+ rc = LY_SUCCESS;
+
+cleanup:
+ ly_set_free(impl_mods, NULL);
+ ly_set_free(imp_mods, NULL);
+ return rc;
+}
+
+/**
+ * @brief Get schema (context) for an inline mount point.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] ext_data Extension data retrieved by the callback.
+ * @param[in] config Whether the whole schema should keep its config or be set to false.
+ * @param[out] ext_ctx Schema to use for parsing the data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_get_ctx_inline(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
+ const struct ly_ctx **ext_ctx)
+{
+ struct lyplg_ext_sm *sm_data = ext->compiled;
+ struct ly_ctx *new_ctx = NULL;
+ uint32_t i;
+ void *mem;
+ LY_ERR rc = LY_SUCCESS, r;
+
+ assert(sm_data && sm_data->shared);
+
+ /* LOCK */
+ if ((r = pthread_mutex_lock(&sm_data->lock))) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
+ return LY_ESYS;
+ }
+
+ /* try to find a context we can reuse */
+ for (i = 0; i < sm_data->inln.schema_count; ++i) {
+ r = schema_mount_ctx_match(ext, ext_data, sm_data->inln.schemas[i].ctx);
+ if (!r) {
+ /* match */
+ *ext_ctx = sm_data->inln.schemas[i].ctx;
+ goto cleanup;
+ } else if (r != LY_ENOT) {
+ /* error */
+ rc = r;
+ goto cleanup;
+ }
+ }
+
+ /* new schema required, create context */
+ if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
+ rc = r;
+ goto cleanup;
+ }
+
+ /* new entry */
+ mem = realloc(sm_data->inln.schemas, (i + 1) * sizeof *sm_data->inln.schemas);
+ if (!mem) {
+ ly_ctx_destroy(new_ctx);
+ EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup);
+ }
+ sm_data->inln.schemas = mem;
+ ++sm_data->inln.schema_count;
+
+ /* fill entry */
+ sm_data->inln.schemas[i].ctx = new_ctx;
+
+ /* use the context */
+ *ext_ctx = sm_data->inln.schemas[i].ctx;
+
+cleanup:
+ /* UNLOCK */
+ pthread_mutex_unlock(&sm_data->lock);
+
+ return rc;
+}
+
+/**
+ * @brief Get schema (context) for a mount point.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[out] ext_ctx Schema to use for parsing the data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_get_ctx(struct lysc_ext_instance *ext, const struct ly_ctx **ext_ctx)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lyd_node *iter, *ext_data = NULL;
+ ly_bool ext_data_free = 0, config, shared;
+
+ *ext_ctx = NULL;
+
+ /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
+ if ((r = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
+ ret = r;
+ goto cleanup;
+ }
+
+ LY_LIST_FOR(ext_data, iter) {
+ if (iter->flags & LYD_NEW) {
+ /* must be validated for the parent-reference prefix data to be stored */
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
+
+ /* learn about this mount point */
+ if ((r = schema_mount_get_smount(ext, ext_data, &config, &shared))) {
+ ret = r;
+ goto cleanup;
+ }
+
+ /* create/get the context for parsing the data */
+ if (shared) {
+ r = schema_mount_get_ctx_shared(ext, ext_data, config, ext_ctx);
+ } else {
+ r = schema_mount_get_ctx_inline(ext, ext_data, config, ext_ctx);
+ }
+ if (r) {
+ ret = r;
+ goto cleanup;
+ }
+
+cleanup:
+ if (ext_data_free) {
+ lyd_free_all(ext_data);
+ }
+ return ret;
+}
+
+/**
+ * @brief Snode callback for schema mount.
+ * Check if data are valid for schema mount and returns their schema node.
+ */
+static LY_ERR
+schema_mount_snode(struct lysc_ext_instance *ext, const struct lyd_node *parent, const struct lysc_node *sparent,
+ const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
+ const struct lysc_node **snode)
+{
+ LY_ERR r;
+ const struct lys_module *mod;
+ const struct ly_ctx *ext_ctx = NULL;
+
+ /* get context based on ietf-yang-library data */
+ if ((r = schema_mount_get_ctx(ext, &ext_ctx))) {
+ return r;
+ }
+
+ /* get the module */
+ mod = lyplg_type_identity_module(ext_ctx, parent ? parent->schema : sparent, prefix, prefix_len, format, prefix_data);
+ if (!mod) {
+ return LY_ENOT;
+ }
+
+ /* get the top-level schema node */
+ *snode = lys_find_child(NULL, mod, name, name_len, 0, 0);
+ return *snode ? LY_SUCCESS : LY_ENOT;
+}
+
+static LY_ERR
+schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data,
+ struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *path = NULL;
+
+ /* get all parent references of this mount point */
+ if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']"
+ "/shared-schema/parent-reference", ext->module->name, ext->argument) == -1) {
+ EXT_LOGERR_MEM_GOTO(NULL, ext, ret, cleanup);
+ }
+ if ((ret = lyd_find_xpath(ext_data, path, set))) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+/**
+ * @brief Duplicate all accessible parent references for a shared-schema mount point.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] ctx_node Context node for evaluating the parent-reference XPath expressions.
+ * @param[in] ext_data Extension data retrieved by the callback.
+ * @param[in] trg_ctx Mounted data context to use for duplication.
+ * @param[out] ref_set Set of all top-level parent-ref subtrees connected to each other, may be empty.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_dup_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ctx_node,
+ const struct lyd_node *ext_data, const struct ly_ctx *trg_ctx, struct ly_set **ref_set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *path = NULL;
+ struct ly_set *set = NULL, *par_set = NULL;
+ struct lyd_node_term *term;
+ struct lyd_node *dup = NULL, *top_node, *first;
+ struct lyd_value_xpath10 *xp_val;
+ uint32_t i, j;
+
+ *ref_set = NULL;
+
+ if (!ext_data) {
+ /* we expect the same ext data as before and there must be some for data to be parsed */
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "No ext data provided.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+
+ if ((ret = schema_mount_get_parent_ref(ext, ext_data, &set))) {
+ goto cleanup;
+ }
+
+ /* prepare result set */
+ if ((ret = ly_set_new(ref_set))) {
+ goto cleanup;
+ }
+
+ first = NULL;
+ for (i = 0; i < set->count; ++i) {
+ term = set->objs[i];
+
+ /* get the referenced nodes (subtrees) */
+ LYD_VALUE_GET(&term->value, xp_val);
+ if ((ret = lyd_find_xpath4(ctx_node, ctx_node, lyxp_get_expr(xp_val->exp), xp_val->format, xp_val->prefix_data,
+ NULL, &par_set))) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Parent reference \"%s\" evaluation failed.",
+ lyxp_get_expr(xp_val->exp));
+ goto cleanup;
+ }
+
+ for (j = 0; j < par_set->count; ++j) {
+ /* duplicate with parents in the context of the mounted data */
+ if ((ret = lyd_dup_single_to_ctx(par_set->dnodes[j], trg_ctx, NULL,
+ LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_EXT, &dup))) {
+ goto cleanup;
+ }
+
+ /* go top-level */
+ while (dup->parent) {
+ dup = lyd_parent(dup);
+ }
+
+ /* check whether the top-level node exists */
+ if (first) {
+ if ((ret = lyd_find_sibling_first(first, dup, &top_node)) && (ret != LY_ENOTFOUND)) {
+ goto cleanup;
+ }
+ } else {
+ top_node = NULL;
+ }
+
+ if (top_node) {
+ /* merge */
+ ret = lyd_merge_tree(&first, dup, LYD_MERGE_DESTRUCT);
+ dup = NULL;
+ if (ret) {
+ goto cleanup;
+ }
+ } else {
+ /* insert */
+ if ((ret = lyd_insert_sibling(first, dup, &first))) {
+ goto cleanup;
+ }
+
+ /* add into the result set because a new top-level node was added */
+ if ((ret = ly_set_add(*ref_set, dup, 1, NULL))) {
+ goto cleanup;
+ }
+ dup = NULL;
+ }
+ }
+ }
+
+cleanup:
+ free(path);
+ ly_set_free(set, NULL);
+ ly_set_free(par_set, NULL);
+ lyd_free_tree(dup);
+ if (ret && *ref_set) {
+ if ((*ref_set)->count) {
+ lyd_free_siblings((*ref_set)->dnodes[0]);
+ }
+ ly_set_free(*ref_set, NULL);
+ *ref_set = NULL;
+ }
+ return ret;
+}
+
+LY_ERR
+lyplg_ext_schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, struct ly_set **refs)
+{
+ LY_ERR rc;
+ struct ly_set *pref_set = NULL;
+ struct ly_set *snode_set = NULL;
+ struct ly_set *results_set = NULL;
+ struct lyd_node *ext_data;
+ ly_bool ext_data_free;
+
+ /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
+ if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
+ return rc;
+ }
+
+ LY_CHECK_GOTO(rc = schema_mount_get_parent_ref(ext, ext_data, &pref_set), cleanup);
+ if (pref_set->count == 0) {
+ goto cleanup;
+ }
+
+ LY_CHECK_GOTO(rc = ly_set_new(&results_set), cleanup);
+
+ for (uint32_t i = 0; i < pref_set->count; ++i) {
+ struct lyd_node_term *term;
+ struct lyd_value_xpath10 *xp_val;
+ char *value;
+ struct ly_err_item *err;
+
+ term = (struct lyd_node_term *)pref_set->dnodes[i];
+ LYD_VALUE_GET(&term->value, xp_val);
+ LY_CHECK_GOTO(rc = lyplg_type_print_xpath10_value(xp_val, LY_VALUE_JSON, NULL, &value, &err), cleanup);
+ LY_CHECK_ERR_GOTO(rc = lys_find_xpath(ext->module->ctx, NULL, value, 0, &snode_set), free(value), cleanup);
+ free(value);
+ for (uint32_t sn = 0; sn < snode_set->count; sn++) {
+ LY_CHECK_GOTO(rc = ly_set_add(results_set, snode_set->snodes[sn], 0, NULL), cleanup);
+ }
+ ly_set_free(snode_set, NULL);
+ snode_set = NULL;
+ }
+
+ *refs = results_set;
+
+cleanup:
+ if (rc) {
+ ly_set_free(results_set, NULL);
+ }
+ ly_set_free(snode_set, NULL);
+ if (ext_data_free) {
+ lyd_free_all(ext_data);
+ }
+ ly_set_free(pref_set, NULL);
+
+ return rc;
+}
+
+/**
+ * @brief Validate callback for schema mount.
+ */
+static LY_ERR
+schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, const struct lyd_node *dep_tree,
+ enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t temp_lo = LY_LOSTORE_LAST, i;
+ struct ly_err_item *err;
+ struct lyd_node *iter, *ext_data = NULL, *ref_first = NULL, *orig_parent = lyd_parent(sibling), *op_tree;
+ struct lyd_node *ext_diff = NULL, *diff_parent = NULL;
+ ly_bool ext_data_free = 0;
+ struct ly_set *ref_set = NULL;
+
+ if (!sibling) {
+ /* some data had to be parsed for this callback to be called */
+ EXT_LOGERR_INT_RET(NULL, ext);
+ }
+
+ /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
+ if ((ret = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
+ goto cleanup;
+ }
+
+ LY_LIST_FOR(ext_data, iter) {
+ if (iter->flags & LYD_NEW) {
+ /* must be validated for the parent-reference prefix data to be stored */
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
+
+ /* duplicate the referenced parent nodes into ext context */
+ if ((ret = schema_mount_dup_parent_ref(ext, orig_parent, ext_data, LYD_CTX(sibling), &ref_set))) {
+ goto cleanup;
+ }
+
+ if (data_type != LYD_TYPE_DATA_YANG) {
+ /* remember the operation data tree, it may be moved */
+ op_tree = sibling;
+ }
+
+ /* create accessible tree, remove LYD_EXT to not call this callback recursively */
+ lyd_unlink_siblings(sibling);
+ LY_LIST_FOR(sibling, iter) {
+ iter->flags &= ~LYD_EXT;
+ }
+ if (ref_set->count) {
+ if ((ret = lyd_insert_sibling(sibling, ref_set->dnodes[0], &sibling))) {
+ goto cleanup;
+ }
+ }
+
+ /* only store messages in the context, log as an extension */
+ ly_temp_log_options(&temp_lo);
+
+ if (data_type == LYD_TYPE_DATA_YANG) {
+ /* validate all the modules with data */
+ ret = lyd_validate_all(&sibling, NULL, val_opts | LYD_VALIDATE_PRESENT, diff ? &ext_diff : NULL);
+ } else {
+ /* validate the operation */
+ ret = lyd_validate_op(op_tree, dep_tree, data_type, diff ? &ext_diff : NULL);
+ }
+
+ /* restore logging */
+ ly_temp_log_options(NULL);
+
+ /* restore sibling tree */
+ for (i = 0; i < ref_set->count; ++i) {
+ if (ref_set->dnodes[i] == sibling) {
+ sibling = sibling->next;
+ }
+ lyd_free_tree(ref_set->dnodes[i]);
+ }
+ LY_LIST_FOR(sibling, iter) {
+ iter->flags |= LYD_EXT;
+ }
+ lyplg_ext_insert(orig_parent, sibling);
+
+ if (ret) {
+ /* log the error in the original context */
+ err = ly_err_first(LYD_CTX(sibling));
+ if (!err) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Unknown validation error (err code %d).", ret);
+ } else {
+ lyplg_ext_compile_log_path(err->path, ext, LY_LLERR, err->no, "%s", err->msg);
+ }
+ goto cleanup;
+ }
+
+ /* create proper diff */
+ if (diff && ext_diff) {
+ /* diff nodes from an extension instance */
+ LY_LIST_FOR(ext_diff, iter) {
+ iter->flags |= LYD_EXT;
+ }
+
+ /* create the parent and insert the diff */
+ if ((ret = lyd_dup_single(lyd_parent(sibling), NULL, LYD_DUP_WITH_PARENTS | LYD_DUP_NO_META, &diff_parent))) {
+ goto cleanup;
+ }
+ if ((ret = lyplg_ext_insert(diff_parent, ext_diff))) {
+ goto cleanup;
+ }
+ ext_diff = NULL;
+
+ /* go top-level and set the operation */
+ while (lyd_parent(diff_parent)) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ if ((ret = lyd_new_meta(LYD_CTX(diff_parent), diff_parent, NULL, "yang:operation", "none", 0, NULL))) {
+ goto cleanup;
+ }
+
+ /* finally merge into the global diff */
+ if ((ret = lyd_diff_merge_all(diff, diff_parent, LYD_DIFF_MERGE_DEFAULTS))) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ ly_set_free(ref_set, NULL);
+ lyd_free_siblings(ref_first);
+ lyd_free_tree(ext_diff);
+ lyd_free_all(diff_parent);
+ if (ext_data_free) {
+ lyd_free_all(ext_data);
+ }
+ return ret;
+}
+
+/**
+ * @brief Schema mount compile free.
+ *
+ * Implementation of ::lyplg_ext_compile_free_clb callback set as ::lyext_plugin::cfree.
+ */
+static void
+schema_mount_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+ struct lyplg_ext_sm *sm_data = ext->compiled;
+ uint32_t i;
+
+ if (!sm_data) {
+ return;
+ }
+
+ if (!--sm_data->shared->ref_count) {
+ for (i = 0; i < sm_data->shared->schema_count; ++i) {
+ ly_ctx_destroy(sm_data->shared->schemas[i].ctx);
+ lydict_remove(ctx, sm_data->shared->schemas[i].content_id);
+ }
+ free(sm_data->shared->schemas);
+ free(sm_data->shared);
+ }
+
+ for (i = 0; i < sm_data->inln.schema_count; ++i) {
+ ly_ctx_destroy(sm_data->inln.schemas[i].ctx);
+ }
+ free(sm_data->inln.schemas);
+
+ pthread_mutex_destroy(&sm_data->lock);
+ free(sm_data);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx)
+{
+ struct lyd_node *ext_data = NULL;
+ ly_bool ext_data_free = 0, config;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (!ext->module->ctx->ext_clb) {
+ return LY_EINVAL;
+ }
+
+ if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
+ return LY_EINVAL;
+ }
+
+ /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
+ if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
+ return rc;
+ }
+
+ /* learn about this mount point */
+ if ((rc = schema_mount_get_smount(ext, ext_data, &config, NULL))) {
+ goto cleanup;
+ }
+
+ /* create the context */
+ rc = schema_mount_create_ctx(ext, ext_data, config, ctx);
+
+cleanup:
+ if (ext_data_free) {
+ lyd_free_all(ext_data);
+ }
+ return rc;
+}
+
+static void
+schema_mount_spriter_tree_free(void *priv)
+{
+ struct sprinter_tree_priv *st_priv;
+
+ st_priv = priv;
+ ly_set_free(st_priv->refs, NULL);
+ ly_ctx_destroy(st_priv->ext_ctx);
+ free(st_priv);
+}
+
+static LY_ERR
+schema_mount_sprinter_tree_cnode_override_mounted(const struct lysc_node *node, const void *UNUSED(plugin_priv),
+ ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts)
+{
+ if (!node->parent) {
+ *add_opts = "/";
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+schema_mount_sprinter_tree_pnode_override_mounted(const struct lysp_node *node, const void *UNUSED(plugin_priv),
+ ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts)
+{
+ if (!node->parent) {
+ *add_opts = "/";
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+schema_mount_sprinter_tree_node_override_parent_refs(const struct lysc_node *node, const void *plugin_priv,
+ ly_bool *skip, const char **UNUSED(flags), const char **add_opts)
+{
+ uint32_t i;
+ const struct ly_set *refs;
+ const struct lysc_module *mod;
+ struct lysc_node *ref, *iter;
+
+ refs = ((struct sprinter_tree_priv *)plugin_priv)->refs;
+ mod = node->module->compiled;
+
+ /* Assume the @p node will be skipped. */
+ *skip = 1;
+ for (i = 0; (i < refs->count) && *skip; i++) {
+ ref = refs->snodes[i];
+ if (ref->module->compiled != mod) {
+ /* parent-reference points to different module */
+ continue;
+ }
+
+ for (iter = ref; iter; iter = iter->parent) {
+ if (iter == node) {
+ /* @p node is not skipped because it is parent-rererence node or his parent */
+ *skip = 0;
+ break;
+ }
+ }
+ }
+
+ if (!*skip && !node->parent) {
+ /* top-node has additional opts */
+ *add_opts = "@";
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Schema mount schema parsed tree printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_ptree_clb callback set as lyext_plugin::printer_ptree.
+ */
+static LY_ERR
+schema_mount_sprinter_ptree(struct lysp_ext_instance *UNUSED(ext), const struct lyspr_tree_ctx *ctx,
+ const char **flags, const char **UNUSED(add_opts))
+{
+ if (!ctx) {
+ *flags = "mp";
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Schema mount schema compiled tree printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_ctree_clb callback set as lyext_plugin::printer_ctree.
+ */
+static LY_ERR
+schema_mount_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **flags, const char **UNUSED(add_opts))
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_ctx *ext_ctx = NULL;
+ const struct lys_module *mod;
+ struct ly_set *refs = NULL;
+ struct lysc_node *tree1, *tree2;
+ uint32_t i, j;
+ ly_bool from_parent_ref, is_first;
+ struct sprinter_tree_priv *st_priv;
+
+ if (!ctx) {
+ *flags = "mp";
+ return LY_SUCCESS;
+ }
+
+ if (lyplg_ext_schema_mount_create_context(ext, &ext_ctx)) {
+ /* Void mount point */
+ return LY_SUCCESS;
+ }
+
+ rc = lyplg_ext_schema_mount_get_parent_ref(ext, &refs);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* build new list of modules to print. This list will omit internal
+ * modules, modules with no nodes (e.g., iana-if-types) and modules
+ * that were loaded as the result of a parent-reference.
+ */
+ i = ly_ctx_internal_modules_count(ext_ctx);
+ while ((mod = ly_ctx_get_module_iter(ext_ctx, &i))) {
+ from_parent_ref = 0;
+
+ for (j = 0; refs && j < refs->count; j++) {
+ if (!strcmp(mod->ns, refs->snodes[j]->module->ns)) {
+ from_parent_ref = 1;
+ break;
+ }
+ }
+ if (from_parent_ref) {
+ /* Modules loaded as the result of a parent-reference are added later. */
+ continue;
+ }
+
+ /* Add data nodes, rpcs and notifications. */
+ if ((ext_ctx->flags & LY_CTX_SET_PRIV_PARSED) && mod->compiled) {
+ /* For compiled module. */
+ rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, mod->compiled->data,
+ schema_mount_sprinter_tree_cnode_override_mounted);
+ LY_CHECK_GOTO(rc, cleanup);
+ if (mod->compiled->rpcs) {
+ rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->rpcs->node,
+ schema_mount_sprinter_tree_cnode_override_mounted);
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ if (mod->compiled->notifs) {
+ rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->notifs->node,
+ schema_mount_sprinter_tree_cnode_override_mounted);
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ } else {
+ /* For parsed module. */
+ rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, mod->parsed->data,
+ schema_mount_sprinter_tree_pnode_override_mounted);
+ LY_CHECK_GOTO(rc, cleanup);
+ if (mod->parsed->rpcs) {
+ rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->rpcs->node,
+ schema_mount_sprinter_tree_pnode_override_mounted);
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ if (mod->parsed->notifs) {
+ rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->notifs->node,
+ schema_mount_sprinter_tree_pnode_override_mounted);
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+ /* Add modules loaded as the result of a parent-reference. */
+ for (i = 0; refs && (i < refs->count); i++) {
+ tree1 = refs->snodes[i]->module->compiled->data;
+
+ /* Add data nodes from the module only once. */
+ is_first = 1;
+ for (j = 0; j < i; j++) {
+ tree2 = refs->snodes[j]->module->compiled->data;
+ if (tree1 == tree2) {
+ is_first = 0;
+ break;
+ }
+ }
+ if (is_first) {
+ /* Add all data nodes but unavailable nodes are skipped in the callback. */
+ rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, tree1, schema_mount_sprinter_tree_node_override_parent_refs);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+ /* add private plugin data */
+ st_priv = calloc(1, sizeof(*st_priv));
+ LY_CHECK_ERR_GOTO(!st_priv, rc = LY_EMEM, cleanup);
+ st_priv->ext_ctx = ext_ctx;
+ st_priv->refs = refs;
+ rc = lyplg_ext_sprinter_tree_set_priv(ctx, st_priv, schema_mount_spriter_tree_free);
+
+cleanup:
+ if (rc) {
+ ly_set_free(refs, NULL);
+ ly_ctx_destroy(ext_ctx);
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Plugin descriptions for the Yang Schema Mount extension.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_EXTENSIONS = {
+ */
+const struct lyplg_ext_record plugins_schema_mount[] = {
+ {
+ .module = "ietf-yang-schema-mount",
+ .revision = "2019-01-14",
+ .name = "mount-point",
+
+ .plugin.id = "ly2 schema mount v1",
+ .plugin.parse = schema_mount_parse,
+ .plugin.compile = schema_mount_compile,
+ .plugin.printer_info = NULL,
+ .plugin.printer_ctree = schema_mount_sprinter_ctree,
+ .plugin.printer_ptree = schema_mount_sprinter_ptree,
+ .plugin.node = NULL,
+ .plugin.snode = schema_mount_snode,
+ .plugin.validate = schema_mount_validate,
+ .plugin.pfree = NULL,
+ .plugin.cfree = schema_mount_cfree
+ },
+ {0} /* terminating zeroed item */
+};
diff --git a/src/plugins_exts/structure.c b/src/plugins_exts/structure.c
new file mode 100644
index 0000000..ee7a52e
--- /dev/null
+++ b/src/plugins_exts/structure.c
@@ -0,0 +1,558 @@
+/**
+ * @file structure.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang extension plugin - structure (RFC 8791)
+ *
+ * Copyright (c) 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat.h"
+#include "libyang.h"
+#include "plugins_exts.h"
+
+struct lysp_ext_instance_structure {
+ struct lysp_restr *musts;
+ uint16_t flags;
+ const char *dsc;
+ const char *ref;
+ struct lysp_tpdf *typedefs;
+ struct lysp_node_grp *groupings;
+ struct lysp_node *child;
+};
+
+struct lysc_ext_instance_structure {
+ struct lysc_must *musts;
+ uint16_t flags;
+ const char *dsc;
+ const char *ref;
+ struct lysc_node *child;
+};
+
+struct lysp_ext_instance_augment_structure {
+ uint16_t flags;
+ const char *dsc;
+ const char *ref;
+ struct lysp_node *child;
+ struct lysp_node_augment *aug;
+};
+
+/**
+ * @brief Parse structure extension instances.
+ *
+ * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
+ */
+static LY_ERR
+structure_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ LY_ERR rc;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_module *pmod;
+ struct lysp_ext_instance_structure *struct_pdata;
+
+ /* structure can appear only at the top level of a YANG module or submodule */
+ if ((ext->parent_stmt != LY_STMT_MODULE) && (ext->parent_stmt != LY_STMT_SUBMODULE)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s must not be used as a non top-level statement in \"%s\" statement.", ext->name,
+ lyplg_ext_stmt2str(ext->parent_stmt));
+ return LY_EVALID;
+ }
+
+ pmod = ext->parent;
+
+ /* check for duplication */
+ LY_ARRAY_FOR(pmod->exts, u) {
+ if ((&pmod->exts[u] != ext) && (pmod->exts[u].name == ext->name) && !strcmp(pmod->exts[u].argument, ext->argument)) {
+ /* duplication of the same structure extension in a single module */
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is instantiated multiple times.", ext->name);
+ return LY_EVALID;
+ }
+ }
+
+ /* allocate the storage */
+ struct_pdata = calloc(1, sizeof *struct_pdata);
+ if (!struct_pdata) {
+ goto emem;
+ }
+ ext->parsed = struct_pdata;
+ LY_ARRAY_CREATE_GOTO(lyplg_ext_parse_get_cur_pmod(pctx)->mod->ctx, ext->substmts, 14, rc, emem);
+
+ /* parse substatements */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_MUST;
+ ext->substmts[0].storage = &struct_pdata->musts;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_STATUS;
+ ext->substmts[1].storage = &struct_pdata->flags;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_DESCRIPTION;
+ ext->substmts[2].storage = &struct_pdata->dsc;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[3].stmt = LY_STMT_REFERENCE;
+ ext->substmts[3].storage = &struct_pdata->ref;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[4].stmt = LY_STMT_TYPEDEF;
+ ext->substmts[4].storage = &struct_pdata->typedefs;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[5].stmt = LY_STMT_GROUPING;
+ ext->substmts[5].storage = &struct_pdata->groupings;
+
+ /* data-def-stmt */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[6].stmt = LY_STMT_CONTAINER;
+ ext->substmts[6].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[7].stmt = LY_STMT_LEAF;
+ ext->substmts[7].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[8].stmt = LY_STMT_LEAF_LIST;
+ ext->substmts[8].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[9].stmt = LY_STMT_LIST;
+ ext->substmts[9].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[10].stmt = LY_STMT_CHOICE;
+ ext->substmts[10].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[11].stmt = LY_STMT_ANYDATA;
+ ext->substmts[11].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[12].stmt = LY_STMT_ANYXML;
+ ext->substmts[12].storage = &struct_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[13].stmt = LY_STMT_USES;
+ ext->substmts[13].storage = &struct_pdata->child;
+
+ rc = lyplg_ext_parse_extension_instance(pctx, ext);
+ return rc;
+
+emem:
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+/**
+ * @brief Compile structure extension instances.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
+ */
+static LY_ERR
+structure_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp, struct lysc_ext_instance *ext)
+{
+ LY_ERR rc;
+ struct lysc_module *mod_c;
+ const struct lysc_node *child;
+ struct lysc_ext_instance_structure *struct_cdata;
+ uint32_t prev_options = *lyplg_ext_compile_get_options(cctx);
+
+ mod_c = ext->parent;
+
+ /* check identifier namespace with the compiled nodes */
+ LY_LIST_FOR(mod_c->data, child) {
+ if (!strcmp(child->name, ext->argument)) {
+ /* identifier collision */
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID, "Extension %s collides with a %s with the same identifier.",
+ extp->name, lys_nodetype2str(child->nodetype));
+ return LY_EVALID;
+ }
+ }
+
+ /* allocate the storage */
+ struct_cdata = calloc(1, sizeof *struct_cdata);
+ if (!struct_cdata) {
+ goto emem;
+ }
+ ext->compiled = struct_cdata;
+
+ /* compile substatements */
+ LY_ARRAY_CREATE_GOTO(cctx->ctx, ext->substmts, 14, rc, emem);
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_MUST;
+ ext->substmts[0].storage = &struct_cdata->musts;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_STATUS;
+ ext->substmts[1].storage = &struct_cdata->flags;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_DESCRIPTION;
+ ext->substmts[2].storage = &struct_cdata->dsc;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[3].stmt = LY_STMT_REFERENCE;
+ ext->substmts[3].storage = &struct_cdata->ref;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[4].stmt = LY_STMT_TYPEDEF;
+ ext->substmts[4].storage = NULL;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[5].stmt = LY_STMT_GROUPING;
+ ext->substmts[5].storage = NULL;
+
+ /* data-def-stmt */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[6].stmt = LY_STMT_CONTAINER;
+ ext->substmts[6].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[7].stmt = LY_STMT_LEAF;
+ ext->substmts[7].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[8].stmt = LY_STMT_LEAF_LIST;
+ ext->substmts[8].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[9].stmt = LY_STMT_LIST;
+ ext->substmts[9].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[10].stmt = LY_STMT_CHOICE;
+ ext->substmts[10].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[11].stmt = LY_STMT_ANYDATA;
+ ext->substmts[11].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[12].stmt = LY_STMT_ANYXML;
+ ext->substmts[12].storage = &struct_cdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[13].stmt = LY_STMT_USES;
+ ext->substmts[13].storage = &struct_cdata->child;
+
+ *lyplg_ext_compile_get_options(cctx) |= LYS_COMPILE_NO_CONFIG | LYS_COMPILE_NO_DISABLED;
+ rc = lyplg_ext_compile_extension_instance(cctx, extp, ext);
+ *lyplg_ext_compile_get_options(cctx) = prev_options;
+ if (rc) {
+ return rc;
+ }
+
+ return LY_SUCCESS;
+
+emem:
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+/**
+ * @brief Structure schema info printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_info_clb set as ::lyext_plugin::printer_info
+ */
+static LY_ERR
+structure_printer_info(struct lyspr_ctx *ctx, struct lysc_ext_instance *ext, ly_bool *flag)
+{
+ lyplg_ext_print_info_extension_instance(ctx, ext, flag);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Free parsed structure extension instance data.
+ *
+ * Implementation of ::lyplg_clb_parse_free_clb callback set as lyext_plugin::pfree.
+ */
+static void
+structure_pfree(const struct ly_ctx *ctx, struct lysp_ext_instance *ext)
+{
+ lyplg_ext_pfree_instance_substatements(ctx, ext->substmts);
+ free(ext->parsed);
+}
+
+/**
+ * @brief Free compiled structure extension instance data.
+ *
+ * Implementation of ::lyplg_clb_compile_free_clb callback set as lyext_plugin::cfree.
+ */
+static void
+structure_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+ lyplg_ext_cfree_instance_substatements(ctx, ext->substmts);
+ free(ext->compiled);
+}
+
+/**
+ * @brief Parse augment-structure extension instances.
+ *
+ * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
+ */
+static LY_ERR
+structure_aug_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ LY_ERR rc;
+ struct lysp_stmt *stmt;
+ struct lysp_ext_instance_augment_structure *aug_pdata;
+ const struct ly_ctx *ctx = lyplg_ext_parse_get_cur_pmod(pctx)->mod->ctx;
+
+ /* augment-structure can appear only at the top level of a YANG module or submodule */
+ if ((ext->parent_stmt != LY_STMT_MODULE) && (ext->parent_stmt != LY_STMT_SUBMODULE)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s must not be used as a non top-level statement in \"%s\" statement.", ext->name,
+ lyplg_ext_stmt2str(ext->parent_stmt));
+ return LY_EVALID;
+ }
+
+ /* augment-structure must define some data-def-stmt */
+ LY_LIST_FOR(ext->child, stmt) {
+ if (stmt->kw & LY_STMT_DATA_NODE_MASK) {
+ break;
+ }
+ }
+ if (!stmt) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s does not define any data-def-stmt statements.",
+ ext->name);
+ return LY_EVALID;
+ }
+
+ /* allocate the storage */
+ aug_pdata = calloc(1, sizeof *aug_pdata);
+ if (!aug_pdata) {
+ goto emem;
+ }
+ ext->parsed = aug_pdata;
+ LY_ARRAY_CREATE_GOTO(ctx, ext->substmts, 13, rc, emem);
+
+ /* parse substatements */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_STATUS;
+ ext->substmts[0].storage = &aug_pdata->flags;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_DESCRIPTION;
+ ext->substmts[1].storage = &aug_pdata->dsc;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_REFERENCE;
+ ext->substmts[2].storage = &aug_pdata->ref;
+
+ /* data-def-stmt */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[3].stmt = LY_STMT_CONTAINER;
+ ext->substmts[3].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[4].stmt = LY_STMT_LEAF;
+ ext->substmts[4].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[5].stmt = LY_STMT_LEAF_LIST;
+ ext->substmts[5].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[6].stmt = LY_STMT_LIST;
+ ext->substmts[6].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[7].stmt = LY_STMT_CHOICE;
+ ext->substmts[7].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[8].stmt = LY_STMT_ANYDATA;
+ ext->substmts[8].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[9].stmt = LY_STMT_ANYXML;
+ ext->substmts[9].storage = &aug_pdata->child;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[10].stmt = LY_STMT_USES;
+ ext->substmts[10].storage = &aug_pdata->child;
+
+ /* case */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[11].stmt = LY_STMT_CASE;
+ ext->substmts[11].storage = &aug_pdata->child;
+
+ if ((rc = lyplg_ext_parse_extension_instance(pctx, ext))) {
+ return rc;
+ }
+
+ /* add fake parsed augment node */
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[12].stmt = LY_STMT_AUGMENT;
+ ext->substmts[12].storage = &aug_pdata->aug;
+
+ aug_pdata->aug = calloc(1, sizeof *aug_pdata->aug);
+ if (!aug_pdata->aug) {
+ goto emem;
+ }
+ aug_pdata->aug->nodetype = LYS_AUGMENT;
+ aug_pdata->aug->flags = aug_pdata->flags;
+ if (lydict_insert(ctx, ext->argument, 0, &aug_pdata->aug->nodeid)) {
+ goto emem;
+ }
+ aug_pdata->aug->child = aug_pdata->child;
+ /* avoid double free */
+ aug_pdata->child = NULL;
+
+ return LY_SUCCESS;
+
+emem:
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+static LY_ERR
+structure_sprinter_pnode(const struct lysp_node *UNUSED(node), const void *UNUSED(plugin_priv),
+ ly_bool *UNUSED(skip), const char **flags, const char **UNUSED(add_opts))
+{
+ *flags = "";
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+structure_sprinter_cnode(const struct lysc_node *UNUSED(node), const void *UNUSED(plugin_priv),
+ ly_bool *UNUSED(skip), const char **flags, const char **UNUSED(add_opts))
+{
+ *flags = "";
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Structure schema compiled tree printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_ctree_clb callback set as lyext_plugin::printer_ctree.
+ */
+static LY_ERR
+structure_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **UNUSED(flags), const char **UNUSED(add_opts))
+{
+ LY_ERR rc;
+
+ rc = lyplg_ext_sprinter_ctree_add_ext_nodes(ctx, ext, structure_sprinter_cnode);
+ return rc;
+}
+
+/**
+ * @brief Structure schema parsed tree printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_ptree_clb callback set as lyext_plugin::printer_ptree.
+ */
+static LY_ERR
+structure_sprinter_ptree(struct lysp_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **UNUSED(flags), const char **UNUSED(add_opts))
+{
+ LY_ERR rc;
+
+ rc = lyplg_ext_sprinter_ptree_add_ext_nodes(ctx, ext, structure_sprinter_pnode);
+ return rc;
+}
+
+/**
+ * @brief Augment structure schema parsed tree printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_ptree_clb callback set as lyext_plugin::printer_ptree.
+ */
+static LY_ERR
+structure_aug_sprinter_ptree(struct lysp_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **UNUSED(flags), const char **UNUSED(add_opts))
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysp_node_augment **aug;
+
+ assert(ctx);
+
+ aug = ext->substmts[12].storage;
+ rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, (*aug)->child, structure_sprinter_pnode);
+
+ return rc;
+}
+
+/**
+ * @brief Augment structure schema compiled tree printer.
+ *
+ * Implementation of ::lyplg_ext_sprinter_ctree_clb callback set as lyext_plugin::printer_ctree.
+ */
+static LY_ERR
+structure_aug_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx, const char **flags,
+ const char **add_opts)
+{
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_ARRAY_COUNT_TYPE i;
+ struct lysp_ext_instance *parsed_ext;
+
+ assert(ctx);
+
+ /* find the parsed ext structure */
+ parsed_ext = ext->module->parsed->exts;
+ LY_ARRAY_FOR(parsed_ext, i) {
+ if (!strcmp(parsed_ext[i].name, "sx:augment-structure") && !strcmp(parsed_ext[i].argument, ext->argument)) {
+ break;
+ }
+ }
+ assert(i < LY_ARRAY_COUNT(parsed_ext));
+
+ /* for augments print the parsed tree */
+ rc = structure_aug_sprinter_ptree(parsed_ext, ctx, flags, add_opts);
+ return rc;
+}
+
+/**
+ * @brief Plugin descriptions for the structure extension
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_EXTENSIONS = {
+ */
+const struct lyplg_ext_record plugins_structure[] = {
+ {
+ .module = "ietf-yang-structure-ext",
+ .revision = "2020-06-17",
+ .name = "structure",
+
+ .plugin.id = "ly2 structure v1",
+ .plugin.parse = structure_parse,
+ .plugin.compile = structure_compile,
+ .plugin.printer_info = structure_printer_info,
+ .plugin.printer_ctree = structure_sprinter_ctree,
+ .plugin.printer_ptree = structure_sprinter_ptree,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = structure_pfree,
+ .plugin.cfree = structure_cfree
+ },
+ {
+ .module = "ietf-yang-structure-ext",
+ .revision = "2020-06-17",
+ .name = "augment-structure",
+
+ .plugin.id = "ly2 structure v1",
+ .plugin.parse = structure_aug_parse,
+ .plugin.compile = NULL,
+ .plugin.printer_info = NULL,
+ .plugin.printer_ctree = structure_aug_sprinter_ctree,
+ .plugin.printer_ptree = structure_aug_sprinter_ptree,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = structure_pfree,
+ .plugin.cfree = NULL
+ },
+ {0} /* terminating zeroed record */
+};
diff --git a/src/plugins_exts/yangdata.c b/src/plugins_exts/yangdata.c
new file mode 100644
index 0000000..0c8f37b
--- /dev/null
+++ b/src/plugins_exts/yangdata.c
@@ -0,0 +1,277 @@
+/**
+ * @file yangdata.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang extension plugin - yang-data (RFC 8040)
+ *
+ * Copyright (c) 2021 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat.h"
+#include "libyang.h"
+#include "plugins_exts.h"
+
+static void yangdata_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext);
+
+/**
+ * @brief Parse yang-data extension instances.
+ *
+ * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
+ */
+static LY_ERR
+yangdata_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
+{
+ LY_ERR ret;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_module *pmod;
+
+ /* yang-data can appear only at the top level of a YANG module or submodule */
+ if ((ext->parent_stmt != LY_STMT_MODULE) && (ext->parent_stmt != LY_STMT_SUBMODULE)) {
+ lyplg_ext_parse_log(pctx, ext, LY_LLWRN, 0, "Extension %s is ignored since it appears as a non top-level statement "
+ "in \"%s\" statement.", ext->name, lyplg_ext_stmt2str(ext->parent_stmt));
+ return LY_ENOT;
+ }
+
+ pmod = ext->parent;
+
+ /* check for duplication */
+ LY_ARRAY_FOR(pmod->exts, u) {
+ if ((&pmod->exts[u] != ext) && (pmod->exts[u].name == ext->name) && !strcmp(pmod->exts[u].argument, ext->argument)) {
+ /* duplication of the same yang-data extension in a single module */
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is instantiated multiple times.", ext->name);
+ return LY_EVALID;
+ }
+ }
+
+ /* parse yang-data substatements */
+ LY_ARRAY_CREATE_GOTO(lyplg_ext_parse_get_cur_pmod(pctx)->mod->ctx, ext->substmts, 3, ret, emem);
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_CONTAINER;
+ ext->substmts[0].storage = &ext->parsed;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_CHOICE;
+ ext->substmts[1].storage = &ext->parsed;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_USES;
+ ext->substmts[2].storage = &ext->parsed;
+
+ if ((ret = lyplg_ext_parse_extension_instance(pctx, ext))) {
+ return ret;
+ }
+
+ return LY_SUCCESS;
+
+emem:
+ lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+/**
+ * @brief Compile yang-data extension instances.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
+ */
+static LY_ERR
+yangdata_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp, struct lysc_ext_instance *ext)
+{
+ LY_ERR ret;
+ const struct lysc_node *child;
+ ly_bool valid = 1;
+ uint32_t prev_options = *lyplg_ext_compile_get_options(cctx);
+
+ /* compile yangg-data substatements */
+ LY_ARRAY_CREATE_GOTO(cctx->ctx, ext->substmts, 3, ret, emem);
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[0].stmt = LY_STMT_CONTAINER;
+ ext->substmts[0].storage = &ext->compiled;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[1].stmt = LY_STMT_CHOICE;
+ ext->substmts[1].storage = &ext->compiled;
+
+ LY_ARRAY_INCREMENT(ext->substmts);
+ ext->substmts[2].stmt = LY_STMT_USES;
+ ext->substmts[2].storage = &ext->compiled;
+
+ *lyplg_ext_compile_get_options(cctx) |= LYS_COMPILE_NO_CONFIG | LYS_COMPILE_NO_DISABLED;
+ ret = lyplg_ext_compile_extension_instance(cctx, extp, ext);
+ *lyplg_ext_compile_get_options(cctx) = prev_options;
+ if (ret) {
+ return ret;
+ }
+
+ /* check that we have really just a single container data definition in the top */
+ child = ext->compiled;
+ if (!child) {
+ valid = 0;
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s is instantiated without any top level data node, but exactly one container data node is expected.",
+ extp->name);
+ } else if (child->next) {
+ valid = 0;
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s is instantiated with multiple top level data nodes, but only a single container data node is allowed.",
+ extp->name);
+ } else if (child->nodetype == LYS_CHOICE) {
+ /* all the choice's case are expected to result to a single container node */
+ struct lysc_module *mod_c = ext->parent;
+ const struct lysc_node *snode = NULL;
+
+ while ((snode = lys_getnext(snode, child, mod_c, 0))) {
+ if (snode->next) {
+ valid = 0;
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s is instantiated with multiple top level data nodes (inside a single choice's case), "
+ "but only a single container data node is allowed.", extp->name);
+ break;
+ } else if (snode->nodetype != LYS_CONTAINER) {
+ valid = 0;
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s is instantiated with %s top level data node (inside a choice), "
+ "but only a single container data node is allowed.", extp->name, lys_nodetype2str(snode->nodetype));
+ break;
+ }
+ }
+ } else if (child->nodetype != LYS_CONTAINER) {
+ /* via uses */
+ valid = 0;
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID,
+ "Extension %s is instantiated with %s top level data node, but only a single container data node is allowed.",
+ extp->name, lys_nodetype2str(child->nodetype));
+ }
+
+ if (!valid) {
+ yangdata_cfree(lyplg_ext_compile_get_ctx(cctx), ext);
+ ext->compiled = ext->substmts = NULL;
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+
+emem:
+ lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
+ return LY_EMEM;
+}
+
+/**
+ * @brief INFO printer
+ *
+ * Implementation of ::lyplg_ext_sprinter_info_clb set as ::lyext_plugin::printer_info
+ */
+static LY_ERR
+yangdata_printer_info(struct lyspr_ctx *ctx, struct lysc_ext_instance *ext, ly_bool *flag)
+{
+ lyplg_ext_print_info_extension_instance(ctx, ext, flag);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Free parsed yang-data extension instance data.
+ *
+ * Implementation of ::lyplg_clb_parse_free_clb callback set as lyext_plugin::pfree.
+ */
+static void
+yangdata_pfree(const struct ly_ctx *ctx, struct lysp_ext_instance *ext)
+{
+ lyplg_ext_pfree_instance_substatements(ctx, ext->substmts);
+}
+
+/**
+ * @brief Free compiled yang-data extension instance data.
+ *
+ * Implementation of ::lyplg_clb_compile_free_clb callback set as lyext_plugin::cfree.
+ */
+static void
+yangdata_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+ lyplg_ext_cfree_instance_substatements(ctx, ext->substmts);
+}
+
+static void
+yangdata_sprinter_node(uint16_t nodetype, const char **flags)
+{
+ if (nodetype & LYS_USES) {
+ *flags = "-u";
+ } else {
+ *flags = "--";
+ }
+}
+
+static LY_ERR
+yangdata_sprinter_cnode(const struct lysc_node *node, const void *UNUSED(plugin_priv), ly_bool *UNUSED(skip),
+ const char **flags, const char **UNUSED(add_opts))
+{
+ yangdata_sprinter_node(node->nodetype, flags);
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+yangdata_sprinter_pnode(const struct lysp_node *node, const void *UNUSED(plugin_priv), ly_bool *UNUSED(skip),
+ const char **flags, const char **UNUSED(add_opts))
+{
+ yangdata_sprinter_node(node->nodetype, flags);
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+yangdata_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **UNUSED(flags), const char **UNUSED(add_opts))
+{
+ LY_ERR rc = LY_SUCCESS;
+
+ assert(ctx);
+ rc = lyplg_ext_sprinter_ctree_add_ext_nodes(ctx, ext, yangdata_sprinter_cnode);
+ return rc;
+}
+
+static LY_ERR
+yangdata_sprinter_ptree(struct lysp_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
+ const char **UNUSED(flags), const char **UNUSED(add_opts))
+{
+ LY_ERR rc = LY_SUCCESS;
+
+ assert(ctx);
+ rc = lyplg_ext_sprinter_ptree_add_ext_nodes(ctx, ext, yangdata_sprinter_pnode);
+ return rc;
+}
+
+/**
+ * @brief Plugin descriptions for the yang-data extension
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_EXTENSIONS = {
+ */
+const struct lyplg_ext_record plugins_yangdata[] = {
+ {
+ .module = "ietf-restconf",
+ .revision = "2017-01-26",
+ .name = "yang-data",
+
+ .plugin.id = "ly2 yang-data v1",
+ .plugin.parse = yangdata_parse,
+ .plugin.compile = yangdata_compile,
+ .plugin.printer_info = yangdata_printer_info,
+ .plugin.printer_ctree = yangdata_sprinter_ctree,
+ .plugin.printer_ptree = yangdata_sprinter_ptree,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = yangdata_pfree,
+ .plugin.cfree = yangdata_cfree
+ },
+ {0} /* terminating zeroed record */
+};
diff --git a/src/plugins_internal.h b/src/plugins_internal.h
new file mode 100644
index 0000000..d13db16
--- /dev/null
+++ b/src/plugins_internal.h
@@ -0,0 +1,85 @@
+/**
+ * @file plugins_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief internal functions to support extension and type plugins.
+ *
+ * Copyright (c) 2019-2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PLUGINS_INTERNAL_H_
+#define LY_PLUGINS_INTERNAL_H_
+
+#include <stdint.h>
+
+#include "plugins.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+
+#define LY_TYPE_UNKNOWN_STR "unknown" /**< text representation of ::LY_TYPE_UNKNOWN */
+#define LY_TYPE_BINARY_STR "binary" /**< text representation of ::LY_TYPE_BINARY */
+#define LY_TYPE_UINT8_STR "8bit unsigned integer" /**< text representation of ::LY_TYPE_UINT8 */
+#define LY_TYPE_UINT16_STR "16bit unsigned integer" /**< text representation of ::LY_TYPE_UINT16 */
+#define LY_TYPE_UINT32_STR "32bit unsigned integer" /**< text representation of ::LY_TYPE_UINT32 */
+#define LY_TYPE_UINT64_STR "64bit unsigned integer" /**< text representation of ::LY_TYPE_UINT64 */
+#define LY_TYPE_STRING_STR "string" /**< text representation of ::LY_TYPE_STRING */
+#define LY_TYPE_BITS_STR "bits" /**< text representation of ::LY_TYPE_BITS */
+#define LY_TYPE_BOOL_STR "boolean" /**< text representation of ::LY_TYPE_BOOL */
+#define LY_TYPE_DEC64_STR "decimal64" /**< text representation of ::LY_TYPE_DEC64 */
+#define LY_TYPE_EMPTY_STR "empty" /**< text representation of ::LY_TYPE_EMPTY */
+#define LY_TYPE_ENUM_STR "enumeration" /**< text representation of ::LY_TYPE_ENUM */
+#define LY_TYPE_IDENT_STR "identityref" /**< text representation of ::LY_TYPE_IDENT */
+#define LY_TYPE_INST_STR "instance-identifier" /**< text representation of ::LY_TYPE_INST */
+#define LY_TYPE_LEAFREF_STR "leafref" /**< text representation of ::LY_TYPE_LEAFREF */
+#define LY_TYPE_UNION_STR "union" /**< text representation of ::LY_TYPE_UNION */
+#define LY_TYPE_INT8_STR "8bit integer" /**< text representation of ::LY_TYPE_INT8 */
+#define LY_TYPE_INT16_STR "16bit integer" /**< text representation of ::LY_TYPE_INT16 */
+#define LY_TYPE_INT32_STR "32bit integer" /**< text representation of ::LY_TYPE_INT32 */
+#define LY_TYPE_INT64_STR "64bit integer" /**< text representation of ::LY_TYPE_INT64 */
+
+/**
+ * @brief Initiate libyang plugins.
+ *
+ * Covers both the types and extensions plugins.
+ *
+ * @return LY_SUCCESS in case of success
+ * @return LY_EINT in case of internal error
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LY_ERR lyplg_init(void);
+
+/**
+ * @brief Remove (unload) all the plugins currently available.
+ */
+void lyplg_clean(void);
+
+/**
+ * @brief Find a type plugin.
+ *
+ * @param[in] module Name of the module where the type is defined. Must not be NULL, in case of plugins for
+ * built-in types, the module is "".
+ * @param[in] revision Revision of the module for which the plugin is implemented. NULL is not a wildcard, it matches
+ * only the plugins with NULL revision specified.
+ * @param[in] name Name of the type which the plugin implements.
+ * @return Found type plugin, NULL if none found.
+ */
+struct lyplg_type *lyplg_type_plugin_find(const char *module, const char *revision, const char *name);
+
+/**
+ * @brief Find an extension plugin.
+ *
+ * @param[in] module Name of the module where the extension is defined.
+ * @param[in] revision Revision of the module for which the plugin is implemented. NULL is not a wildcard, it matches
+ * only the plugins with NULL revision specified.
+ * @param[in] name Name of the extension which the plugin implements.
+ * @return Found extension record, NULL if none found.
+ */
+struct lyplg_ext_record *lyplg_ext_record_find(const char *module, const char *revision, const char *name);
+
+#endif /* LY_PLUGINS_INTERNAL_H_ */
diff --git a/src/plugins_types.c b/src/plugins_types.c
new file mode 100644
index 0000000..cb4b896
--- /dev/null
+++ b/src/plugins_types.c
@@ -0,0 +1,1043 @@
+/**
+ * @file plugins_types.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Built-in types plugins and interface for user types plugins.
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "path.h"
+#include "schema_compile.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+#include "xpath.h"
+
+/**
+ * @brief Find import prefix in imports.
+ */
+static const struct lys_module *
+ly_schema_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, const void *prefix_data)
+{
+ const struct lysp_module *prefix_mod = prefix_data;
+ struct lys_module *m = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+ const char *local_prefix;
+
+ local_prefix = prefix_mod->is_submod ? ((struct lysp_submodule *)prefix_mod)->prefix : prefix_mod->mod->prefix;
+ if (!prefix_len || !ly_strncmp(local_prefix, prefix, prefix_len)) {
+ /* it is the prefix of the module itself */
+ m = prefix_mod->mod;
+ }
+
+ /* search in imports */
+ if (!m) {
+ LY_ARRAY_FOR(prefix_mod->imports, u) {
+ if (!ly_strncmp(prefix_mod->imports[u].prefix, prefix, prefix_len)) {
+ m = prefix_mod->imports[u].module;
+ break;
+ }
+ }
+ }
+
+ return m;
+}
+
+/**
+ * @brief Find resolved module for a prefix in prefix - module pairs.
+ */
+static const struct lys_module *
+ly_schema_resolved_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len,
+ const void *prefix_data)
+{
+ const struct lysc_prefix *prefixes = prefix_data;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(prefixes, u) {
+ if ((!prefixes[u].prefix && !prefix_len) || (prefixes[u].prefix && !ly_strncmp(prefixes[u].prefix, prefix, prefix_len))) {
+ return prefixes[u].mod;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find XML namespace prefix in XML namespaces, which are then mapped to modules.
+ */
+static const struct lys_module *
+ly_xml_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, const void *prefix_data)
+{
+ const struct lys_module *mod;
+ const struct lyxml_ns *ns;
+ const struct ly_set *ns_set = prefix_data;
+
+ ns = lyxml_ns_get(ns_set, prefix, prefix_len);
+ if (!ns) {
+ return NULL;
+ }
+
+ mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri);
+ if (!mod) {
+ /* for YIN extension prefix resolution */
+ mod = ly_ctx_get_module_latest_ns(ctx, ns->uri);
+ }
+ return mod;
+}
+
+/**
+ * @brief Find module name.
+ */
+static const struct lys_module *
+ly_json_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, const void *UNUSED(prefix_data))
+{
+ return ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
+}
+
+const struct lys_module *
+ly_resolve_prefix(const struct ly_ctx *ctx, const void *prefix, size_t prefix_len, LY_VALUE_FORMAT format,
+ const void *prefix_data)
+{
+ const struct lys_module *mod = NULL;
+
+ LY_CHECK_ARG_RET(ctx, prefix, prefix_len, NULL);
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ mod = ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ case LY_VALUE_SCHEMA_RESOLVED:
+ mod = ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ mod = ly_xml_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ mod = ly_json_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ }
+
+ return mod;
+}
+
+LIBYANG_API_DEF const struct lys_module *
+lyplg_type_identity_module(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *prefix,
+ size_t prefix_len, LY_VALUE_FORMAT format, const void *prefix_data)
+{
+ if (prefix_len) {
+ return ly_resolve_prefix(ctx, prefix, prefix_len, format, prefix_data);
+ } else {
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ /* use local module */
+ return ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* use local module */
+ return ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* use context node module (as specified) */
+ return ctx_node ? ctx_node->module : NULL;
+ case LY_VALUE_XML:
+ /* use the default namespace */
+ return ly_xml_resolve_prefix(ctx, NULL, 0, prefix_data);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find module in import prefixes.
+ */
+static const char *
+ly_schema_get_prefix(const struct lys_module *mod, void *prefix_data)
+{
+ const struct lysp_module *pmod = prefix_data;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (pmod->mod == mod) {
+ if (pmod->is_submod) {
+ return ((struct lysp_submodule *)pmod)->prefix;
+ } else {
+ return pmod->mod->prefix;
+ }
+ }
+
+ LY_ARRAY_FOR(pmod->imports, u) {
+ if (pmod->imports[u].module == mod) {
+ /* match */
+ return pmod->imports[u].prefix;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find prefix in prefix - module pairs.
+ */
+static const char *
+ly_schema_resolved_get_prefix(const struct lys_module *mod, void *prefix_data)
+{
+ struct lysc_prefix *prefixes = prefix_data;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(prefixes, u) {
+ if (prefixes[u].mod == mod) {
+ return prefixes[u].prefix;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Simply return module local prefix. Also, store the module in a set.
+ */
+static const char *
+ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data)
+{
+ struct ly_set *ns_list = prefix_data;
+
+ LY_CHECK_RET(ly_set_add(ns_list, (void *)mod, 0, NULL), NULL);
+ return mod->prefix;
+}
+
+/**
+ * @brief Simply return module name.
+ */
+static const char *
+ly_json_get_prefix(const struct lys_module *mod, void *UNUSED(prefix_data))
+{
+ return mod->name;
+}
+
+const char *
+ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data)
+{
+ const char *prefix = NULL;
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ prefix = ly_schema_get_prefix(mod, prefix_data);
+ break;
+ case LY_VALUE_SCHEMA_RESOLVED:
+ prefix = ly_schema_resolved_get_prefix(mod, prefix_data);
+ break;
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ prefix = ly_xml_get_prefix(mod, prefix_data);
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ prefix = ly_json_get_prefix(mod, prefix_data);
+ break;
+ }
+
+ return prefix;
+}
+
+LIBYANG_API_DEF const char *
+lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data)
+{
+ return ly_get_prefix(mod, format, prefix_data);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->_canonical == val2->_canonical) {
+ return LY_SUCCESS;
+ }
+
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_simple(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT UNUSED(format),
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = ly_strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ memset(dup, 0, sizeof *dup);
+ LY_CHECK_RET(lydict_insert(ctx, original->_canonical, 0, &dup->_canonical));
+ memcpy(dup->fixed_mem, original->fixed_mem, sizeof dup->fixed_mem);
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len,
+ int64_t *ret, struct ly_err_item **err)
+{
+ LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+
+ *err = NULL;
+
+ /* consume leading whitespaces */
+ for ( ; value_len && isspace(*value); ++value, --value_len) {}
+
+ if (!value || !value_len || !value[0]) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype);
+ }
+
+ switch (ly_parse_int(value, value_len, min, max, base, ret)) {
+ case LY_EDENIED:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype);
+ case LY_SUCCESS:
+ return LY_SUCCESS;
+ default:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid type %s value \"%.*s\".", datatype, (int)value_len, value);
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, size_t value_len, uint64_t *ret,
+ struct ly_err_item **err)
+{
+ LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+
+ *err = NULL;
+
+ /* consume leading whitespaces */
+ for ( ; value_len && isspace(*value); ++value, --value_len) {}
+
+ if (!value || !value_len || !value[0]) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype);
+ }
+
+ *err = NULL;
+ switch (ly_parse_uint(value, value_len, max, base, ret)) {
+ case LY_EDENIED:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype);
+ case LY_SUCCESS:
+ return LY_SUCCESS;
+ default:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid type %s value \"%.*s\".", datatype, (int)value_len, value);
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err)
+{
+ LY_ERR ret_val;
+ char *valcopy = NULL;
+ size_t fraction = 0, size, len = 0, trailing_zeros;
+ int64_t d;
+
+ *err = NULL;
+
+ /* consume leading whitespaces */
+ for ( ; value_len && isspace(*value); ++value, --value_len) {}
+
+ /* parse value */
+ if (!value_len) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty decimal64 value.");
+ } else if (!isdigit(value[len]) && (value[len] != '-') && (value[len] != '+')) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %zu. character of decimal64 value \"%.*s\".",
+ len + 1, (int)value_len, value);
+ }
+
+ if ((value[len] == '-') || (value[len] == '+')) {
+ ++len;
+ }
+
+ while (len < value_len && isdigit(value[len])) {
+ ++len;
+ }
+
+ trailing_zeros = 0;
+ if ((len < value_len) && ((value[len] != '.') || !isdigit(value[len + 1]))) {
+ goto decimal;
+ }
+ fraction = len;
+ ++len;
+ while (len < value_len && isdigit(value[len])) {
+ if (value[len] == '0') {
+ ++trailing_zeros;
+ } else {
+ trailing_zeros = 0;
+ }
+ ++len;
+ }
+ len = len - trailing_zeros;
+
+decimal:
+ if (fraction && (len - 1 - fraction > fraction_digits)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Value \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
+ (int)len, value, fraction_digits);
+ }
+ if (fraction) {
+ size = len + (fraction_digits - (len - 1 - fraction));
+ } else {
+ size = len + fraction_digits + 1;
+ }
+
+ if (len + trailing_zeros < value_len) {
+ /* consume trailing whitespaces to check that there is nothing after it */
+ uint64_t u;
+
+ for (u = len + trailing_zeros; u < value_len && isspace(value[u]); ++u) {}
+ if (u != value_len) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid %" PRIu64 ". character of decimal64 value \"%.*s\".", u + 1, (int)value_len, value);
+ }
+ }
+
+ /* prepare value string without decimal point to easily parse using standard functions */
+ valcopy = malloc(size * sizeof *valcopy);
+ if (!valcopy) {
+ return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
+ }
+
+ valcopy[size - 1] = '\0';
+ if (fraction) {
+ memcpy(&valcopy[0], &value[0], fraction);
+ memcpy(&valcopy[fraction], &value[fraction + 1], len - 1 - (fraction));
+ /* add trailing zero characters */
+ memset(&valcopy[len - 1], '0', fraction_digits - (len - 1 - fraction));
+ } else {
+ memcpy(&valcopy[0], &value[0], len);
+ /* add trailing zero characters */
+ memset(&valcopy[len], '0', fraction_digits);
+ }
+
+ ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1),
+ INT64_C(9223372036854775807), valcopy, size - 1, &d, err);
+ if (!ret_val && ret) {
+ *ret = d;
+ }
+ free(valcopy);
+
+ return ret_val;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err)
+{
+ int rc, match_opts;
+ LY_ARRAY_COUNT_TYPE u;
+ pcre2_match_data *match_data = NULL;
+
+ LY_CHECK_ARG_RET(NULL, str, err, LY_EINVAL);
+
+ *err = NULL;
+
+ LY_ARRAY_FOR(patterns, u) {
+ /* match_data needs to be allocated each time because of possible multi-threaded evaluation */
+ match_data = pcre2_match_data_create_from_pattern(patterns[u]->code, NULL);
+ if (!match_data) {
+ return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
+ }
+
+ match_opts = PCRE2_ANCHORED;
+#ifdef PCRE2_ENDANCHORED
+ /* PCRE2_ENDANCHORED was added in PCRE2 version 10.30 */
+ match_opts |= PCRE2_ENDANCHORED;
+#endif
+ rc = pcre2_match(patterns[u]->code, (PCRE2_SPTR)str, str_len, 0, match_opts, match_data, NULL);
+ pcre2_match_data_free(match_data);
+
+ if ((rc != PCRE2_ERROR_NOMATCH) && (rc < 0)) {
+ PCRE2_UCHAR pcre2_errmsg[LY_PCRE2_MSG_LIMIT] = {0};
+
+ pcre2_get_error_message(rc, pcre2_errmsg, LY_PCRE2_MSG_LIMIT);
+
+ return ly_err_new(err, LY_ESYS, 0, NULL, NULL, "%s", (const char *)pcre2_errmsg);
+ } else if (((rc == PCRE2_ERROR_NOMATCH) && !patterns[u]->inverted) ||
+ ((rc != PCRE2_ERROR_NOMATCH) && patterns[u]->inverted)) {
+ char *eapptag = patterns[u]->eapptag ? strdup(patterns[u]->eapptag) : NULL;
+
+ if (patterns[u]->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", patterns[u]->emsg);
+ } else {
+ const char *inverted = patterns[u]->inverted ? "inverted " : "";
+
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
+ LY_ERRMSG_NOPATTERN, (int)str_len, str, inverted, patterns[u]->expr);
+ }
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
+ size_t strval_len, struct ly_err_item **err)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool is_length; /* length or range */
+
+ *err = NULL;
+ is_length = (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? 1 : 0;
+
+ LY_ARRAY_FOR(range->parts, u) {
+ if (basetype < LY_TYPE_DEC64) {
+ /* unsigned */
+ if ((uint64_t)value < range->parts[u].min_u64) {
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
+ is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
+ }
+ } else if ((uint64_t)value <= range->parts[u].max_u64) {
+ /* inside the range */
+ return LY_SUCCESS;
+ } else if (u == LY_ARRAY_COUNT(range->parts) - 1) {
+ /* we have the last range part, so the value is out of bounds */
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
+ is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
+ }
+ }
+ } else {
+ /* signed */
+ if (value < range->parts[u].min_64) {
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
+ }
+ } else if (value <= range->parts[u].max_64) {
+ /* inside the range */
+ return LY_SUCCESS;
+ } else if (u == LY_ARRAY_COUNT(range->parts) - 1) {
+ /* we have the last range part, so the value is out of bounds */
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_prefix_data_new(const struct ly_ctx *ctx, const void *value, size_t value_len, LY_VALUE_FORMAT format,
+ const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p)
+{
+ LY_CHECK_ARG_RET(ctx, value, format_p, prefix_data_p, LY_EINVAL);
+
+ *prefix_data_p = NULL;
+ return ly_store_prefix_data(ctx, value, value_len, format, prefix_data, format_p, (void **)prefix_data_p);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_prefix_data_dup(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *orig, void **dup)
+{
+ LY_CHECK_ARG_RET(NULL, dup, LY_EINVAL);
+
+ *dup = NULL;
+ if (!orig) {
+ return LY_SUCCESS;
+ }
+
+ return ly_dup_prefix_data(ctx, format, orig, (void **)dup);
+}
+
+LIBYANG_API_DEF void
+lyplg_type_prefix_data_free(LY_VALUE_FORMAT format, void *prefix_data)
+{
+ ly_free_prefix_data(format, prefix_data);
+}
+
+static int
+type_get_hints_base(uint32_t hints)
+{
+ /* set allowed base */
+ switch (hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) {
+ case LYD_VALHINT_DECNUM:
+ return LY_BASE_DEC;
+ case LYD_VALHINT_OCTNUM:
+ return LY_BASE_OCT;
+ case LYD_VALHINT_HEXNUM:
+ return LY_BASE_HEX;
+ default:
+ /* generic base - decimal by default, hexa if prexed by 0x/0X and octal otherwise if prefixed by 0 */
+ return 0;
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type, int *base,
+ struct ly_err_item **err)
+{
+ LY_CHECK_ARG_RET(NULL, value || !value_len, err, LY_EINVAL);
+
+ *err = NULL;
+ if (!value) {
+ value = "";
+ }
+
+ switch (type) {
+ case LY_TYPE_UINT8:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_INT32:
+ LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
+
+ if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
+ 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);
+ }
+ *base = type_get_hints_base(hints);
+ break;
+ case LY_TYPE_UINT64:
+ case LY_TYPE_INT64:
+ LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
+
+ if (!(hints & LYD_VALHINT_NUM64)) {
+ 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);
+ }
+ *base = type_get_hints_base(hints);
+ break;
+ case LY_TYPE_STRING:
+ case LY_TYPE_DEC64:
+ case LY_TYPE_ENUM:
+ case LY_TYPE_BITS:
+ case LY_TYPE_BINARY:
+ case LY_TYPE_IDENT:
+ case LY_TYPE_INST:
+ if (!(hints & LYD_VALHINT_STRING)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-string-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ break;
+ case LY_TYPE_BOOL:
+ if (!(hints & LYD_VALHINT_BOOLEAN)) {
+ 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);
+ }
+ break;
+ case LY_TYPE_EMPTY:
+ if (!(hints & LYD_VALHINT_EMPTY)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-empty-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ break;
+ case LY_TYPE_UNKNOWN:
+ case LY_TYPE_LEAFREF:
+ case LY_TYPE_UNION:
+ LOGINT_RET(NULL);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_check_status(const struct lysc_node *ctx_node, uint16_t val_flags, LY_VALUE_FORMAT format, void *prefix_data,
+ const char *val_name, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ const struct lys_module *mod2;
+ uint16_t flg1, flg2;
+
+ if (format != LY_VALUE_SCHEMA) {
+ /* nothing/unable to check */
+ return LY_SUCCESS;
+ }
+
+ mod2 = ((struct lysp_module *)prefix_data)->mod;
+
+ if (mod2 == ctx_node->module) {
+ /* use flags of the context node since the definition is local */
+ flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ } else {
+ /* definition is foreign (deviation, refine), always current */
+ flg1 = LYS_STATUS_CURR;
+ }
+ flg2 = (val_flags & LYS_STATUS_MASK) ? (val_flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+
+ if ((flg1 < flg2) && (ctx_node->module == mod2)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL,
+ "A %s definition \"%s\" is not allowed to reference %s value \"%s\".",
+ flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name,
+ flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", val_name);
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_lypath_check_status(const struct lysc_node *ctx_node, const struct ly_path *path, LY_VALUE_FORMAT format,
+ void *prefix_data, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lys_module *val_mod;
+ const struct lysc_node *node;
+ uint16_t flg1, flg2;
+
+ if (format != LY_VALUE_SCHEMA) {
+ /* nothing to check */
+ return LY_SUCCESS;
+ }
+
+ val_mod = ((struct lysp_module *)prefix_data)->mod;
+ if (val_mod == ctx_node->module) {
+ /* use flags of the context node since the definition is local */
+ flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ } else {
+ /* definition is foreign (deviation, refine), always current */
+ flg1 = LYS_STATUS_CURR;
+ }
+
+ LY_ARRAY_FOR(path, u) {
+ node = path[u].node;
+
+ flg2 = (node->flags & LYS_STATUS_MASK) ? (node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ if ((flg1 < flg2) && (val_mod == node->module)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL,
+ "A %s definition \"%s\" is not allowed to reference %s value \"%s\".",
+ flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name,
+ flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", node->name);
+ return ret;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_len, uint32_t options,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node, struct lys_glob_unres *unres,
+ struct ly_path **path, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ uint32_t prefix_opt = 0;
+
+ LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL);
+
+ *path = NULL;
+ *err = NULL;
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ prefix_opt = LY_PATH_PREFIX_MANDATORY;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_JSON:
+ case LY_VALUE_STR_NS:
+ prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
+ break;
+ }
+
+ /* parse the value */
+ ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
+ if (ret) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value);
+ goto cleanup;
+ }
+
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ /* implement all prefixes */
+ LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup);
+ }
+
+ /* resolve it on schema tree */
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node->flags & LYS_IS_OUTPUT) ?
+ LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 1, format, prefix_data, path);
+ if (ret) {
+ ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value);
+ goto cleanup;
+ }
+
+cleanup:
+ lyxp_expr_free(ctx, exp);
+ if (ret) {
+ ly_path_free(ctx, *path);
+ *path = NULL;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_lypath_free(const struct ly_ctx *ctx, struct ly_path *path)
+{
+ ly_path_free(ctx, path);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_make_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
+{
+ if (mod->implemented) {
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(lys_implement(mod, features, unres));
+ LY_CHECK_RET(lys_compile(mod, &unres->ds_unres));
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *der)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(base->module->ctx == der->module->ctx);
+
+ LY_ARRAY_FOR(base->derived, u) {
+ if (der == base->derived[u]) {
+ return LY_SUCCESS;
+ }
+ if (!lyplg_type_identity_isderived(base->derived[u], der)) {
+ return LY_SUCCESS;
+ }
+ }
+ return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Try to generate a path to the leafref target with its value to enable the use of hash-based search.
+ *
+ * @param[in] path Leafref path.
+ * @param[in] ctx_node Leafref context node.
+ * @param[in] format Format of @p path.
+ * @param[in] prefix_data Prefix data of @p path.
+ * @param[in] target_val Leafref target value.
+ * @param[out] target_path Generated path with the target value.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if no matching target exists.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyplg_type_resolve_leafref_get_target_path(const struct lyxp_expr *path, const struct lysc_node *ctx_node,
+ LY_VALUE_FORMAT format, void *prefix_data, const char *target_val, struct lyxp_expr **target_path)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint8_t oper;
+ struct ly_path *p = NULL;
+ char *str_path = NULL, quot;
+ int len;
+ ly_bool list_key = 0;
+
+ *target_path = NULL;
+
+ /* compile, has already been so it must succeed */
+ oper = (ctx_node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
+ if (ly_path_compile_leafref(ctx_node->module->ctx, ctx_node, NULL, path, oper, LY_PATH_TARGET_MANY, format,
+ prefix_data, &p)) {
+ /* the target was found before but is disabled so it was removed */
+ return LY_ENOT;
+ }
+
+ /* check whether we can search for a list instance with a specific key value */
+ if (lysc_is_key(p[LY_ARRAY_COUNT(p) - 1].node)) {
+ if ((LY_ARRAY_COUNT(p) >= 2) && (p[LY_ARRAY_COUNT(p) - 2].node->nodetype == LYS_LIST)) {
+ if ((path->tokens[path->used - 1] == LYXP_TOKEN_NAMETEST) &&
+ (path->tokens[path->used - 2] == LYXP_TOKEN_OPER_PATH) &&
+ (path->tokens[path->used - 3] == LYXP_TOKEN_NAMETEST)) {
+ list_key = 1;
+ } /* else again, should be possible but does not make sense */
+ } /* else allowed despite not making sense */
+ }
+
+ if (list_key) {
+ /* get the length of the orig expression without the last "/" and the key node */
+ len = path->tok_pos[path->used - 3] + path->tok_len[path->used - 3];
+
+ /* generate the string path evaluated using hashes */
+ quot = strchr(target_val, '\'') ? '\"' : '\'';
+ if (asprintf(&str_path, "%.*s[%s=%c%s%c]/%s", len, path->expr, path->expr + path->tok_pos[path->used - 1],
+ quot, target_val, quot, path->expr + path->tok_pos[path->used - 1]) == -1) {
+ LOGMEM(ctx_node->module->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+
+ } else {
+ /* leaf will not be found using hashes, but generate the path just to unify it */
+ assert(p[LY_ARRAY_COUNT(p) - 1].node->nodetype & LYD_NODE_TERM);
+
+ /* generate the string path evaluated using hashes */
+ quot = strchr(target_val, '\'') ? '\"' : '\'';
+ if (asprintf(&str_path, "%s[.=%c%s%c]", path->expr, quot, target_val, quot) == -1) {
+ LOGMEM(ctx_node->module->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ }
+
+ /* parse into an expression */
+ LY_CHECK_GOTO(lyxp_expr_parse(ctx_node->module->ctx, str_path, 0, 1, target_path), cleanup);
+
+cleanup:
+ ly_path_free(ctx_node->module->ctx, p);
+ free(str_path);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node, struct lyd_value *value,
+ const struct lyd_node *tree, struct lyd_node **target, char **errmsg)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxp_expr *target_path = NULL;
+ struct lyxp_set set = {0};
+ const char *val_str, *xp_err_msg;
+ uint32_t i;
+ int r;
+
+ LY_CHECK_ARG_RET(NULL, lref, node, value, errmsg, LY_EINVAL);
+
+ if (target) {
+ *target = NULL;
+ }
+
+ /* get the canonical value */
+ val_str = lyd_value_get_canonical(LYD_CTX(node), value);
+
+ if (!strchr(val_str, '\"') || !strchr(val_str, '\'')) {
+ /* get the path with the value */
+ r = lyplg_type_resolve_leafref_get_target_path(lref->path, node->schema, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes,
+ val_str, &target_path);
+ if (r == LY_ENOT) {
+ goto cleanup;
+ } else if (r) {
+ rc = r;
+ goto cleanup;
+ }
+ } /* else value with both ' and ", XPath does not support that */
+
+ /* find the target data instance(s) */
+ rc = lyxp_eval(LYD_CTX(node), target_path ? target_path : lref->path, node->schema->module,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, node, node, tree, NULL, &set, LYXP_IGNORE_WHEN);
+ if (rc) {
+ if (ly_errcode(LYD_CTX(node)) == rc) {
+ xp_err_msg = ly_errmsg(LYD_CTX(node));
+ } else {
+ xp_err_msg = NULL;
+ }
+
+ if (xp_err_msg) {
+ r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error (%s).", val_str, xp_err_msg);
+ } else {
+ r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error.", val_str);
+ }
+ if (r == -1) {
+ *errmsg = NULL;
+ rc = LY_EMEM;
+ }
+ goto cleanup;
+ }
+
+ /* check the result */
+ if (target_path) {
+ /* no or exact match(es) */
+ i = 0;
+ } else {
+ /* check whether any matches */
+ for (i = 0; i < set.used; ++i) {
+ if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ continue;
+ }
+
+ if (!lref->plugin->compare(&((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
+ break;
+ }
+ }
+ }
+
+ if (i == set.used) {
+ /* no match found */
+ rc = LY_ENOTFOUND;
+ if (asprintf(errmsg, LY_ERRMSG_NOLREF_VAL, val_str, lref->path->expr) == -1) {
+ *errmsg = NULL;
+ rc = LY_EMEM;
+ }
+ goto cleanup;
+ }
+ if (target) {
+ *target = set.val.nodes[i].node;
+ }
+
+cleanup:
+ lyxp_expr_free(LYD_CTX(node), target_path);
+ lyxp_set_free_content(&set);
+ return rc;
+}
diff --git a/src/plugins_types.h b/src/plugins_types.h
new file mode 100644
index 0000000..3ec1d3b
--- /dev/null
+++ b/src/plugins_types.h
@@ -0,0 +1,1214 @@
+/**
+ * @file plugins_types.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief API for (user) types plugins
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PLUGINS_TYPES_H_
+#define LY_PLUGINS_TYPES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "log.h"
+#include "plugins.h"
+#include "tree.h"
+
+#include "tree_edit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_ctx;
+struct ly_path;
+struct lyd_node;
+struct lyd_value;
+struct lyd_value_xpath10;
+struct lys_module;
+struct lys_glob_unres;
+struct lysc_ident;
+struct lysc_node;
+struct lysc_pattern;
+struct lysc_range;
+struct lysc_type;
+struct lysc_type_bits;
+struct lysc_type_leafref;
+
+/**
+ * @page howtoPluginsTypes Type Plugins
+ *
+ * Note that the part of the libyang API here is available only by including a separated `<libyang/plugins_types.h>` header
+ * file. Also note that the type plugins API is versioned separately from libyang itself, so backward incompatible changes
+ * can come even without changing libyang major version.
+ *
+ * YANG allows to define new data types via *typedef* statements or even in leaf's/leaf-list's *type* statements.
+ * Such types are derived (directly or indirectly) from a set of [YANG built-in types](https://tools.ietf.org/html/rfc7950#section-4.2.4).
+ * libyang implements all handling of the data values of the YANG types via the Type Plugins API. Internally, there is
+ * implementation of the built-in types and others can be added as an external plugin (see @ref howtoPlugins).
+ *
+ * Type plugin is supposed to
+ * - store (and canonize) data value,
+ * - validate it according to the type's restrictions,
+ * - compare two values (::lyd_value) of the same type,
+ * - duplicate value (::lyd_value),
+ * - print it and
+ * - free the specific data inserted into ::lyd_value.
+ *
+ * These tasks are implemented as callbacks provided to libyang via ::lyplg_type_record structures defined as array using
+ * ::LYPLG_TYPES macro.
+ *
+ * All the callbacks are supposed to do not log directly via libyang logger. Instead, they return ::LY_ERR value and
+ * ::ly_err_item error structure(s) describing the detected error(s) (helper functions ::ly_err_new() and ::ly_err_free()
+ * are available).
+ *
+ * The main functionality is provided via ::lyplg_type_store_clb callback responsible for canonizing and storing
+ * provided string representation of the value in specified format (XML and JSON supported). Valid value is stored in
+ * ::lyd_value structure - its union allows to store data as one of the predefined type or in a custom form behind
+ * the void *ptr member of ::lyd_value structure. The callback is also responsible for storing canonized string
+ * representation of the value as ::lyd_value._canonical. If the type does not define canonical representation, the original
+ * representation is stored. In case there are any differences between the representation in specific input types, the plugin
+ * is supposed to store the value in JSON representation - typically, the difference is in prefix representation and JSON
+ * format uses directly the module names as prefixes.
+ *
+ * Usually, all the validation according to the type's restrictions is done in the store callback. However, in case the type
+ * requires some validation referencing other entities in the data tree, the optional validation callback
+ * ::lyplg_type_validate_clb can be implemented.
+ *
+ * The stored values can be compared in a specific way by providing ::lyplg_type_compare_clb. In case the best way to compare
+ * the values is to compare their canonical string representations, the ::lyplg_type_compare_simple() function can be used.
+ *
+ * Data duplication is done with ::lyplg_type_dup_clb callbacks. Note that the callback is responsible even for duplicating
+ * the ::lyd_value._canonical, so the callback must be always present (the canonical value is always present). If there is
+ * nothing else to duplicate, the plugin can use the generic ::lyplg_type_dup_simple().
+ *
+ * The stored value can be printed into the required format via ::lyplg_type_print_clb implementation. Simple printing
+ * canonical representation of the value is implemented by ::lyplg_type_print_simple().
+ *
+ * And finally freeing any data stored in the ::lyd_value by the plugin is done by implementation of ::lyplg_type_free_clb.
+ * Freeing only the canonical string is implemented by ::lyplg_type_free_simple().
+ *
+ * The plugin information contains also the plugin identifier (::lyplg_type.id). This string can serve to identify the
+ * specific plugin responsible to storing data value. In case the user can recognize the id string, it can access the
+ * plugin specific data with the appropriate knowledge of its structure.
+ *
+ * Besides the mentioned `_simple` functions, libyang provides, as part of the type plugins API, all the callbacks
+ * implementing the built-in types in the internal plugins:
+ *
+ * - [simple callbacks](@ref pluginsTypesSimple) handling only the canonical strings in the value,
+ * - [binary built-in type](@ref pluginsTypesBinary)
+ * - [bits built-in type](@ref pluginsTypesBits)
+ * - [boolean built-in type](@ref pluginsTypesBoolean)
+ * - [decimal64 built-in type](@ref pluginsTypesDecimal64)
+ * - [empty built-in type](@ref pluginsTypesEmpty)
+ * - [enumeration built-in type](@ref pluginsTypesEnumeration)
+ * - [identityref built-in type](@ref pluginsTypesIdentityref)
+ * - [instance-identifier built-in type](@ref pluginsTypesInstanceid)
+ * - [integer built-in types](@ref pluginsTypesInteger)
+ * - [leafref built-in type](@ref pluginsTypesLeafref)
+ * - [string built-in type](@ref pluginsTypesString)
+ * - [union built-in type](@ref pluginsTypesUnion)
+ *
+ * And one derived type:
+ *
+ * - [xpath1.0 `ietf-yang-types` type](@ref pluginsTypesXpath10)
+ *
+ * In addition to these callbacks, the API also provides several functions which can help to implement your own plugin for the
+ * derived YANG types:
+ *
+ * - ::ly_err_new()
+ * - ::ly_err_free()
+ *
+ * - ::lyplg_type_lypath_new()
+ * - ::lyplg_type_lypath_free()
+ *
+ * - ::lyplg_type_prefix_data_new()
+ * - ::lyplg_type_prefix_data_dup()
+ * - ::lyplg_type_prefix_data_free()
+ * - ::lyplg_type_get_prefix()
+ *
+ * - ::lyplg_type_check_hints()
+ * - ::lyplg_type_check_status()
+ * - ::lyplg_type_lypath_check_status()
+ * - ::lyplg_type_identity_isderived()
+ * - ::lyplg_type_identity_module()
+ * - ::lyplg_type_make_implemented()
+ * - ::lyplg_type_parse_dec64()
+ * - ::lyplg_type_parse_int()
+ * - ::lyplg_type_parse_uint()
+ * - ::lyplg_type_resolve_leafref()
+ */
+
+/**
+ * @defgroup pluginsTypes Plugins: Types
+ * @{
+ *
+ * Structures and functions to for libyang plugins implementing specific YANG types defined in YANG modules. For more
+ * information, see @ref howtoPluginsTypes.
+ *
+ * This part of libyang API is available by including `<libyang/plugins_types.h>` header file.
+ */
+
+/**
+ * @brief Type API version
+ */
+#define LYPLG_TYPE_API_VERSION 1
+
+/**
+ * @brief Macro to define plugin information in external plugins
+ *
+ * Use as follows:
+ * LYPLG_TYPES = {{<filled information of ::lyplg_type_record>}, ..., {0}};
+ */
+#define LYPLG_TYPES \
+ uint32_t plugins_types_apiver__ = LYPLG_TYPE_API_VERSION; \
+ const struct lyplg_type_record plugins_types__[]
+
+/**
+ * @brief Check whether specific type value needs to be allocated dynamically.
+ *
+ * @param[in] type_val Pointer to specific type value storage.
+ */
+#define LYPLG_TYPE_VAL_IS_DYN(type_val) \
+ (sizeof *(type_val) > LYD_VALUE_FIXED_MEM_SIZE)
+
+/**
+ * @brief Prepare value memory for storing a specific type value, may be allocated dynamically.
+ *
+ * Must be called for values larger than 8 bytes.
+ * To be used in ::lyplg_type_store_clb.
+ *
+ * @param[in] storage Pointer to the value storage to use (struct ::lyd_value *).
+ * @param[in,out] type_val Pointer to specific type value structure.
+ */
+#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val) \
+ (LYPLG_TYPE_VAL_IS_DYN(type_val) \
+ ? ((type_val) = ((storage)->dyn_mem = calloc(1, sizeof *(type_val)))) \
+ : ((type_val) = memset((storage)->fixed_mem, 0, sizeof *(type_val))))
+
+/**
+ * @brief Destroy a prepared value.
+ *
+ * Must be called for values prepared with ::LYPLG_TYPE_VAL_INLINE_PREPARE.
+ *
+ * @param[in] type_val Pointer to specific type value structure.
+ */
+#define LYPLG_TYPE_VAL_INLINE_DESTROY(type_val) \
+ do { if (LYPLG_TYPE_VAL_IS_DYN(type_val)) free(type_val); } while(0)
+
+/**
+ * @brief Create and fill error structure.
+ *
+ * Helper function for various plugin functions to generate error information structure.
+ *
+ * @param[in, out] err Pointer to store a new error structure filled according to the input parameters. If the storage
+ * already contains error information, the new record is appended into the errors list.
+ * @param[in] ecode Code of the error to fill. In case LY_SUCCESS value, nothing is done and LY_SUCCESS is returned.
+ * @param[in] vecode Validity error code in case of LY_EVALID error code.
+ * @param[in] path Path to the node causing the error.
+ * @param[in] apptag Error-app-tag value.
+ * @param[in] err_format Format string (same like at printf) or string literal.
+ * If you want to print just an unknown string, use "%s" for the @p err_format, otherwise undefined behavior may occur
+ * because the unknown string may contain the % character, which is interpreted as conversion specifier.
+ * @return The given @p ecode value if the @p err is successfully created. The structure can be freed using ::ly_err_free()
+ * or passed back from callback into libyang.
+ * @return LY_EMEM If there is not enough memory for allocating error record, the @p err is not touched in that case.
+ * @return LY_SUCCESS if @p ecode is LY_SUCCESS, the @p err is not touched in this case.
+ */
+LIBYANG_API_DECL LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag,
+ const char *err_format, ...) _FORMAT_PRINTF(6, 7);
+
+/**
+ * @brief Destructor for the error records created with ::ly_err_new().
+ *
+ * Compatible with the free(), so usable as a generic callback.
+ *
+ * @param[in] ptr Error record (::ly_err_item, the void pointer is here only for compatibility with a generic free()
+ * function) to free. With the record, also all the records (if any) connected after this one are freed.
+ */
+LIBYANG_API_DECL void ly_err_free(void *ptr);
+
+/**
+ * @brief Check that the type is suitable for the parser's hints (if any) in the specified format
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types provided by parsers
+ * to ::lyplg_type_store_clb.
+ * @param[in] value Lexical representation of the value to be stored.
+ * @param[in] value_len Length (number of bytes) of the given \p value.
+ * @param[in] type Expected base type of the @p value by the caller.
+ * @param[out] base Pointer to store the numeric base for parsing numeric values using strtol()/strtoll() function.
+ * Returned (and required) only for numeric @p type values.
+ * @param[out] err Pointer to store error information in case of failure.
+ * @return LY_ERR value
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type,
+ int *base, struct ly_err_item **err);
+
+/**
+ * @brief Check that the value of a type is allowed based on its status.
+ *
+ * @param[in] ctx_node Context node (which references the value).
+ * @param[in] val_flags Flags fo the value.
+ * @param[in] format Format of the value.
+ * @param[in] prefix_data Prefix data of the value.
+ * @param[in] val_name Name of the value, only for logging.
+ * @param[out] err Pointer to store error information in case of failure.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_check_status(const struct lysc_node *ctx_node, uint16_t val_flags, LY_VALUE_FORMAT format,
+ void *prefix_data, const char *val_name, struct ly_err_item **err);
+
+/**
+ * @brief Check that the lypath instance-identifier value is allowed based on the status of the nodes.
+ *
+ * @param[in] ctx_node Context node (which references the value).
+ * @param[in] path Path of the instance-identifier.
+ * @param[in] format Format of the value.
+ * @param[in] prefix_data Prefix data of the value.
+ * @param[out] err Pointer to store error information in case of failure.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_lypath_check_status(const struct lysc_node *ctx_node, const struct ly_path *path,
+ LY_VALUE_FORMAT format, void *prefix_data, struct ly_err_item **err);
+
+/**
+ * @brief Get the corresponding module for the identity value.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Schema node where the value is instantiated to determine the module in case of unprefixed value
+ * in specific @p format.
+ * @param[in] prefix Prefix to resolve - identified beginning of a prefix in ::lyplg_type_store_clb's value parameter.
+ * If NULL, an unprefixed identity is resolved.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of the prefix (::lyplg_type_store_clb's format parameter).
+ * @param[in] prefix_data Format-specific data (::lyplg_type_store_clb's prefix_data parameter).
+ * @return Resolved prefix module,
+ * @return NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lys_module *lyplg_type_identity_module(const struct ly_ctx *ctx,
+ const struct lysc_node *ctx_node, const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format,
+ const void *prefix_data);
+
+/**
+ * @brief Implement a module (just like ::lys_set_implemented()), but keep maintaining unresolved items.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] mod Module to implement.
+ * @param[in] features Array of features to enable.
+ * @param[in,out] unres Global unres to add to.
+ * @return LY_ERECOMPILE if the context need to be recompiled, should be returned.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_make_implemented(struct lys_module *mod, const char **features,
+ struct lys_glob_unres *unres);
+
+/**
+ * @brief Get the bitmap size of a bits value bitmap.
+ *
+ * Bitmap size is rounded up to the smallest integer size (1, 2, 4, or 8 bytes).
+ * If more than 8 bytes are needed to hold all the bit positions, no rounding is performed.
+ *
+ * @param[in] type Bits type.
+ * @return Bitmap size in bytes.
+ */
+LIBYANG_API_DECL size_t lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type);
+
+/**
+ * @brief Check whether a particular bit of a bitmap is set.
+ *
+ * @param[in] bitmap Bitmap to read from.
+ * @param[in] size Size of @p bitmap.
+ * @param[in] bit_position Bit position to check.
+ * @return Whether the bit is set or not.
+ */
+LIBYANG_API_DECL ly_bool lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position);
+
+/**
+ * @brief Print xpath1.0 token in the specific format.
+ *
+ * @param[in] token Token to transform.
+ * @param[in] tok_len Lenghth of @p token.
+ * @param[in] is_nametest Whether the token is a nametest, it then always requires a prefix in XML @p get_format.
+ * @param[in,out] context_mod Current context module, may be updated.
+ * @param[in] resolve_ctx Context to use for resolving prefixes.
+ * @param[in] resolve_format Format of the resolved prefixes.
+ * @param[in] resolve_prefix_data Resolved prefixes prefix data.
+ * @param[in] get_format Format of the output prefixes.
+ * @param[in] get_prefix_data Format-specific prefix data for the output.
+ * @param[out] token_p Printed token.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DEF LY_ERR lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_nametest,
+ const struct lys_module **context_mod, const struct ly_ctx *resolve_ctx, LY_VALUE_FORMAT resolve_format,
+ const void *resolve_prefix_data, LY_VALUE_FORMAT get_format, void *get_prefix_data, char **token_p,
+ struct ly_err_item **err);
+
+/**
+ * @brief Get format-specific prefix for a module.
+ *
+ * Use only in implementations of ::lyplg_type_print_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] mod Module whose prefix to get - the module somehow connected with the value to print.
+ * @param[in] format Format of the prefix (::lyplg_type_print_clb's format parameter).
+ * @param[in] prefix_data Format-specific data (::lyplg_type_print_clb's prefix_data parameter).
+ * @return Module prefix to print.
+ * @return NULL on error.
+ */
+LIBYANG_API_DECL const char *lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @brief Store used prefixes in a string into an internal libyang structure used in ::lyd_value.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * If @p prefix_data_p are non-NULL, they are treated as valid according to the @p format_p and new possible
+ * prefixes are simply added. This way it is possible to store prefix data for several strings together.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] value Value to be parsed.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Format of the prefixes in the value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in,out] format_p Resulting format of the prefixes.
+ * @param[in,out] prefix_data_p Resulting prefix data for the value in format @p format_p.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_prefix_data_new(const struct ly_ctx *ctx, const void *value, size_t value_len,
+ LY_VALUE_FORMAT format, const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p);
+/**
+ * @brief Duplicate prefix data.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] format Format of the prefixes in the value.
+ * @param[in] orig Prefix data to duplicate.
+ * @param[out] dup Duplicated prefix data.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_prefix_data_dup(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *orig,
+ void **dup);
+
+/**
+ * @brief Free internal prefix data.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] format Format of the prefixes.
+ * @param[in] prefix_data Format-specific data to free.
+ */
+LIBYANG_API_DECL void lyplg_type_prefix_data_free(LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @brief Helper function to create internal schema path representation for instance-identifier value representation.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] ctx libyang Context
+ * @param[in] value Lexical representation of the value to be stored.
+ * @param[in] value_len Length (number of bytes) of the given @p value.
+ * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
+ * @param[in] format Input format of the value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in] ctx_node The @p value schema context node.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] path Pointer to store the created structure representing the schema path from the @p value.
+ * @param[out] err Pointer to store the error information provided in case of failure.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERECOMPILE if the context need to be recompiled, should be returned.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node,
+ struct lys_glob_unres *unres, struct ly_path **path, struct ly_err_item **err);
+
+/**
+ * @brief Free ly_path structure used by instanceid value representation.
+ *
+ * The ly_path representation can be created by ::lyplg_type_lypath_new().
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] path The structure ([sized array](@ref sizedarrays)) to free.
+ */
+LIBYANG_API_DECL void lyplg_type_lypath_free(const struct ly_ctx *ctx, struct ly_path *path);
+
+/**
+ * @brief Print xpath1.0 value in the specific format.
+ *
+ * @param[in] xp_val xpath1.0 value structure.
+ * @param[in] format Format to print in.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[out] str_value Printed value.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_print_xpath10_value(const struct lyd_value_xpath10 *xp_val, LY_VALUE_FORMAT format,
+ void *prefix_data, char **str_value, struct ly_err_item **err);
+
+/**
+ * @defgroup plugintypestoreopts Plugins: Type store callback options.
+ *
+ * Options applicable to ::lyplg_type_store_clb().
+ *
+ * @{
+ */
+#define LYPLG_TYPE_STORE_DYNAMIC 0x01 /**< Value was dynamically allocated in its exact size and is supposed to be freed or
+ directly inserted into the context's dictionary (e.g. in case of canonization).
+ In any case, the caller of the callback does not free the provided
+ value after calling the type's store callback with this option. */
+#define LYPLG_TYPE_STORE_IMPLEMENT 0x02 /**< If a foreign module is needed to be implemented to successfully instantiate
+ the value, make the module implemented. */
+/** @} plugintypestoreopts */
+
+/**
+ * @brief Callback to store the given @p value according to the given @p type.
+ *
+ * Value must always be correctly stored meaning all the other type callbacks (such as print or compare)
+ * must function as expected. However, ::lyd_value._canonical can be left NULL and will be generated
+ * and stored on-demand. But if @p format is ::LY_VALUE_CANON (or another, which must be equal to the canonical
+ * value), the canonical value should be stored so that it does not have to be generated later.
+ *
+ * Note that the @p value is not necessarily used whole (may not be zero-terminated if a string). The provided
+ * @p value_len is always correct. All store functions have to free a dynamically allocated @p value in all
+ * cases (even on error).
+ *
+ * @param[in] ctx libyang context
+ * @param[in] type Type of the value being stored.
+ * @param[in] value Value to be stored.
+ * @param[in] value_len Length (number of bytes) of the given @p value.
+ * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
+ * @param[in] format Input format of the value, see the description for details.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types.
+ * @param[in] ctx_node Schema context node of @p value, may be NULL for metadata.
+ * @param[out] storage Storage for the value in the type's specific encoding. Except for _canonical_, all the members
+ * should be filled by the plugin (if it fills them at all).
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
+ * error message is prepared instead. The error structure can be created by ::ly_err_new().
+ * @return LY_SUCCESS on success,
+ * @return LY_EINCOMPLETE in case the ::lyplg_type_validate_clb should be called to finish value validation in data,
+ * @return LY_ERR value on error, @p storage must not have any pointers to dynamic memory.
+ */
+LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_store_clb)(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const void *value, size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Callback to validate the stored value in data.
+ *
+ * This callback is optional for types that can only be validated in a data tree. It must be called and succeed
+ * in case the ::lyplg_type_store_clb callback returned ::LY_EINCOMPLETE for the value to be valid. However, this
+ * callback can be called even in other cases (such as separate/repeated validation).
+ *
+ * @param[in] ctx libyang context
+ * @param[in] type Original type of the value (not necessarily the stored one) being validated.
+ * @param[in] ctx_node The value data context node for validation.
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) with possibly referenced data.
+ * @param[in,out] storage Storage of the value successfully filled by ::lyplg_type_store_clb. May be modified.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
+ * error message is prepared instead. The error structure can be created by ::ly_err_new().
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_validate_clb)(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
+
+/**
+ * @brief Callback for comparing 2 values of the same type.
+ *
+ * In case the value types (::lyd_value.realtype) are different, ::LY_ENOT must always be returned.
+ * It can be assumed that the same context (dictionary) was used for storing both values.
+ *
+ * @param[in] val1 First value to compare.
+ * @param[in] val2 Second value to compare.
+ * @return LY_SUCCESS if values are same (according to the type's definition of being same).
+ * @return LY_ENOT if values differ.
+ */
+typedef LY_ERR (*lyplg_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Unused callback for sorting values.
+ *
+ * @param[in] val1 First value to compare.
+ * @param[in] val2 Second value to compare.
+ * @return -1 if val1 < val2,
+ * @return 0 if val1 == val2,
+ * @return 1 if val1 > val2.
+ */
+typedef int (*lyplg_type_sort_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Callback for getting the value of the data stored in @p value.
+ *
+ * Canonical value (@p format of ::LY_VALUE_CANON) must always be a zero-terminated const string stored in
+ * the dictionary. The ::lyd_value._canonical member should be used for storing (caching) it.
+ *
+ * @param[in] ctx libyang context for storing the canonical value. May not be set for ::LY_VALUE_LYB format.
+ * @param[in] value Value to print.
+ * @param[in] format Format in which the data are supposed to be printed. Formats ::LY_VALUE_SCHEMA and
+ * ::LY_VALUE_SCHEMA_RESOLVED are not supported and should not be implemented.
+ * @param[in] prefix_data Format-specific data for processing prefixes. In case of using one of the built-in's print
+ * callback (or ::lyplg_type_print_simple()), the argument is just simply passed in. If you need to handle prefixes
+ * in the value on your own, there is ::lyplg_type_get_prefix() function to help.
+ * @param[out] dynamic Flag if the returned value is dynamically allocated. In such a case the caller is responsible
+ * for freeing it. Will not be set and should be ignored for @p format ::LY_VALUE_CANON.
+ * @param[out] value_len Optional returned value length in bytes. For strings it EXCLUDES the terminating zero.
+ * @return Pointer to @p value in the specified @p format. According to the returned @p dynamic flag, caller
+ * can be responsible for freeing allocated memory.
+ * @return NULL in case of error.
+ */
+typedef const void *(*lyplg_type_print_clb)(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Callback to duplicate data in the data structure.
+ *
+ * @param[in] ctx libyang context of the @p dup. Note that the context of @p original and @p dup might not be the same.
+ * @param[in] original Original data structure to be duplicated.
+ * @param[in,out] dup Prepared data structure to be filled with the duplicated data of @p original.
+ * @return LY_SUCCESS after successful duplication.
+ * @return LY_ERR value on error.
+ */
+typedef LY_ERR (*lyplg_type_dup_clb)(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+
+/**
+ * @brief Callback for freeing the user type values stored by ::lyplg_type_store_clb.
+ *
+ * Note that this callback is responsible also for freeing the canonized member in the @p value.
+ *
+ * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary.
+ * @param[in,out] value Value structure to free the data stored there by the plugin's ::lyplg_type_store_clb callback
+ */
+typedef void (*lyplg_type_free_clb)(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Hold type-specific functions for various operations with the data values.
+ *
+ * libyang includes set of plugins for all the built-in types. They are, by default, inherited to the derived types.
+ * However, if the user type plugin for the specific type is loaded, the plugin can provide it's own functions.
+ * The built-in types plugin callbacks are public, so even the user type plugins can use them to do part of their own
+ * functionality.
+ */
+struct lyplg_type {
+ const char *id; /**< Plugin identification (mainly for distinguish incompatible versions when
+ used by external tools) */
+ lyplg_type_store_clb store; /**< store and canonize the value in the type-specific way */
+ lyplg_type_validate_clb validate; /**< optional, validate the value in the type-specific way in data */
+ lyplg_type_compare_clb compare; /**< comparison callback to compare 2 values of the same type */
+ lyplg_type_sort_clb sort; /**< unused comparison callback for sorting values */
+ lyplg_type_print_clb print; /**< printer callback to get string representing the value */
+ lyplg_type_dup_clb duplicate; /**< data duplication callback */
+ lyplg_type_free_clb free; /**< optional function to free the type-spceific way stored value */
+ int32_t lyb_data_len; /**< Length of the data in [LYB format](@ref howtoDataLYB).
+ For variable-length is set to -1. */
+};
+
+struct lyplg_type_record {
+ /* plugin identification */
+ const char *module; /**< name of the module where the type is defined (top-level typedef) */
+ const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
+ which is not an optimal approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same pointer to the plugin functions. The
+ only valid use case for the NULL revision is the case the module has no revision. */
+ const char *name; /**< name of the typedef */
+
+ /* runtime data */
+ struct lyplg_type plugin; /**< data to utilize plugin implementation */
+};
+
+/**
+ * @defgroup pluginsTypesSimple Plugins: Simple Types Callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Simple functions implementing @ref howtoPluginsTypes callbacks handling types that allocate no dynamic
+ * value and always generate their canonical value (::lyd_value._canonical).
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for a generic simple type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for a generic simple type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_simple(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for a generic simple type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for a generic simple type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesSimple */
+
+/**
+ * @defgroup pluginsTypesBinary Plugins: Binary built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used to implement binary built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesBinary */
+
+/**
+ * @defgroup pluginsTypesBits Plugins: Bits built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement bits built-in type.
+ */
+
+/**
+ * @brief Implementation of the ::lyplg_type_store_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_bits(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of the ::lyplg_type_compare_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of the ::lyplg_type_print_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_bits(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of the ::lyplg_type_dup_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of the ::lyplg_type_free_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesBits */
+
+/**
+ * @defgroup pluginsTypesBoolean Plugins: Boolean built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement boolean built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in boolean type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_boolean(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in boolean type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_boolean(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in boolean type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_boolean(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesBoolean */
+
+/**
+ * @defgroup pluginsTypesDecimal64 Plugins: Decimal64 built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement decimal64 built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in decimal64 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const void *value, size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in decimal64 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_decimal64(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in decimal64 type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_decimal64(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesDecimal64 */
+
+/**
+ * @defgroup pluginsTypesEmpty Plugins: Empty built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement empty built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in empty type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_empty(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/** @} pluginsTypesEmpty */
+
+/**
+ * @defgroup pluginsTypesEnumeration Plugins: Enumeration built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement enumeration built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in enumeration type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_enum(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in enumeration type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_enum(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesEnumeration */
+
+/**
+ * @defgroup pluginsTypesIdentityref Plugins: Identityref built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement identityref built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in identityref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const void *value, size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in identityref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in identityref type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_identityref(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesIdentityref */
+
+/**
+ * @defgroup pluginsTypesInstanceid Plugins: Instance-identifier built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement instance-identifier built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const void *value, size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesInstanceid */
+
+/**
+ * @defgroup pluginsTypesInteger Plugins: Integer built-in types callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement integer built-in types.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in signed integer types.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_int(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in signed integer types.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_int(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in signed integer types.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_int(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in unsigned integer types.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_uint(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in unsigned integer types.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in unsigned integer types.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_uint(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesInteger */
+
+/**
+ * @defgroup pluginsTypesLeafref Plugins: Leafref built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement leafref built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_leafref(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_leafref(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesLeafref */
+
+/**
+ * @defgroup pluginsTypesString Plugins: String built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement string built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in string type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/** @} pluginsTypesString */
+
+/**
+ * @defgroup pluginsTypesUnion Plugins: Union built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement union built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in union type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in union type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in union type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in union type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in union type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type,
+ const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in union type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesUnion */
+
+/**
+ * @defgroup pluginsTypesXpath10 Plugins: xpath1.0 `ietf-yang-types` type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement xpath1.0 derived type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_store_xpath10(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_xpath10(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesXpath10 */
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] min Lower bound of the type.
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed integer value (optional).
+ * @param[out] err Error information in case of failure. The error structure can be freed by ::ly_err_free().
+ * @return LY_ERR value according to the result of the parsing and validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value,
+ size_t value_len, int64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the unsigned integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed unsigned integer value (optional).
+ * @param[out] err Error information in case of failure. The error structure can be freed by ::ly_err_free().
+ * @return LY_ERR value according to the result of the parsing and validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value,
+ size_t value_len, uint64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Convert a string with a decimal64 value into libyang representation:
+ * ret = value * 10^fraction-digits
+ *
+ * @param[in] fraction_digits Fraction-digits of the decimal64 type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed decimal64 value representing original value * 10^fraction-digits (optional).
+ * @param[out] err Error information in case of failure. The error structure can be freed by ::ly_err_free().
+ * @return LY_ERR value according to the result of the parsing and validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret,
+ struct ly_err_item **err);
+
+/**
+ * @brief Decide if the @p derived identity is derived from (based on) the @p base identity.
+ *
+ * @param[in] base Expected base identity.
+ * @param[in] derived Expected derived identity.
+ * @return LY_SUCCESS if @p derived IS based on the @p base identity.
+ * @return LY_ENOTFOUND if @p derived IS NOT not based on the @p base identity.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *derived);
+
+/**
+ * @brief Data type validator for a range/length-restricted values.
+ *
+ * @param[in] basetype Base built-in type of the type with the range specified to get know if the @p range structure represents range or length restriction.
+ * @param[in] range Range (length) restriction information.
+ * @param[in] value Value to check. In case of basetypes using unsigned integer values, the value is actually cast to uint64_t.
+ * @param[in] strval String representation of the @p value for error logging.
+ * @param[in] strval_len Length of @p strval.
+ * @param[out] err Error information in case of failure. The error structure can be freed by ::ly_err_free().
+ * @return LY_ERR value according to the result of the validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value,
+ const char *strval, size_t strval_len, struct ly_err_item **err);
+
+/**
+ * @brief Data type validator for pattern-restricted string values.
+ *
+ * @param[in] patterns ([Sized array](@ref sizedarrays)) of the compiled list of pointers to the pattern restrictions.
+ * The array can be found in the ::lysc_type_str.patterns structure.
+ * @param[in] str String to validate.
+ * @param[in] str_len Length (number of bytes) of the string to validate (mandatory).
+ * @param[out] err Error information in case of failure or non-matching @p str. The error structure can be freed by ::ly_err_free().
+ * @return LY_SUCCESS when @p matches all the patterns.
+ * @return LY_EVALID when @p does not match any of the patterns.
+ * @return LY_ESYS in case of PCRE2 error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len,
+ struct ly_err_item **err);
+
+/**
+ * @brief Find leafref target in data.
+ *
+ * @param[in] lref Leafref type.
+ * @param[in] node Context node.
+ * @param[in] value Target value.
+ * @param[in] tree Full data tree to search in.
+ * @param[out] target Optional found target.
+ * @param[out] errmsg Error message in case of error.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node,
+ struct lyd_value *value, const struct lyd_node *tree, struct lyd_node **target, char **errmsg);
+
+/** @} pluginsTypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PLUGINS_TYPES_H_ */
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
new file mode 100644
index 0000000..519ec2e
--- /dev/null
+++ b/src/plugins_types/binary.c
@@ -0,0 +1,466 @@
+/**
+ * @file binary.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in binary type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBinary binary (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | binary value size | yes | `void *` | value in binary |
+ */
+
+/**
+ * @brief base64 encode table
+ */
+static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * @brief Encode binary value into a base64 string value.
+ *
+ * Reference https://tools.ietf.org/html/rfc4648#section-4
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] data Binary data value.
+ * @param[in] size Size of @p data.
+ * @param[out] str Encoded base64 string.
+ * @param[out] str_len Length of returned @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_encode(const struct ly_ctx *ctx, const char *data, size_t size, char **str, size_t *str_len)
+{
+ uint32_t i;
+ char *ptr;
+
+ *str_len = (size + 2) / 3 * 4;
+ *str = malloc(*str_len + 1);
+ LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
+ if (!(*str_len)) {
+ **str = 0;
+ return LY_SUCCESS;
+ }
+
+ ptr = *str;
+ for (i = 0; i + 2 < size; i += 3) {
+ *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
+ *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
+ *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
+ *ptr++ = b64_etable[data[i + 2] & 0x3F];
+ }
+ if (i < size) {
+ *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
+ if (i == (size - 1)) {
+ *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
+ *ptr++ = '=';
+ } else {
+ *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
+ *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
+ }
+ *ptr++ = '=';
+ }
+ *ptr = '\0';
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief base64 decode table
+ */
+static const int b64_dtable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
+ 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * @brief Decode the binary value from a base64 string value.
+ *
+ * Reference https://tools.ietf.org/html/rfc4648#section-4
+ *
+ * @param[in] value Base64-encoded string value.
+ * @param[in] value_len Length of @p value.
+ * @param[out] data Decoded binary value.
+ * @param[out] size Size of @p data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_decode(const char *value, size_t value_len, void **data, size_t *size)
+{
+ unsigned char *ptr = (unsigned char *)value;
+ uint32_t pad_chars, octet_count;
+ char *str;
+
+ if (!value_len || (ptr[value_len - 1] != '=')) {
+ pad_chars = 0;
+ } else if (ptr[value_len - 2] == '=') {
+ pad_chars = 1;
+ } else {
+ pad_chars = 2;
+ }
+
+ octet_count = ((value_len + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
+ *size = octet_count / 4 * 3 + pad_chars;
+
+ str = malloc(*size + 1);
+ LY_CHECK_RET(!str, LY_EMEM);
+ str[*size] = '\0';
+
+ for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
+ int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
+
+ str[j++] = n >> 16;
+ str[j++] = n >> 8 & 0xFF;
+ str[j++] = n & 0xFF;
+ }
+ if (pad_chars) {
+ int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
+
+ str[*size - pad_chars] = n >> 16;
+
+ if (pad_chars == 2) {
+ n |= b64_dtable[ptr[octet_count + 2]] << 6;
+ n >>= 8 & 0xFF;
+ str[*size - pad_chars + 1] = n;
+ }
+ }
+
+ *data = str;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate a base64 string.
+ *
+ * @param[in] value Value to validate.
+ * @param[in] value_len Length of @p value.
+ * @param[in] type type of the value.
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_validate(const char *value, size_t value_len, const struct lysc_type_bin *type, struct ly_err_item **err)
+{
+ uint32_t idx, pad;
+
+ /* check correct characters in base64 */
+ idx = 0;
+ while ((idx < value_len) &&
+ ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
+ (('a' <= value[idx]) && (value[idx] <= 'z')) ||
+ (('0' <= value[idx]) && (value[idx] <= '9')) ||
+ ('+' == value[idx]) || ('/' == value[idx]))) {
+ idx++;
+ }
+
+ /* find end of padding */
+ pad = 0;
+ while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
+ pad++;
+ }
+
+ /* check if value is valid base64 value */
+ if (value_len != idx + pad) {
+ if (isprint(value[idx + pad])) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
+ }
+ }
+
+ if (value_len & 3) {
+ /* base64 length must be multiple of 4 chars */
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
+ }
+
+ /* length restriction of the binary value */
+ if (type->length) {
+ const uint32_t octet_count = ((idx + pad) / 4) * 3 - pad;
+
+ LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type->length, octet_count, value, value_len, err));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Remove all newlines from a base64 string if present.
+ *
+ * @param[in,out] value Value, may be dynamic and modified.
+ * @param[in,out] value_len Length of @p value, is updated.
+ * @param[in,out] options Type options, are updated.
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
+{
+ char *val;
+ size_t len;
+
+ if ((*value_len < 65) || ((*value)[64] != '\n')) {
+ /* no newlines */
+ return LY_SUCCESS;
+ }
+
+ if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
+ /* make the value dynamic so we can modify it */
+ *value = strndup(*value, *value_len);
+ LY_CHECK_RET(!*value, LY_EMEM);
+ *options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+
+ val = *value;
+ len = *value_len;
+ while (len > 64) {
+ if (val[64] != '\n') {
+ /* missing, error */
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
+ }
+
+ /* remove the newline */
+ memmove(val + 64, val + 65, len - 64);
+ --(*value_len);
+ val += 64;
+ len -= 65;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
+ struct lyd_value_binary *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* store value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val->data = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else if (value_len) {
+ val->data = malloc(value_len);
+ LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
+ memcpy(val->data, value, value_len);
+ } else {
+ val->data = strdup("");
+ LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
+ }
+
+ /* store size */
+ val->size = value_len;
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (format != LY_VALUE_CANON) {
+ /* accept newline every 64 characters (PEM data) */
+ ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* validate */
+ ret = binary_base64_validate(value, value_len, type_bin, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* get the binary value */
+ ret = binary_base64_decode(value, value_len, &val->data, &val->size);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_binary(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_binary *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_binary *val;
+ char *ret;
+ size_t ret_len = 0;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = val->size;
+ }
+ return val->data;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the base64 string value */
+ if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
+ return NULL;
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = ret_len ? ret_len : strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_binary *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
+ LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
+
+ memcpy(dup_val->data, orig_val->data, orig_val->size);
+ dup_val->size = orig_val->size;
+ dup->realtype = original->realtype;
+
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_binary(ctx, dup);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_binary *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ free(val->data);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for binray type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_binary[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_BINARY_STR,
+
+ .plugin.id = "libyang 2 - binary, version 1",
+ .plugin.store = lyplg_type_store_binary,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_binary,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_binary,
+ .plugin.duplicate = lyplg_type_dup_binary,
+ .plugin.free = lyplg_type_free_binary,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
new file mode 100644
index 0000000..04adace
--- /dev/null
+++ b/src/plugins_types/bits.c
@@ -0,0 +1,510 @@
+/**
+ * @file bits.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in bits type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBits bits (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | returned by ::lyplg_type_bits_bitmap_size() | yes | pointer to integer type of the specific size, if size more than 8 use `char *` | bitmap of the set bits |
+ */
+
+/**
+ * @brief Get the position of the last bit.
+ */
+#define BITS_LAST_BIT_POSITION(type_bits) (type_bits->bits[LY_ARRAY_COUNT(type_bits->bits) - 1].position)
+
+/**
+ * @brief Get a specific byte in a bitmap.
+ */
+#ifdef IS_BIG_ENDIAN
+# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + (size - 1) - idx)
+#else
+# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + idx)
+#endif
+
+LIBYANG_API_DEF size_t
+lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type)
+{
+ size_t needed_bytes, size;
+
+ LY_CHECK_ARG_RET(NULL, type, type->basetype == LY_TYPE_BITS, 0);
+
+ /* minimum needed bytes to hold all the bit positions (which start at 0) */
+ needed_bytes = ((BITS_LAST_BIT_POSITION(type) + 1) / 8) + ((BITS_LAST_BIT_POSITION(type) + 1) % 8 ? 1 : 0);
+ LY_CHECK_ERR_RET(!needed_bytes, LOGINT(NULL), 0);
+
+ if ((needed_bytes == 1) || (needed_bytes == 2)) {
+ /* uint8_t or uint16_t */
+ size = needed_bytes;
+ } else if (needed_bytes < 5) {
+ /* uint32_t */
+ size = 4;
+ } else if (needed_bytes < 9) {
+ /* uint64_t */
+ size = 8;
+ } else {
+ /* no basic type, do not round */
+ size = needed_bytes;
+ }
+
+ return size;
+}
+
+LIBYANG_API_DEF ly_bool
+lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position)
+{
+ char bitmask;
+
+ /* find the byte with our bit */
+ (void)size;
+ bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8);
+ bit_position %= 8;
+
+ /* generate bitmask */
+ bitmask = 1;
+ bitmask <<= bit_position;
+
+ /* check if bit set */
+ if (*bitmap & bitmask) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Set bit at a specific position.
+ *
+ * @param[in,out] bitmap Bitmap to modify.
+ * @param[in] size Size of @p bitmap.
+ * @param[in] bit_position Bit position to set.
+ */
+static void
+bits_bit_set(char *bitmap, size_t size, uint32_t bit_position)
+{
+ char bitmask;
+
+ /* find the byte with our bit */
+ (void)size;
+ bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8);
+ bit_position %= 8;
+
+ /* generate bitmask */
+ bitmask = 1;
+ bitmask <<= bit_position;
+
+ /* set the bit */
+ *bitmap |= bitmask;
+}
+
+/**
+ * @brief Convert a list of bit names separated by whitespaces to a bitmap.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] type Type of the value.
+ * @param[in,out] bitmap Zeroed bitmap, is filled (set).
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+bits_str2bitmap(const char *value, size_t value_len, struct lysc_type_bits *type, char *bitmap, struct ly_err_item **err)
+{
+ size_t idx_start, idx_end;
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool found;
+
+ idx_start = idx_end = 0;
+ while (idx_end < value_len) {
+ /* skip whitespaces */
+ while ((idx_end < value_len) && isspace(value[idx_end])) {
+ ++idx_end;
+ }
+ if (idx_end == value_len) {
+ break;
+ }
+
+ /* parse bit name */
+ idx_start = idx_end;
+ while ((idx_end < value_len) && !isspace(value[idx_end])) {
+ ++idx_end;
+ }
+
+ /* find the bit */
+ found = 0;
+ LY_ARRAY_FOR(type->bits, u) {
+ if (!ly_strncmp(type->bits[u].name, value + idx_start, idx_end - idx_start)) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* check if name exists */
+ if (!found) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid bit \"%.*s\".", (int)(idx_end - idx_start),
+ value + idx_start);
+ }
+
+ /* check for duplication */
+ if (lyplg_type_bits_is_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Duplicate bit \"%s\".", type->bits[u].name);
+ }
+
+ /* set the bit */
+ bits_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add a bit item into an array.
+ *
+ * @param[in] position Bit position to add.
+ * @param[in] type Bitis type to read the bit positions and names from.
+ * @param[in,out] items Array of bit item pointers to add to.
+ */
+static void
+bits_add_item(uint32_t position, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* find the bit item */
+ LY_ARRAY_FOR(type->bits, u) {
+ if (type->bits[u].position == position) {
+ break;
+ }
+ }
+
+ /* add it at the end */
+ items[LY_ARRAY_COUNT(items)] = &type->bits[u];
+ LY_ARRAY_INCREMENT(items);
+}
+
+/**
+ * @brief Convert a bitmap to a sized array of pointers to their bit definitions.
+ *
+ * @param[in] bitmap Bitmap to read from.
+ * @param[in] type Bits type.
+ * @param[in,out] items Allocated sized array to fill with the set bits.
+ */
+static void
+bits_bitmap2items(const char *bitmap, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items)
+{
+ size_t i, bitmap_size = lyplg_type_bits_bitmap_size(type);
+ uint32_t bit_pos;
+ uint8_t bitmask;
+ const uint8_t *byte;
+
+ bit_pos = 0;
+ for (i = 0; i < bitmap_size; ++i) {
+ /* check this byte (but not necessarily all bits in the last byte) */
+ byte = (uint8_t *)BITS_BITMAP_BYTE(bitmap, bitmap_size, i);
+ for (bitmask = 1; bitmask; bitmask <<= 1) {
+ if (*byte & bitmask) {
+ /* add this bit */
+ bits_add_item(bit_pos, type, items);
+ }
+
+ if (bit_pos == BITS_LAST_BIT_POSITION(type)) {
+ /* we have checked the last valid bit */
+ break;
+ }
+
+ ++bit_pos;
+ }
+ }
+}
+
+/**
+ * @brief Generate canonical value from ordered array of set bit items.
+ *
+ * @param[in] items Sized array of set bit items.
+ * @param[out] canonical Canonical string value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+bits_items2canon(struct lysc_type_bitenum_item **items, char **canonical)
+{
+ char *ret;
+ size_t ret_len;
+ LY_ARRAY_COUNT_TYPE u;
+
+ *canonical = NULL;
+
+ /* init value */
+ ret = strdup("");
+ LY_CHECK_RET(!ret, LY_EMEM);
+ ret_len = 0;
+
+ LY_ARRAY_FOR(items, u) {
+ if (!ret_len) {
+ ret = ly_realloc(ret, strlen(items[u]->name) + 1);
+ LY_CHECK_RET(!ret, LY_EMEM);
+ strcpy(ret, items[u]->name);
+
+ ret_len = strlen(ret);
+ } else {
+ ret = ly_realloc(ret, ret_len + 1 + strlen(items[u]->name) + 1);
+ LY_CHECK_RET(!ret, LY_EMEM);
+ sprintf(ret + ret_len, " %s", items[u]->name);
+
+ ret_len += 1 + strlen(items[u]->name);
+ }
+ }
+
+ *canonical = ret;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_bits(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type;
+ struct lyd_value_bits *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != lyplg_type_bits_bitmap_size(type_bits)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB bits value size %zu (expected %zu).",
+ value_len, lyplg_type_bits_bitmap_size(type_bits));
+ goto cleanup;
+ }
+
+ /* store value (bitmap) */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val->bitmap = (char *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ val->bitmap = malloc(value_len);
+ LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
+ memcpy(val->bitmap, value, value_len);
+ }
+
+ /* allocate and fill the bit item array */
+ LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup);
+ bits_bitmap2items(val->bitmap, type_bits, val->items);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate the bitmap */
+ val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
+ LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
+ memset(val->bitmap, 0, lyplg_type_bits_bitmap_size(type_bits));
+
+ /* fill the bitmap */
+ ret = bits_str2bitmap(value, value_len, type_bits, val->bitmap, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate and fill the bit item array */
+ LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup);
+ bits_bitmap2items(val->bitmap, type_bits, val->items);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_bits(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_bits *v1, *v2;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(v1->bitmap, v2->bitmap, lyplg_type_bits_bitmap_size(type_bits))) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_bits(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)value->realtype;
+ struct lyd_value_bits *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = lyplg_type_bits_bitmap_size(type_bits);
+ }
+ return val->bitmap;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the canonical value */
+ if (bits_items2canon(val->items, &ret)) {
+ return NULL;
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)original->realtype;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_value_bits *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ /* duplicate bitmap */
+ dup_val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
+ LY_CHECK_ERR_GOTO(!dup_val->bitmap, ret = LY_EMEM, error);
+ memcpy(dup_val->bitmap, orig_val->bitmap, lyplg_type_bits_bitmap_size(type_bits));
+
+ /* duplicate bit item pointers */
+ LY_ARRAY_CREATE_GOTO(ctx, dup_val->items, LY_ARRAY_COUNT(orig_val->items), ret, error);
+ LY_ARRAY_FOR(orig_val->items, u) {
+ LY_ARRAY_INCREMENT(dup_val->items);
+ dup_val->items[u] = orig_val->items[u];
+ }
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_bits(ctx, dup);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_bits *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ free(val->bitmap);
+ LY_ARRAY_FREE(val->items);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for bits type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_bits[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_BITS_STR,
+
+ .plugin.id = "libyang 2 - bits, version 1",
+ .plugin.store = lyplg_type_store_bits,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_bits,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_bits,
+ .plugin.duplicate = lyplg_type_dup_bits,
+ .plugin.free = lyplg_type_free_bits,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
new file mode 100644
index 0000000..f8b19f6
--- /dev/null
+++ b/src/plugins_types/boolean.c
@@ -0,0 +1,165 @@
+/**
+ * @file boolean.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in boolean type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBoolean boolean (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 1 | yes | `int8_t *` | 0 for false, otherwise true |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_boolean(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ int8_t i;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB boolean value size %zu (expected 1).",
+ value_len);
+ goto cleanup;
+ }
+
+ /* store value */
+ i = *(int8_t *)value;
+ storage->boolean = i ? 1 : 0;
+
+ /* store canonical value, it always is */
+ ret = lydict_insert(ctx, i ? "true" : "false", 0, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* validate and store the value */
+ if ((value_len == ly_strlen_const("true")) && !strncmp(value, "true", ly_strlen_const("true"))) {
+ i = 1;
+ } else if ((value_len == ly_strlen_const("false")) && !strncmp(value, "false", ly_strlen_const("false"))) {
+ i = 0;
+ } else {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid boolean value \"%.*s\".", (int)value_len,
+ (char *)value);
+ goto cleanup;
+ }
+ storage->boolean = i;
+
+ /* store canonical value, it always is */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((char *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_boolean(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->boolean != val2->boolean) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_boolean(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof value->boolean;
+ }
+ return &value->boolean;
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for boolean type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_boolean[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_BOOL_STR,
+
+ .plugin.id = "libyang 2 - boolean, version 1",
+ .plugin.store = lyplg_type_store_boolean,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_boolean,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_boolean,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
new file mode 100644
index 0000000..5ccb86d
--- /dev/null
+++ b/src/plugins_types/date_and_time.c
@@ -0,0 +1,339 @@
+/**
+ * @file date_and_time.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-yang-types date-and-time type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDateAndTime date-and-time (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `time_t *` | UNIX timestamp |
+ * | 1 | no | `int8_t *` | flag whether the value is in the special -00:00 unknown timezone or not |
+ * | string length | no | `char *` | string with the fraction digits of a second |
+ */
+
+static void lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_dat = (struct lysc_type_str *)type;
+ struct lyd_value_date_and_time *val;
+ struct tm tm;
+ uint32_t i;
+ char c;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 8) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time value size %zu "
+ "(expected at least 8).", value_len);
+ goto cleanup;
+ }
+ for (i = 9; i < value_len; ++i) {
+ c = ((char *)value)[i];
+ if (!isdigit(c)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time character '%c' "
+ "(expected a digit).", c);
+ goto cleanup;
+ }
+ }
+
+ /* store timestamp */
+ memcpy(&val->time, value, sizeof val->time);
+
+ /* store fractions of second */
+ if (value_len > 9) {
+ val->fractions_s = strndup(((char *)value) + 9, value_len - 9);
+ LY_CHECK_ERR_GOTO(!val->fractions_s, ret = LY_EMEM, cleanup);
+ }
+
+ /* store unknown timezone */
+ if (value_len > 8) {
+ val->unknown_tz = *(((int8_t *)value) + 8) ? 1 : 0;
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction, there can be only ASCII chars */
+ if (type_dat->length) {
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_dat->length, value_len, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* date-and-time pattern */
+ ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* pattern validation succeeded, convert to UNIX time and fractions of second */
+ ret = ly_time_str2time(value, &val->time, &val->fractions_s);
+ LY_CHECK_GOTO(ret, cleanup);
+
+#ifdef HAVE_TIME_H_TIMEZONE
+ if (!strncmp(((char *)value + value_len) - 6, "-00:00", 6)) {
+ /* unknown timezone, move the timestamp to UTC */
+ tzset();
+ val->time += (time_t)timezone;
+ val->unknown_tz = 1;
+
+ /* DST may apply, adjust accordingly */
+ if (!localtime_r(&val->time, &tm)) {
+ ret = ly_err_new(err, LY_ESYS, LYVE_DATA, NULL, NULL, "localtime_r() call failed (%s).", strerror(errno));
+ goto cleanup;
+ } else if (tm.tm_isdst < 0) {
+ ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Failed to get DST information.");
+ goto cleanup;
+ }
+ if (tm.tm_isdst) {
+ /* move an hour back */
+ val->time -= 3600;
+ }
+ }
+#else
+ (void)tm;
+ val->unknown_tz = 1;
+#endif
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_date_and_time(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_compare_date_and_time(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_date_and_time *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ /* compare timestamp and unknown tz */
+ if ((v1->time != v2->time) || (v1->unknown_tz != v2->unknown_tz)) {
+ return LY_ENOT;
+ }
+
+ /* compare second fractions */
+ if ((!v1->fractions_s && !v2->fractions_s) ||
+ (v1->fractions_s && v2->fractions_s && !strcmp(v1->fractions_s, v2->fractions_s))) {
+ return LY_SUCCESS;
+ }
+ return LY_ENOT;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_date_and_time *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ if (val->unknown_tz || val->fractions_s) {
+ ret = malloc(8 + 1 + (val->fractions_s ? strlen(val->fractions_s) : 0));
+ LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = 8 + 1 + (val->fractions_s ? strlen(val->fractions_s) : 0);
+ }
+ memcpy(ret, &val->time, sizeof val->time);
+ memcpy(ret + 8, &val->unknown_tz, sizeof val->unknown_tz);
+ if (val->fractions_s) {
+ memcpy(ret + 9, val->fractions_s, strlen(val->fractions_s));
+ }
+ } else {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 8;
+ }
+ ret = (char *)&val->time;
+ }
+ return ret;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the canonical value */
+ if (ly_time_time2str(val->time, val->fractions_s, &ret)) {
+ return NULL;
+ }
+
+ if (val->unknown_tz) {
+ /* date and time is correct, fix only the timezone */
+ strcpy((ret + strlen(ret)) - 6, "-00:00");
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_dup_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_date_and_time *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ /* copy timestamp and unknown tz */
+ dup_val->time = orig_val->time;
+ dup_val->unknown_tz = orig_val->unknown_tz;
+
+ /* duplicate second fractions */
+ if (orig_val->fractions_s) {
+ dup_val->fractions_s = strdup(orig_val->fractions_s);
+ LY_CHECK_ERR_GOTO(!dup_val->fractions_s, ret = LY_EMEM, error);
+ }
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_date_and_time(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for ietf-yang-types date-and-time type.
+ */
+static void
+lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_date_and_time *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ free(val->fractions_s);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for date-and-time type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_date_and_time[] = {
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "date-and-time",
+
+ .plugin.id = "libyang 2 - date-and-time, version 1",
+ .plugin.store = lyplg_type_store_date_and_time,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_date_and_time,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_date_and_time,
+ .plugin.duplicate = lyplg_type_dup_date_and_time,
+ .plugin.free = lyplg_type_free_date_and_time,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
new file mode 100644
index 0000000..25a88d9
--- /dev/null
+++ b/src/plugins_types/decimal64.c
@@ -0,0 +1,239 @@
+/**
+ * @file decimal64.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in decimal64 type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDecimal64 decimal64 (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `int64_t *` | little-endian value represented without floating point |
+ */
+
+/**
+ * @brief Convert decimal64 number to canonical string.
+ *
+ * @param[in] num Decimal64 number stored in int64.
+ * @param[in] type Decimal64 type with fraction digits.
+ * @param[out] str Canonical string value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+decimal64_num2str(int64_t num, struct lysc_type_dec *type, char **str)
+{
+ char *ret;
+
+ /* allocate the value */
+ ret = calloc(1, LY_NUMBER_MAXLEN);
+ LY_CHECK_RET(!ret, LY_EMEM);
+
+ if (num) {
+ int count = sprintf(ret, "%" PRId64 " ", num);
+
+ if (((num > 0) && ((count - 1) <= type->fraction_digits)) || ((count - 2) <= type->fraction_digits)) {
+ /* we have 0. value, print the value with the leading zeros
+ * (one for 0. and also keep the correct with of num according
+ * to fraction-digits value)
+ * for (num < 0) - extra character for '-' sign */
+ count = sprintf(ret, "%0*" PRId64 " ", (num > 0) ? (type->fraction_digits + 1) : (type->fraction_digits + 2), num);
+ }
+ for (uint8_t i = type->fraction_digits, j = 1; i > 0; i--) {
+ if (j && (i > 1) && (ret[count - 2] == '0')) {
+ /* we have trailing zero to skip */
+ ret[count - 1] = '\0';
+ } else {
+ j = 0;
+ ret[count - 1] = ret[count - 2];
+ }
+ count--;
+ }
+ ret[count - 1] = '.';
+ } else {
+ /* zero */
+ sprintf(ret, "0.0");
+ }
+
+ *str = ret;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ struct lysc_type_dec *type_dec = (struct lysc_type_dec *)type;
+ LY_ERR ret = LY_SUCCESS;
+ int64_t num;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 8) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB decimal64 value size %zu (expected 8).",
+ value_len);
+ goto cleanup;
+ }
+
+ /* we have the decimal64 number, in host byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse decimal64 value */
+ ret = lyplg_type_parse_dec64(type_dec->fraction_digits, value, value_len, &num, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* store value */
+ storage->dec64 = num;
+
+ /* we need canonical value for the range check */
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* generate canonical value */
+ ret = decimal64_num2str(num, type_dec, &canon);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store it */
+ ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ if (type_dec->range) {
+ /* check range of the number */
+ ret = lyplg_type_validate_range(type->basetype, type_dec->range, num, storage->_canonical,
+ strlen(storage->_canonical), err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_decimal64(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ /* if type is the same, the fraction digits are, too */
+ if (val1->dec64 != val2->dec64) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_decimal64(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ int64_t num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ num = htole64(value->dec64);
+ if (num == value->dec64) {
+ /* values are equal, little-endian */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof value->dec64;
+ }
+ return &value->dec64;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, sizeof value->dec64);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = sizeof value->dec64;
+ }
+ memcpy(buf, &num, sizeof value->dec64);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for decimal64 type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_decimal64[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_DEC64_STR,
+
+ .plugin.id = "libyang 2 - decimal64, version 1",
+ .plugin.store = lyplg_type_store_decimal64,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_decimal64,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_decimal64,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 8,
+ },
+ {0}
+};
diff --git a/src/plugins_types/empty.c b/src/plugins_types/empty.c
new file mode 100644
index 0000000..d84634f
--- /dev/null
+++ b/src/plugins_types/empty.c
@@ -0,0 +1,103 @@
+/**
+ * @file empty.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in empty type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEmpty empty (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 0 | yes | `void` | none |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_empty(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* validation */
+ if (value_len) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty value length %zu.", value_len);
+ goto cleanup;
+ }
+
+ /* store canonical value, it always is */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for empty type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_empty[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_EMPTY_STR,
+
+ .plugin.id = "libyang 2 - empty, version 1",
+ .plugin.store = lyplg_type_store_empty,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 0,
+ },
+ {0}
+};
diff --git a/src/plugins_types/enumeration.c b/src/plugins_types/enumeration.c
new file mode 100644
index 0000000..91ca822
--- /dev/null
+++ b/src/plugins_types/enumeration.c
@@ -0,0 +1,202 @@
+/**
+ * @file enumeration.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in enumeration type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEnumeration enumeration (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `int32 *` | assigned little-endian value of the enum |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_enum(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool found = 0;
+ int64_t num = 0;
+ int32_t num_val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB enumeration value size %zu (expected 4).",
+ value_len);
+ goto cleanup;
+ }
+
+ /* convert the value to host byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ num_val = num;
+
+ /* find the matching enumeration value item */
+ LY_ARRAY_FOR(type_enum->enums, u) {
+ if (type_enum->enums[u].value == num_val) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* value not found */
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value % " PRIi32 ".", num_val);
+ goto cleanup;
+ }
+
+ /* store value */
+ storage->enum_item = &type_enum->enums[u];
+
+ /* canonical settings via dictionary due to free callback */
+ ret = lydict_insert(ctx, type_enum->enums[u].name, 0, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* find the matching enumeration value item */
+ LY_ARRAY_FOR(type_enum->enums, u) {
+ if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* enum not found */
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value \"%.*s\".", (int)value_len,
+ (char *)value);
+ goto cleanup;
+ }
+
+ /* store value */
+ storage->enum_item = &type_enum->enums[u];
+
+ /* store canonical value, it always is */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_enum(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ int64_t prev_num = 0, num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ prev_num = num = value->enum_item->value;
+ num = htole64(num);
+ if (num == prev_num) {
+ /* values are equal, little-endian */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 4;
+ }
+ return &value->enum_item->value;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, 4);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = 4;
+ }
+ memcpy(buf, &num, 4);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for enumeration type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_enumeration[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_ENUM_STR,
+
+ .plugin.id = "libyang 2 - enumeration, version 1",
+ .plugin.store = lyplg_type_store_enum,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_enum,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ },
+ {0}
+};
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
new file mode 100644
index 0000000..8b7985d
--- /dev/null
+++ b/src/plugins_types/identityref.c
@@ -0,0 +1,352 @@
+/**
+ * @file identityref.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in identityref type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIdentityref identityref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the identityref |
+ */
+
+/**
+ * @brief Print an identityref value in a specific format.
+ *
+ * @param[in] ident Identityref to print.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed identityref.
+ * @param[out] str_len Optional length of @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_ident2str(const struct lysc_ident *ident, LY_VALUE_FORMAT format, void *prefix_data, char **str, size_t *str_len)
+{
+ int len;
+
+ len = asprintf(str, "%s:%s", lyplg_type_get_prefix(ident->module, format, prefix_data), ident->name);
+ if (len == -1) {
+ return LY_EMEM;
+ }
+
+ if (str_len) {
+ *str_len = (size_t)len;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Convert a string identityref value to matching identity.
+ *
+ * @param[in] value Identityref value.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Context node for resolving the prefixes.
+ * @param[out] ident Found identity.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_str2ident(const char *value, size_t value_len, LY_VALUE_FORMAT format, void *prefix_data,
+ const struct ly_ctx *ctx, const struct lysc_node *ctx_node, struct lysc_ident **ident, struct ly_err_item **err)
+{
+ const char *id_name, *prefix = value;
+ size_t id_len, prefix_len;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_ident *id, *identities;
+
+ /* locate prefix if any */
+ for (prefix_len = 0; (prefix_len < value_len) && (value[prefix_len] != ':'); ++prefix_len) {}
+ if (prefix_len < value_len) {
+ id_name = &value[prefix_len + 1];
+ id_len = value_len - (prefix_len + 1);
+ } else {
+ prefix_len = 0;
+ id_name = value;
+ id_len = value_len;
+ }
+
+ if (!id_len) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty identityref value.");
+ }
+
+ mod = lyplg_type_identity_module(ctx, ctx_node, prefix, prefix_len, format, prefix_data);
+ if (!mod) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value);
+ }
+
+ id = NULL;
+ identities = mod->identities;
+ LY_ARRAY_FOR(identities, u) {
+ if (!ly_strncmp(identities[u].name, id_name, id_len)) {
+ /* we have match */
+ id = &identities[u];
+ break;
+ }
+ }
+ if (!id) {
+ /* no match */
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity not found in module \"%s\".",
+ (int)value_len, value, mod->name);
+ }
+
+ *ident = id;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that an identityref is derived from the type base.
+ *
+ * @param[in] ident Derived identity to which identityref points.
+ * @param[in] type Identityref type.
+ * @param[in] value String value for logging.
+ * @param[in] value_len Length of @p value.
+ * @param[out] err Error information on error.
+ */
+static LY_ERR
+identityref_check_base(const struct lysc_ident *ident, struct lysc_type_identityref *type, const char *value,
+ size_t value_len, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ size_t str_len;
+ char *str;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_ident *base;
+
+ /* check that the identity matches some of the type's base identities */
+ LY_ARRAY_FOR(type->bases, u) {
+ if (!lyplg_type_identity_isderived(type->bases[u], ident)) {
+ /* we have match */
+ break;
+ }
+ }
+
+ /* it does not, generate a nice error */
+ if (u == LY_ARRAY_COUNT(type->bases)) {
+ str = NULL;
+ str_len = 1;
+ LY_ARRAY_FOR(type->bases, u) {
+ base = type->bases[u];
+ str_len += (u ? 2 : 0) + 1 + strlen(base->module->name) + 1 + strlen(base->name) + 1;
+ str = ly_realloc(str, str_len);
+ sprintf(str + (u ? strlen(str) : 0), "%s\"%s:%s\"", u ? ", " : "", base->module->name, base->name);
+ }
+
+ /* no match */
+ if (u == 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity not derived from the base %s.",
+ (int)value_len, value, str);
+ } else {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity not derived from all the bases %s.",
+ (int)value_len, value, str);
+ }
+ free(str);
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check if @p ident is not disabled.
+ *
+ * Identity is disabled if it is located in an unimplemented model or
+ * it can be disabled by if-feature. Calling this function may invoke
+ * the implementation of another module.
+ *
+ * @param[in] ident Derived identity to which identityref points.
+ * @param[in] value Value of identityref.
+ * @param[in] value_len Length (number of bytes) of the given @p value.
+ * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_check_ident(const struct lysc_ident *ident, const char *value,
+ size_t value_len, uint32_t options, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (!ident->module->implemented) {
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ ret = lyplg_type_make_implemented(ident->module, NULL, unres);
+ } else {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
+ (int)value_len, (char *)value, ident->module->name);
+ }
+ } else if (lys_identity_iffeature_value(ident) == LY_ENOT) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity is disabled by if-feature.",
+ (int)value_len, value);
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
+ char *canon;
+ struct lysc_ident *ident = NULL;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* find a matching identity */
+ ret = identityref_str2ident(value, value_len, format, prefix_data, ctx, ctx_node, &ident, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* check if the identity is enabled */
+ ret = identityref_check_ident(ident, value, value_len, options, unres, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* check that the identity is derived form all the bases */
+ ret = identityref_check_base(ident, type_ident, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (ctx_node) {
+ /* check status */
+ ret = lyplg_type_check_status(ctx_node, ident->flags, format, prefix_data, ident->name, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* store value */
+ storage->ident = ident;
+
+ /* store canonical value */
+ if (format == LY_VALUE_CANON) {
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* JSON format with prefix is the canonical one */
+ ret = identityref_ident2str(ident, LY_VALUE_JSON, NULL, &canon, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->ident == val2->ident) {
+ return LY_SUCCESS;
+ }
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ char *ret;
+
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+ }
+
+ /* print the value in the specific format */
+ if (identityref_ident2str(value->ident, format, prefix_data, &ret, value_len)) {
+ return NULL;
+ }
+ *dynamic = 1;
+ return ret;
+}
+
+/**
+ * @brief Plugin information for identityref type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_identityref[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_IDENT_STR,
+
+ .plugin.id = "libyang 2 - identityref, version 1",
+ .plugin.store = lyplg_type_store_identityref,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_identityref,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_identityref,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
new file mode 100644
index 0000000..d151d0a
--- /dev/null
+++ b/src/plugins_types/instanceid.c
@@ -0,0 +1,382 @@
+/**
+ * @file instanceid.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in instance-identifier type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "path.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInstanceIdentifier instance-identifier (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier |
+ */
+
+/**
+ * @brief Convert compiled path (instance-identifier) into string.
+ *
+ * @param[in] path Compiled path.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed instance-identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *prefix_data, char **str)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ char *result = NULL, quot;
+ const struct lys_module *mod = NULL;
+ ly_bool inherit_prefix = 0, d;
+ const char *strval;
+
+ switch (format) {
+ case LY_VALUE_XML:
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* everything is prefixed */
+ inherit_prefix = 0;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* the same prefix is inherited and skipped */
+ inherit_prefix = 1;
+ break;
+ }
+
+ LY_ARRAY_FOR(path, u) {
+ /* new node */
+ if (!inherit_prefix || (mod != path[u].node->module)) {
+ mod = path[u].node->module;
+ ret = ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), path[u].node->name);
+ } else {
+ ret = ly_strcat(&result, "/%s", path[u].node->name);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* node predicates */
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ struct ly_path_predicate *pred = &path[u].predicates[v];
+
+ switch (path[u].pred_type) {
+ case LY_PATH_PREDTYPE_NONE:
+ break;
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* key-predicate */
+ strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+ &d, NULL);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ if (inherit_prefix) {
+ /* always the same prefix as the parent */
+ ret = ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, strval, quot);
+ } else {
+ ret = ly_strcat(&result, "[%s:%s=%c%s%c]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
+ pred->key->name, quot, strval, quot);
+ }
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list-predicate */
+ strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+ &d, NULL);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ ret = ly_strcat(&result, "[.=%c%s%c]", quot, strval, quot);
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ }
+
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ free(result);
+ } else {
+ *str = result;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
+ struct ly_path *path;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compile instance-identifier into path */
+ if (format == LY_VALUE_LYB) {
+ /* The @p value in LYB format is the same as in JSON format. */
+ ret = lyplg_type_lypath_new(ctx, value, value_len, options, LY_VALUE_JSON, prefix_data, ctx_node,
+ unres, &path, err);
+ } else {
+ ret = lyplg_type_lypath_new(ctx, value, value_len, options, format, prefix_data, ctx_node,
+ unres, &path, err);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store value */
+ storage->target = path;
+
+ /* check status */
+ ret = lyplg_type_lypath_check_status(ctx_node, path, format, prefix_data, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store canonical value */
+ if (format == LY_VALUE_CANON) {
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* JSON format with prefix is the canonical one */
+ ret = instanceid_path2str(path, LY_VALUE_JSON, NULL, &canon);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_instanceid(ctx, storage);
+ }
+ if (!ret && type_inst->require_instance) {
+ /* needs to be resolved */
+ return LY_EINCOMPLETE;
+ } else {
+ return ret;
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type *UNUSED(type),
+ const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage,
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)storage->realtype;
+ const char *value;
+ char *path;
+
+ *err = NULL;
+
+ if (!type_inst->require_instance) {
+ /* redundant to resolve */
+ return LY_SUCCESS;
+ }
+
+ /* find the target in data */
+ if ((ret = ly_path_eval(storage->target, tree, NULL))) {
+ value = lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+ path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
+ return ly_err_new(err, ret, LYVE_DATA, path, strdup("instance-required"), LY_ERRMSG_NOINST, value);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1 == val2) {
+ return LY_SUCCESS;
+ } else if (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target)) {
+ return LY_ENOT;
+ }
+
+ LY_ARRAY_FOR(val1->target, u) {
+ struct ly_path *s1 = &val1->target[u];
+ struct ly_path *s2 = &val2->target[u];
+
+ if ((s1->node != s2->node) || (s1->pred_type != s2->pred_type) ||
+ (s1->predicates && (LY_ARRAY_COUNT(s1->predicates) != LY_ARRAY_COUNT(s2->predicates)))) {
+ return LY_ENOT;
+ }
+ if (s1->predicates) {
+ LY_ARRAY_FOR(s1->predicates, v) {
+ struct ly_path_predicate *pred1 = &s1->predicates[v];
+ struct ly_path_predicate *pred2 = &s2->predicates[v];
+
+ switch (s1->pred_type) {
+ case LY_PATH_PREDTYPE_NONE:
+ break;
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ if (pred1->position != pred2->position) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* key-predicate */
+ if ((pred1->key != pred2->key) ||
+ ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list predicate */
+ if (((struct lysc_node_leaflist *)s1->node)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ char *ret;
+
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+ }
+
+ /* print the value in the specific format */
+ if (instanceid_path2str(value->target, format, prefix_data, &ret)) {
+ return NULL;
+ }
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* canonical value */
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* copy path */
+ ret = ly_path_dup(ctx, original->target, &dup->target);
+ LY_CHECK_GOTO(ret, error);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_instanceid(ctx, dup);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ ly_path_free(ctx, value->target);
+}
+
+/**
+ * @brief Plugin information for instance-identifier type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_instanceid[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INST_STR,
+
+ .plugin.id = "libyang 2 - instance-identifier, version 1",
+ .plugin.store = lyplg_type_store_instanceid,
+ .plugin.validate = lyplg_type_validate_instanceid,
+ .plugin.compare = lyplg_type_compare_instanceid,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_instanceid,
+ .plugin.duplicate = lyplg_type_dup_instanceid,
+ .plugin.free = lyplg_type_free_instanceid,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c
new file mode 100644
index 0000000..0cd08f7
--- /dev/null
+++ b/src/plugins_types/instanceid_keys.c
@@ -0,0 +1,229 @@
+/**
+ * @file instanceid_keys.c
+ * @author Michal Basko <mvasko@cesnet.cz>
+ * @brief ietf-netconf edit-config key metadata instance-identifier keys predicate type plugin.
+ *
+ * Copyright (c) 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "path.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+#include "xpath.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInstanceIdentifierKeys instance-identifier-keys (yang)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier keys predicate |
+ */
+
+/**
+ * @brief Special lyd_value structure for yang instance-identifier-keys values.
+ */
+struct lyd_value_instance_identifier_keys {
+ struct lyxp_expr *keys;
+ const struct ly_ctx *ctx;
+ void *prefix_data;
+ LY_VALUE_FORMAT format;
+};
+
+/**
+ * @brief Print instance-id-keys value in the specific format.
+ *
+ * @param[in] val instance-id-keys value structure.
+ * @param[in] format Format to print in.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[out] str_value Printed value.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val, LY_VALUE_FORMAT format, void *prefix_data,
+ char **str_value, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t cur_idx = 0, str_len = 0;
+ enum lyxp_token cur_tok;
+ char *str_tok;
+ void *mem;
+ const char *cur_exp_ptr;
+ ly_bool is_nt;
+ const struct lys_module *context_mod = NULL;
+
+ *str_value = NULL;
+
+ while (cur_idx < val->keys->used) {
+ cur_tok = val->keys->tokens[cur_idx];
+ cur_exp_ptr = val->keys->expr + val->keys->tok_pos[cur_idx];
+
+ if ((cur_tok == LYXP_TOKEN_NAMETEST) || (cur_tok == LYXP_TOKEN_LITERAL)) {
+ /* tokens that may include prefixes, get them in the target format */
+ is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
+ LY_CHECK_GOTO(ret = lyplg_type_xpath10_print_token(cur_exp_ptr, val->keys->tok_len[cur_idx], is_nt, &context_mod,
+ val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), error);
+
+ /* append the converted token */
+ mem = realloc(*str_value, str_len + strlen(str_tok) + 1);
+ LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ *str_value = mem;
+ str_len += sprintf(*str_value + str_len, "%s", str_tok);
+ free(str_tok);
+
+ /* token processed */
+ ++cur_idx;
+ } else {
+ /* just copy the token */
+ mem = realloc(*str_value, str_len + val->keys->tok_len[cur_idx] + 1);
+ LY_CHECK_GOTO(!mem, error_mem);
+ *str_value = mem;
+ str_len += sprintf(*str_value + str_len, "%.*s", (int)val->keys->tok_len[cur_idx], cur_exp_ptr);
+
+ /* token processed */
+ ++cur_idx;
+ }
+ }
+
+ return LY_SUCCESS;
+
+error_mem:
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+
+error:
+ free(*str_value);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the instance-identifier-keys yang type.
+ */
+static LY_ERR
+lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *UNUSED(ctx_node),
+ struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres), struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_instance_identifier_keys *val;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse instance-identifier keys, with optional prefix even though it should be mandatory */
+ if (value_len && (((char *)value)[0] != '[')) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid first character '%c', list key predicates expected.",
+ ((char *)value)[0]);
+ goto cleanup;
+ }
+ ret = ly_path_parse_predicate(ctx, NULL, value_len ? value : "", value_len, LY_PATH_PREFIX_OPTIONAL,
+ LY_PATH_PRED_KEYS, &val->keys);
+ if (ret) {
+ ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL, "%s", ly_errmsg(ctx));
+ goto cleanup;
+ }
+ val->ctx = ctx;
+
+ /* store format-specific data and context for later prefix resolution */
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ switch (format) {
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ break;
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ /* JSON format with prefix is the canonical one */
+ ret = instanceid_keys_print_value(val, LY_VALUE_JSON, NULL, &canon, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ break;
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_xpath10(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for instance-identifier type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_instanceid_keys[] = {
+ {
+ .module = "yang",
+ .revision = NULL,
+ .name = "instance-identifier-keys",
+
+ .plugin.id = "libyang 2 - instance-identifier-keys, version 1",
+ .plugin.store = lyplg_type_store_instanceid_keys,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_xpath10,
+ .plugin.duplicate = lyplg_type_dup_xpath10,
+ .plugin.free = lyplg_type_free_xpath10,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
new file mode 100644
index 0000000..0903365
--- /dev/null
+++ b/src/plugins_types/integer.c
@@ -0,0 +1,585 @@
+/**
+ * @file integer.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in integer types plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInteger (u)int(8/16/32/64) (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 1/2/4/8 | yes | pointer to the specific integer type | little-endian integer value |
+ */
+
+/**
+ * @brief LYB value size of each integer type.
+ */
+static size_t integer_lyb_size[] = {
+ [LY_TYPE_INT8] = 1, [LY_TYPE_INT16] = 2, [LY_TYPE_INT32] = 4, [LY_TYPE_INT64] = 8,
+ [LY_TYPE_UINT8] = 1, [LY_TYPE_UINT16] = 2, [LY_TYPE_UINT32] = 4, [LY_TYPE_UINT64] = 8
+};
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_int(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ int64_t num = 0;
+ int base = 1;
+ char *canon = NULL;
+ struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != integer_lyb_size[type->basetype]) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB signed integer value size %zu (expected %zu).",
+ value_len, integer_lyb_size[type->basetype]);
+ goto cleanup;
+ }
+
+ /* copy the integer and correct the byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse the integer */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ ret = lyplg_type_parse_int("int8", base, INT64_C(-128), INT64_C(127), value, value_len, &num, err);
+ break;
+ case LY_TYPE_INT16:
+ ret = lyplg_type_parse_int("int16", base, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err);
+ break;
+ case LY_TYPE_INT32:
+ ret = lyplg_type_parse_int("int32", base, INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num, err);
+ break;
+ case LY_TYPE_INT64:
+ ret = lyplg_type_parse_int("int64", base, INT64_C(-9223372036854775807) - INT64_C(1),
+ INT64_C(9223372036854775807), value, value_len, &num, err);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* set the value (matters for big-endian) and get the correct int64 number */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ storage->int8 = num;
+ num = storage->int8;
+ break;
+ case LY_TYPE_INT16:
+ storage->int16 = num;
+ num = storage->int16;
+ break;
+ case LY_TYPE_INT32:
+ storage->int32 = num;
+ num = storage->int32;
+ break;
+ case LY_TYPE_INT64:
+ storage->int64 = num;
+ num = storage->int64;
+ break;
+ default:
+ break;
+ }
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* generate canonical value */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId8, storage->int8) == -1, ret = LY_EMEM, cleanup);
+ break;
+ case LY_TYPE_INT16:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId16, storage->int16) == -1, ret = LY_EMEM, cleanup);
+ break;
+ case LY_TYPE_INT32:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId32, storage->int32) == -1, ret = LY_EMEM, cleanup);
+ break;
+ case LY_TYPE_INT64:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId64, storage->int64) == -1, ret = LY_EMEM, cleanup);
+ break;
+ default:
+ break;
+ }
+
+ /* store it */
+ ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* validate range of the number */
+ if (type_num->range) {
+ ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical,
+ strlen(storage->_canonical), err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_int(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ switch (val1->realtype->basetype) {
+ case LY_TYPE_INT8:
+ if (val1->int8 != val2->int8) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_INT16:
+ if (val1->int16 != val2->int16) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_INT32:
+ if (val1->int32 != val2->int32) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_INT64:
+ if (val1->int64 != val2->int64) {
+ return LY_ENOT;
+ }
+ break;
+ default:
+ break;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_int(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ int64_t prev_num = 0, num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ switch (value->realtype->basetype) {
+ case LY_TYPE_INT8:
+ prev_num = num = value->int8;
+ break;
+ case LY_TYPE_INT16:
+ prev_num = num = value->int16;
+ break;
+ case LY_TYPE_INT32:
+ prev_num = num = value->int32;
+ break;
+ case LY_TYPE_INT64:
+ prev_num = num = value->int64;
+ break;
+ default:
+ break;
+ }
+ num = htole64(num);
+ if (num == prev_num) {
+ /* values are equal, little-endian or int8 */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ return &value->int64;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, integer_lyb_size[value->realtype->basetype]);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ memcpy(buf, &num, integer_lyb_size[value->realtype->basetype]);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_uint(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint64_t num = 0;
+ int base = 0;
+ char *canon;
+ struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != integer_lyb_size[type->basetype]) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB unsigned integer value size %zu (expected %zu).",
+ value_len, integer_lyb_size[type->basetype]);
+ goto cleanup;
+ }
+
+ /* copy the integer and correct the byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse the integer */
+ switch (type->basetype) {
+ case LY_TYPE_UINT8:
+ ret = lyplg_type_parse_uint("uint8", base, UINT64_C(255), value, value_len, &num, err);
+ break;
+ case LY_TYPE_UINT16:
+ ret = lyplg_type_parse_uint("uint16", base, UINT64_C(65535), value, value_len, &num, err);
+ break;
+ case LY_TYPE_UINT32:
+ ret = lyplg_type_parse_uint("uint32", base, UINT64_C(4294967295), value, value_len, &num, err);
+ break;
+ case LY_TYPE_UINT64:
+ ret = lyplg_type_parse_uint("uint64", base, UINT64_C(18446744073709551615), value, value_len, &num, err);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* store value, matters for big-endian */
+ switch (type->basetype) {
+ case LY_TYPE_UINT8:
+ storage->uint8 = num;
+ break;
+ case LY_TYPE_UINT16:
+ storage->uint16 = num;
+ break;
+ case LY_TYPE_UINT32:
+ storage->uint32 = num;
+ break;
+ case LY_TYPE_UINT64:
+ storage->uint64 = num;
+ break;
+ default:
+ break;
+ }
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* generate canonical value */
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRIu64, num) == -1, ret = LY_EMEM, cleanup);
+
+ /* store it */
+ ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* validate range of the number */
+ if (type_num->range) {
+ ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical,
+ strlen(storage->_canonical), err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ switch (val1->realtype->basetype) {
+ case LY_TYPE_UINT8:
+ if (val1->uint8 != val2->uint8) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_UINT16:
+ if (val1->uint16 != val2->uint16) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_UINT32:
+ if (val1->uint32 != val2->uint32) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_UINT64:
+ if (val1->uint64 != val2->uint64) {
+ return LY_ENOT;
+ }
+ break;
+ default:
+ break;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_uint(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ uint64_t num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ switch (value->realtype->basetype) {
+ case LY_TYPE_UINT8:
+ num = value->uint8;
+ break;
+ case LY_TYPE_UINT16:
+ num = value->uint16;
+ break;
+ case LY_TYPE_UINT32:
+ num = value->uint32;
+ break;
+ case LY_TYPE_UINT64:
+ num = value->uint64;
+ break;
+ default:
+ break;
+ }
+ num = htole64(num);
+ if (num == value->uint64) {
+ /* values are equal, little-endian or uint8 */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ return &value->uint64;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, integer_lyb_size[value->realtype->basetype]);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ memcpy(buf, &num, integer_lyb_size[value->realtype->basetype]);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for integer types implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_integer[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT8_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 1,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT16_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 2,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT32_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT64_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 8,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT8_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 1,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT16_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 2,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT32_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT64_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 8,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
new file mode 100644
index 0000000..f7b297c
--- /dev/null
+++ b/src/plugins_types/ipv4_address.c
@@ -0,0 +1,377 @@
+/**
+ * @file ipv4_address.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-address type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4Address ipv4-address (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ * | string length | no | `char *` | IPv4 address zone string |
+ */
+
+static void lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with optional zone to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in] ctx libyang context with dictionary.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] zone Ipv6 address zone in dictionary.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv4address_str2ip(const char *value, size_t value_len, uint32_t options, const struct ly_ctx *ctx,
+ struct in_addr *addr, const char **zone, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *addr_no_zone;
+ char *zone_ptr = NULL, *addr_dyn = NULL;
+ size_t zone_len;
+
+ /* store zone and get the string IPv4 address without it */
+ if ((zone_ptr = ly_strnchr(value, '%', value_len))) {
+ /* there is a zone index */
+ zone_len = value_len - (zone_ptr - value) - 1;
+ ret = lydict_insert(ctx, zone_ptr + 1, zone_len, zone);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the IP without it */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ *zone_ptr = '\0';
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, zone_ptr - value);
+ addr_no_zone = addr_dyn;
+ }
+ } else {
+ /* no zone */
+ *zone = NULL;
+
+ /* get the IP terminated with zero */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* we can use the value directly */
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, value_len);
+ addr_no_zone = addr_dyn;
+ }
+ }
+
+ /* store the IPv4 address in network-byte order */
+ if (!inet_pton(AF_INET, addr_no_zone, addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", addr_no_zone);
+ goto cleanup;
+ }
+
+ /* restore the value */
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
+ *zone_ptr = '%';
+ }
+
+cleanup:
+ free(addr_dyn);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_address(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *value_str = value;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv4_address *val;
+ size_t i;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address value size %zu "
+ "(expected at least 4).", value_len);
+ goto cleanup;
+ }
+ for (i = 4; i < value_len; ++i) {
+ if (!isalnum(value_str[i])) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address zone character 0x%x.",
+ value_str[i]);
+ goto cleanup;
+ }
+ }
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+
+ /* store zone, if any */
+ if (value_len > 4) {
+ ret = lydict_insert(ctx, value_str + 4, value_len - 4, &val->zone);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ val->zone = NULL;
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the network-byte order address */
+ ret = ipv4address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv4_address(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_address(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv4_address *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ /* zones are NULL or in the dictionary */
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr) || (v1->zone != v2->zone)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv4-address ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv4_address(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv4_address *val;
+ size_t zone_len;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ if (!val->zone) {
+ /* address-only, const */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof val->addr;
+ }
+ return &val->addr;
+ }
+
+ /* dynamic */
+ zone_len = strlen(val->zone);
+ ret = malloc(sizeof val->addr + zone_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ memcpy(ret, &val->addr, sizeof val->addr);
+ memcpy(ret + sizeof val->addr, val->zone, zone_len);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = sizeof val->addr + zone_len;
+ }
+ return ret;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* '%' + zone */
+ zone_len = val->zone ? strlen(val->zone) + 1 : 0;
+ ret = malloc(INET_ADDRSTRLEN + zone_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv4 address in string (%s).", strerror(errno));
+ return NULL;
+ }
+
+ /* add zone */
+ if (zone_len) {
+ sprintf(ret + strlen(ret), "%%%s", val->zone);
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv4_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv4_address *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+ ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
+ LY_CHECK_GOTO(ret, error);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv4_address(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv4-address ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv4_address *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ lydict_remove(ctx, val->zone);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for ipv4-address type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_address[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-address",
+
+ .plugin.id = "libyang 2 - ipv4-address, version 1",
+ .plugin.store = lyplg_type_store_ipv4_address,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv4_address,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv4_address,
+ .plugin.duplicate = lyplg_type_dup_ipv4_address,
+ .plugin.free = lyplg_type_free_ipv4_address,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_address_no_zone.c b/src/plugins_types/ipv4_address_no_zone.c
new file mode 100644
index 0000000..91fe677
--- /dev/null
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -0,0 +1,221 @@
+/**
+ * @file ipv4_address_no_zone.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-address-no-zone type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4AddressNoZone ipv4-address-no-zone (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_address_no_zone(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv4_address_no_zone *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address-no-zone value size %zu "
+ "(expected 4).", value_len);
+ goto cleanup;
+ }
+
+ /* store IP address */
+ memcpy(&val->addr, value, 4);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* we always need a dynamic value */
+ if (!(options & LYPLG_TYPE_STORE_DYNAMIC)) {
+ value = strndup(value, value_len);
+ LY_CHECK_ERR_GOTO(!value, ret = LY_EMEM, cleanup);
+
+ options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+
+ /* get the network-byte order address */
+ if (!inet_pton(AF_INET, value, &val->addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", (char *)value);
+ goto cleanup;
+ }
+
+ /* store canonical value */
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv4_address_no_zone *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv4_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv4_address_no_zone *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 4;
+ }
+ return &val->addr;
+ }
+
+ /* generate canonical value if not already (loaded from LYB) */
+ if (!value->_canonical) {
+ ret = malloc(INET_ADDRSTRLEN);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv4 address in string (%s).", strerror(errno));
+ return NULL;
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for ipv4-address-no-zone type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_address_no_zone[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-address-no-zone",
+
+ .plugin.id = "libyang 2 - ipv4-address-no-zone, version 1",
+ .plugin.store = lyplg_type_store_ipv4_address_no_zone,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv4_address_no_zone,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv4_address_no_zone,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
new file mode 100644
index 0000000..6f13eee
--- /dev/null
+++ b/src/plugins_types/ipv4_prefix.c
@@ -0,0 +1,337 @@
+/**
+ * @file ipv4_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-prefix type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4Prefix ipv4-prefix (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ * | 1 | yes | `uint8_t *` | prefix length up to 32 |
+ */
+
+#define LYB_VALUE_LEN 5
+
+static void lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with a prefix in string to a binary network-byte order value.
+ *
+ * @param[in] value String to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] addr Allocated address value to fill.
+ * @param[out] prefix Prefix length.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv4prefix_str2ip(const char *value, size_t value_len, struct in_addr *addr, uint8_t *prefix, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *pref_str;
+ char *mask_str = NULL;
+
+ /* it passed the pattern validation */
+ pref_str = ly_strnchr(value, '/', value_len);
+ ly_strntou8(pref_str + 1, value_len - (pref_str + 1 - value), prefix);
+
+ /* get just the network prefix */
+ mask_str = strndup(value, pref_str - value);
+ LY_CHECK_ERR_GOTO(!mask_str, ret = LY_EMEM, cleanup);
+
+ /* convert it to netword-byte order */
+ if (inet_pton(AF_INET, mask_str, addr) != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", mask_str);
+ goto cleanup;
+ }
+
+cleanup:
+ free(mask_str);
+ return ret;
+}
+
+/**
+ * @brief Zero host-portion of the IP address.
+ *
+ * @param[in,out] addr IP address.
+ * @param[in] prefix Prefix length.
+ */
+static void
+ipv4prefix_zero_host(struct in_addr *addr, uint8_t prefix)
+{
+ uint32_t i, mask;
+
+ /* zero host bits */
+ mask = 0;
+ for (i = 0; i < 32; ++i) {
+ mask <<= 1;
+ if (prefix > i) {
+ mask |= 1;
+ }
+ }
+ mask = htonl(mask);
+ addr->s_addr &= mask;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-prefix ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv4_prefix *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != LYB_VALUE_LEN) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix value size %zu (expected %d).",
+ value_len, LYB_VALUE_LEN);
+ goto cleanup;
+ }
+ if (((uint8_t *)value)[4] > 32) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix prefix length %" PRIu8 ".",
+ ((uint8_t *)value)[4]);
+ goto cleanup;
+ }
+
+ /* store addr + prefix */
+ memcpy(val, value, value_len);
+
+ /* zero host */
+ ipv4prefix_zero_host(&val->addr, val->prefix);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the mask in network-byte order */
+ ret = ipv4prefix_str2ip(value, value_len, &val->addr, &val->prefix, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* zero host */
+ ipv4prefix_zero_host(&val->addr, val->prefix);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv4_prefix(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_prefix(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv4_prefix *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(v1, v2, sizeof *v1)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static const void *
+lyplg_type_print_ipv4_prefix(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv4_prefix *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = LYB_VALUE_LEN;
+ }
+ return val;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* IPv4 mask + '/' + prefix */
+ ret = malloc(INET_ADDRSTRLEN + 3);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* convert back to string */
+ if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ return NULL;
+ }
+
+ /* add the prefix */
+ sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv4_prefix(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv4_prefix *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(dup_val, orig_val, sizeof *orig_val);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv4_prefix(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static void
+lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv4_prefix *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+}
+
+/**
+ * @brief Plugin information for ipv4-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_prefix[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-prefix",
+
+ .plugin.id = "libyang 2 - ipv4-prefix, version 1",
+ .plugin.store = lyplg_type_store_ipv4_prefix,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv4_prefix,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv4_prefix,
+ .plugin.duplicate = lyplg_type_dup_ipv4_prefix,
+ .plugin.free = lyplg_type_free_ipv4_prefix,
+ .plugin.lyb_data_len = LYB_VALUE_LEN,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
new file mode 100644
index 0000000..74f5c62
--- /dev/null
+++ b/src/plugins_types/ipv6_address.c
@@ -0,0 +1,378 @@
+/**
+ * @file ipv6_address.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-address type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6Address ipv6-address (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | string length | no | `char *` | IPv6 address zone string |
+ */
+
+static void lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with optional zone to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in] ctx libyang context with dictionary.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] zone Ipv6 address zone in dictionary.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6address_str2ip(const char *value, size_t value_len, uint32_t options, const struct ly_ctx *ctx,
+ struct in6_addr *addr, const char **zone, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *addr_no_zone;
+ char *zone_ptr = NULL, *addr_dyn = NULL;
+ size_t zone_len;
+
+ /* store zone and get the string IPv6 address without it */
+ if ((zone_ptr = ly_strnchr(value, '%', value_len))) {
+ /* there is a zone index */
+ zone_len = value_len - (zone_ptr - value) - 1;
+ ret = lydict_insert(ctx, zone_ptr + 1, zone_len, zone);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the IP without it */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ *zone_ptr = '\0';
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, zone_ptr - value);
+ addr_no_zone = addr_dyn;
+ }
+ } else {
+ /* no zone */
+ *zone = NULL;
+
+ /* get the IP terminated with zero */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* we can use the value directly */
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, value_len);
+ addr_no_zone = addr_dyn;
+ }
+ }
+
+ /* store the IPv6 address in network-byte order */
+ if (!inet_pton(AF_INET6, addr_no_zone, addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_no_zone);
+ goto cleanup;
+ }
+
+ /* restore the value */
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
+ *zone_ptr = '%';
+ }
+
+cleanup:
+ free(addr_dyn);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_address(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *value_str = value;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv6_address *val;
+ size_t i;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 16) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address value size %zu "
+ "(expected at least 16).", value_len);
+ goto cleanup;
+ }
+ for (i = 16; i < value_len; ++i) {
+ if (!isalnum(value_str[i])) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address zone character 0x%x.",
+ value_str[i]);
+ goto cleanup;
+ }
+ }
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+
+ /* store zone, if any */
+ if (value_len > 16) {
+ ret = lydict_insert(ctx, value_str + 16, value_len - 16, &val->zone);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ val->zone = NULL;
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the network-byte order address */
+ ret = ipv6address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv6_address(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv6-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_address(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv6_address *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ /* zones are NULL or in the dictionary */
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr) || (v1->zone != v2->zone)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv6-address ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv6_address *val;
+ size_t zone_len;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ if (!val->zone) {
+ /* address-only, const */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof val->addr;
+ }
+ return &val->addr;
+ }
+
+ /* dynamic */
+ zone_len = strlen(val->zone);
+ ret = malloc(sizeof val->addr + zone_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ memcpy(ret, &val->addr, sizeof val->addr);
+ memcpy(ret + sizeof val->addr, val->zone, zone_len);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = sizeof val->addr + zone_len;
+ }
+ return ret;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* '%' + zone */
+ zone_len = val->zone ? strlen(val->zone) + 1 : 0;
+ ret = malloc(INET6_ADDRSTRLEN + zone_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv6 address in string (%s).", strerror(errno));
+ return NULL;
+ }
+
+ /* add zone */
+ if (zone_len) {
+ sprintf(ret + strlen(ret), "%%%s", val->zone);
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv6-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv6_address *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+ ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
+ LY_CHECK_GOTO(ret, error);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv6_address(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv6-address ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv6_address *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ lydict_remove(ctx, val->zone);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for ipv6-address type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_address[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-address",
+
+ .plugin.id = "libyang 2 - ipv6-address, version 1",
+ .plugin.store = lyplg_type_store_ipv6_address,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv6_address,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv6_address,
+ .plugin.duplicate = lyplg_type_dup_ipv6_address,
+ .plugin.free = lyplg_type_free_ipv6_address,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_address_no_zone.c b/src/plugins_types/ipv6_address_no_zone.c
new file mode 100644
index 0000000..26fbf80
--- /dev/null
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -0,0 +1,312 @@
+/**
+ * @file ipv6_address_no_zone.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-address-no-zone type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6AddressNoZone ipv6-address-no-zone (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ */
+
+static void lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6addressnozone_str2ip(const char *value, size_t value_len, uint32_t options, struct in6_addr *addr, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *addr_str;
+ char *addr_dyn = NULL;
+
+ /* get the IP terminated with zero */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* we can use the value directly */
+ addr_str = value;
+ } else {
+ addr_dyn = strndup(value, value_len);
+ addr_str = addr_dyn;
+ }
+
+ /* store the IPv6 address in network-byte order */
+ if (!inet_pton(AF_INET6, addr_str, addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_str);
+ goto cleanup;
+ }
+
+cleanup:
+ free(addr_dyn);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+ size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv6_address_no_zone *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 16) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address-no-zone value size %zu "
+ "(expected 16).", value_len);
+ goto cleanup;
+ }
+
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && LYPLG_TYPE_VAL_IS_DYN(val)) {
+ /* use the value directly */
+ storage->dyn_mem = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the network-byte order address */
+ ret = ipv6addressnozone_str2ip(value, value_len, options, &val->addr, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv6_address_no_zone(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv6_address_no_zone *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv6_address_no_zone *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof val->addr;
+ }
+ return &val->addr;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* '%' + zone */
+ ret = malloc(INET6_ADDRSTRLEN);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv6 address in string (%s).", strerror(errno));
+ return NULL;
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv6_address_no_zone *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv6_address_no_zone(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv6_address_no_zone *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+}
+
+/**
+ * @brief Plugin information for ipv6-address-no-zone type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_address_no_zone[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-address-no-zone",
+
+ .plugin.id = "libyang 2 - ipv6-address-no-zone, version 1",
+ .plugin.store = lyplg_type_store_ipv6_address_no_zone,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv6_address_no_zone,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv6_address_no_zone,
+ .plugin.duplicate = lyplg_type_dup_ipv6_address_no_zone,
+ .plugin.free = lyplg_type_free_ipv6_address_no_zone,
+ .plugin.lyb_data_len = 16,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
new file mode 100644
index 0000000..8e62311
--- /dev/null
+++ b/src/plugins_types/ipv6_prefix.c
@@ -0,0 +1,351 @@
+/**
+ * @file ipv6_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-prefix type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6Prefix ipv6-prefix (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | 1 | yes | `uint8_t *` | prefix length up to 128 |
+ */
+
+#define LYB_VALUE_LEN 17
+
+static void lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with a prefix in string to a binary network-byte order value.
+ *
+ * @param[in] value String to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] addr Allocated address value to fill.
+ * @param[out] prefix Prefix length.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6prefix_str2ip(const char *value, size_t value_len, struct in6_addr *addr, uint8_t *prefix, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *pref_str;
+ char *mask_str = NULL;
+
+ /* it passed the pattern validation */
+ pref_str = ly_strnchr(value, '/', value_len);
+ ly_strntou8(pref_str + 1, value_len - (pref_str + 1 - value), prefix);
+
+ /* get just the network prefix */
+ mask_str = strndup(value, pref_str - value);
+ LY_CHECK_ERR_GOTO(!mask_str, ret = LY_EMEM, cleanup);
+
+ /* convert it to netword-byte order */
+ if (inet_pton(AF_INET6, mask_str, addr) != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", mask_str);
+ goto cleanup;
+ }
+
+cleanup:
+ free(mask_str);
+ return ret;
+}
+
+/**
+ * @brief Zero host-portion of the IP address.
+ *
+ * @param[in,out] addr IP address.
+ * @param[in] prefix Prefix length.
+ */
+static void
+ipv6prefix_zero_host(struct in6_addr *addr, uint8_t prefix)
+{
+ uint32_t i, j, mask;
+
+ /* zero host bits */
+ for (i = 0; i < 4; ++i) {
+ mask = 0;
+ for (j = 0; j < 32; ++j) {
+ mask <<= 1;
+ if (prefix > (i * 32) + j) {
+ mask |= 1;
+ }
+ }
+ mask = htonl(mask);
+ ((uint32_t *)addr->s6_addr)[i] &= mask;
+ }
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-prefix ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv6_prefix *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != LYB_VALUE_LEN) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix value size %zu (expected %d).",
+ value_len, LYB_VALUE_LEN);
+ goto cleanup;
+ }
+ if (((uint8_t *)value)[16] > 128) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix prefix length %" PRIu8 ".",
+ ((uint8_t *)value)[16]);
+ goto cleanup;
+ }
+
+ /* store/allocate value */
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && LYPLG_TYPE_VAL_IS_DYN(val)) {
+ storage->dyn_mem = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+
+ LYD_VALUE_GET(storage, val);
+ } else {
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ memcpy(val, value, value_len);
+ }
+
+ /* zero host */
+ ipv6prefix_zero_host(&val->addr, val->prefix);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the mask in network-byte order */
+ ret = ipv6prefix_str2ip(value, value_len, &val->addr, &val->prefix, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* zero host */
+ ipv6prefix_zero_host(&val->addr, val->prefix);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv6_prefix(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_prefix(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv6_prefix *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(v1, v2, sizeof *v1)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static const void *
+lyplg_type_print_ipv6_prefix(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv6_prefix *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = LYB_VALUE_LEN;
+ }
+ return val;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* IPv6 mask + '/' + prefix */
+ ret = malloc(INET6_ADDRSTRLEN + 4);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* convert back to string */
+ if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+ free(ret);
+ return NULL;
+ }
+
+ /* add the prefix */
+ sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_prefix(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv6_prefix *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(dup_val, orig_val, sizeof *orig_val);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv6_prefix(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static void
+lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv6_prefix *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+}
+
+/**
+ * @brief Plugin information for ipv6-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_prefix[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-prefix",
+
+ .plugin.id = "libyang 2 - ipv6-prefix, version 1",
+ .plugin.store = lyplg_type_store_ipv6_prefix,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv6_prefix,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv6_prefix,
+ .plugin.duplicate = lyplg_type_dup_ipv6_prefix,
+ .plugin.free = lyplg_type_free_ipv6_prefix,
+ .plugin.lyb_data_len = LYB_VALUE_LEN,
+ },
+ {0}
+};
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
new file mode 100644
index 0000000..8ab3fc5
--- /dev/null
+++ b/src/plugins_types/leafref.c
@@ -0,0 +1,140 @@
+/**
+ * @file leafref.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in leafref type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesLeafref leafref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------: | :-------: | :--: | :-----: |
+ * | exact same format as the leafref target ||||
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
+
+ assert(type_lr->realtype);
+
+ /* store the value as the real type of the leafref target */
+ ret = type_lr->realtype->plugin->store(ctx, type_lr->realtype, value, value_len, options, format, prefix_data,
+ hints, ctx_node, storage, unres, err);
+ if (ret == LY_EINCOMPLETE) {
+ /* it is irrelevant whether the target type needs some resolving */
+ ret = LY_SUCCESS;
+ }
+ LY_CHECK_RET(ret);
+
+ if (type_lr->require_instance) {
+ /* needs to be resolved */
+ return LY_EINCOMPLETE;
+ } else {
+ return LY_SUCCESS;
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *ctx_node,
+ const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
+ char *errmsg = NULL, *path;
+
+ *err = NULL;
+
+ if (!type_lr->require_instance) {
+ /* redundant to resolve */
+ return LY_SUCCESS;
+ }
+
+ /* check leafref target existence */
+ if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
+ path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, path, strdup("instance-required"), "%s", errmsg);
+ free(errmsg);
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ return val1->realtype->plugin->compare(val1, val2);
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_leafref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ return value->realtype->plugin->print(ctx, value, format, prefix_data, dynamic, value_len);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ return original->realtype->plugin->duplicate(ctx, original, dup);
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ value->realtype->plugin->free(ctx, value);
+}
+
+/**
+ * @brief Plugin information for leafref type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_leafref[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_LEAFREF_STR,
+
+ .plugin.id = "libyang 2 - leafref, version 1",
+ .plugin.store = lyplg_type_store_leafref,
+ .plugin.validate = lyplg_type_validate_leafref,
+ .plugin.compare = lyplg_type_compare_leafref,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_leafref,
+ .plugin.duplicate = lyplg_type_dup_leafref,
+ .plugin.free = lyplg_type_free_leafref,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c
new file mode 100644
index 0000000..04fb164
--- /dev/null
+++ b/src/plugins_types/node_instanceid.c
@@ -0,0 +1,320 @@
+/**
+ * @file node_instanceid.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-netconf-acm node-instance-identifier type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "path.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+#include "xpath.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesNodeInstanceIdentifier node-instance-identifier (ietf-netconf-acm)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier |
+ */
+
+/**
+ * @brief Convert compiled path (node-instance-identifier) or NULL ("/") into string.
+ *
+ * @param[in] path Compiled path.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed instance-identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *prefix_data, char **str)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ char *result = NULL, quot;
+ const struct lys_module *mod = NULL;
+ ly_bool inherit_prefix = 0, d;
+ const char *strval;
+
+ if (!path) {
+ /* special path */
+ ret = ly_strcat(&result, "/");
+ goto cleanup;
+ }
+
+ switch (format) {
+ case LY_VALUE_XML:
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* everything is prefixed */
+ inherit_prefix = 0;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* the same prefix is inherited and skipped */
+ inherit_prefix = 1;
+ break;
+ }
+
+ LY_ARRAY_FOR(path, u) {
+ /* new node */
+ if (!inherit_prefix || (mod != path[u].node->module)) {
+ mod = path[u].node->module;
+ ret = ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), path[u].node->name);
+ } else {
+ ret = ly_strcat(&result, "/%s", path[u].node->name);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* node predicates */
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ struct ly_path_predicate *pred = &path[u].predicates[v];
+
+ switch (path[u].pred_type) {
+ case LY_PATH_PREDTYPE_NONE:
+ break;
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* key-predicate */
+ strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+ &d, NULL);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ if (inherit_prefix) {
+ /* always the same prefix as the parent */
+ ret = ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, strval, quot);
+ } else {
+ ret = ly_strcat(&result, "[%s:%s=%c%s%c]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
+ pred->key->name, quot, strval, quot);
+ }
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list-predicate */
+ strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+ &d, NULL);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ ret = ly_strcat(&result, "[.=%c%s%c]", quot, strval, quot);
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ }
+
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ free(result);
+ } else {
+ *str = result;
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the node-instance-identifier ietf-netconf-acm type.
+ */
+static LY_ERR
+lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ uint32_t prefix_opt = 0;
+ struct ly_path *path = NULL;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if ((((char *)value)[0] == '/') && (value_len == 1)) {
+ /* special path */
+ goto store;
+ }
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ prefix_opt = LY_PATH_PREFIX_MANDATORY;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_JSON:
+ case LY_VALUE_STR_NS:
+ prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
+ break;
+ }
+
+ /* parse the value */
+ ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
+ if (ret) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, (char *)value);
+ goto cleanup;
+ }
+
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ /* implement all prefixes */
+ LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup);
+ }
+
+ /* resolve it on schema tree, use JSON format instead of LYB because for this type they are equal but for some
+ * nested types (such as numbers in predicates in the path) LYB would be invalid */
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node && (ctx_node->flags & LYS_IS_OUTPUT)) ?
+ LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, 1, (format == LY_VALUE_LYB) ?
+ LY_VALUE_JSON : format, prefix_data, &path);
+ if (ret) {
+ ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, (char *)value);
+ goto cleanup;
+ }
+
+store:
+ /* store value */
+ storage->target = path;
+
+ /* store canonical value */
+ if (format == LY_VALUE_CANON) {
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* JSON format with prefix is the canonical one */
+ ret = node_instanceid_path2str(path, LY_VALUE_JSON, NULL, &canon);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ lyxp_expr_free(ctx, exp);
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_instanceid(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the node-instance-identifier ietf-netconf-acm type.
+ */
+static const void *
+lyplg_type_print_node_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ char *ret;
+
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+ }
+
+ /* print the value in the specific format */
+ if (node_instanceid_path2str(value->target, format, prefix_data, &ret)) {
+ return NULL;
+ }
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for instance-identifier type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_node_instanceid[] = {
+ {
+ .module = "ietf-netconf-acm",
+ .revision = "2012-02-22",
+ .name = "node-instance-identifier",
+
+ .plugin.id = "libyang 2 - node-instance-identifier, version 1",
+ .plugin.store = lyplg_type_store_node_instanceid,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_instanceid,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_node_instanceid,
+ .plugin.duplicate = lyplg_type_dup_instanceid,
+ .plugin.free = lyplg_type_free_instanceid,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-netconf-acm",
+ .revision = "2018-02-14",
+ .name = "node-instance-identifier",
+
+ .plugin.id = "libyang 2 - node-instance-identifier, version 1",
+ .plugin.store = lyplg_type_store_node_instanceid,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_instanceid,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_node_instanceid,
+ .plugin.duplicate = lyplg_type_dup_instanceid,
+ .plugin.free = lyplg_type_free_instanceid,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
new file mode 100644
index 0000000..4f988ef
--- /dev/null
+++ b/src/plugins_types/string.c
@@ -0,0 +1,109 @@
+/**
+ * @file string.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in string type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesString string (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for string type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_string[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_STRING_STR,
+
+ .plugin.id = "libyang 2 - string, version 1",
+ .plugin.store = lyplg_type_store_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
new file mode 100644
index 0000000..6e31d1e
--- /dev/null
+++ b/src/plugins_types/union.c
@@ -0,0 +1,585 @@
+/**
+ * @file union.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in union type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesUnion union (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `uint32_t *` | little-endian index of the resolved type in ::lysc_type_union.types |
+ * | exact same format as the resolved type ||||
+ *
+ * Note that loading union value in this format prevents it from changing its real (resolved) type.
+ */
+
+/**
+ * @brief Size in bytes of the index in the LYB Binary Format.
+ */
+#define IDX_SIZE 4
+
+/**
+ * @brief Assign a value to the union subvalue.
+ *
+ * @param[in] value Value for assignment.
+ * @param[in] value_len Length of the @p value.
+ * @param[out] original Destination item of the subvalue.
+ * @param[out] orig_len Length of the @p original.
+ * @param[in,out] options Flag containing LYPLG_TYPE_STORE_DYNAMIC.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_subvalue_assignment(const void *value, size_t value_len, void **original, size_t *orig_len, uint32_t *options)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (*options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* The allocated value is stored and spend. */
+ *original = (void *)value;
+ *options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else if (value_len) {
+ /* Make copy of the value. */
+ *original = calloc(1, value_len);
+ LY_CHECK_ERR_RET(!*original, ret = LY_EMEM, ret);
+ memcpy(*original, value, value_len);
+ } else {
+ /* Empty value. */
+ *original = strdup("");
+ LY_CHECK_ERR_RET(!*original, ret = LY_EMEM, ret);
+ }
+ *orig_len = value_len;
+
+ return ret;
+}
+
+/**
+ * @brief Validate LYB Binary Format.
+ *
+ * @param[in] lyb_data Source of LYB data to parse.
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[in] type_u Compiled type of union.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_union_validate(const void *lyb_data, size_t lyb_data_len, const struct lysc_type_union *type_u, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint64_t type_idx = 0;
+
+ /* Basic validation. */
+ if (lyb_data_len < IDX_SIZE) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union value size %zu (expected at least 4).",
+ lyb_data_len);
+ return ret;
+ }
+
+ /* Get index in correct byte order. */
+ memcpy(&type_idx, lyb_data, IDX_SIZE);
+ type_idx = le64toh(type_idx);
+ if (type_idx >= LY_ARRAY_COUNT(type_u->types)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid LYB union type index %" PRIu64 " (type count %" LY_PRI_ARRAY_COUNT_TYPE ").",
+ type_idx, LY_ARRAY_COUNT(type_u->types));
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Parse index and lyb_value from LYB Binary Format.
+ *
+ * @param[in] lyb_data Source of LYB data to parse.
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[out] type_idx Index of the union type.
+ * @param[out] lyb_value Value after index number. If there is no value
+ * after the index, it is set to empty string ("").
+ * @param[out] lyb_value_len Length of @p lyb_value.
+ */
+static void
+lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, const void **lyb_value, size_t *lyb_value_len)
+{
+ uint64_t num = 0;
+
+ assert(lyb_data && !(lyb_value && !lyb_value_len));
+
+ if (type_idx) {
+ memcpy(&num, lyb_data, IDX_SIZE);
+ num = le64toh(num);
+
+ *type_idx = num;
+ }
+
+ if (lyb_value && lyb_value_len && lyb_data_len) {
+ /* Get lyb_value and its length. */
+ if (lyb_data_len == IDX_SIZE) {
+ *lyb_value_len = 0;
+ *lyb_value = "";
+ } else {
+ *lyb_value_len = lyb_data_len - IDX_SIZE;
+ *lyb_value = (char *)lyb_data + IDX_SIZE;
+ }
+ }
+}
+
+/**
+ * @brief Store subvalue as a specific type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type Specific union type to use for storing.
+ * @param[in] subvalue Union subvalue structure.
+ * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] ctx_node Context node for prefix resolution.
+ * @param[in] tree Data tree for resolving (validation).
+ * @param[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value_union *subvalue,
+ ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
+ struct ly_err_item **err)
+{
+ LY_ERR ret;
+ const void *value = NULL;
+ size_t value_len = 0;
+
+ if (subvalue->format == LY_VALUE_LYB) {
+ lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
+ } else {
+ value = subvalue->original;
+ value_len = subvalue->orig_len;
+ }
+
+ ret = type->plugin->store(ctx, type, value, value_len, 0, subvalue->format, subvalue->prefix_data, subvalue->hints,
+ subvalue->ctx_node, &subvalue->value, unres, err);
+ if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+ /* clear any leftover/freed garbage */
+ memset(&subvalue->value, 0, sizeof subvalue->value);
+ return ret;
+ }
+
+ if (resolve && (ret == LY_EINCOMPLETE)) {
+ /* we need the value resolved */
+ ret = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
+ if (ret) {
+ /* resolve failed, we need to free the stored value */
+ type->plugin->free(ctx, &subvalue->value);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Find the first valid type for a union value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] types Sized array of union types.
+ * @param[in] subvalue Union subvalue structure.
+ * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] ctx_node Context node for prefix resolution.
+ * @param[in] tree Data tree for resolving (validation).
+ * @param[out] type_idx Index of the type in which the value was stored.
+ * @param[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
+ ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, uint32_t *type_idx,
+ struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t temp_lo = 0;
+
+ if (!types || !LY_ARRAY_COUNT(types)) {
+ return LY_EINVAL;
+ }
+
+ *err = NULL;
+
+ /* turn logging temporarily off */
+ ly_temp_log_options(&temp_lo);
+
+ /* use the first usable subtype to store the value */
+ for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err);
+ if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
+ break;
+ }
+
+ ly_err_free(*err);
+ *err = NULL;
+ }
+
+ if (u == LY_ARRAY_COUNT(types)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid union value \"%.*s\" - no matching subtype found.",
+ (int)subvalue->orig_len, (char *)subvalue->original);
+ } else if (type_idx) {
+ *type_idx = u;
+ }
+
+ /* restore logging */
+ ly_temp_log_options(NULL);
+ return ret;
+}
+
+/**
+ * @brief Fill union subvalue items: original, origin_len, format prefix_data and call 'store' function for value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type_u Compiled type of union.
+ * @param[in] lyb_data Input LYB data consisting of index followed by value (lyb_value).
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in,out] subvalue Union subvalue to be filled.
+ * @param[in,out] options Option containing LYPLG_TYPE_STORE_DYNAMIC.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u, const void *lyb_data, size_t lyb_data_len,
+ void *prefix_data, struct lyd_value_union *subvalue, uint32_t *options, struct lys_glob_unres *unres,
+ struct ly_err_item **err)
+{
+ LY_ERR ret;
+ uint32_t type_idx;
+ const void *lyb_value = NULL;
+ size_t lyb_value_len = 0;
+
+ ret = lyb_union_validate(lyb_data, lyb_data_len, type_u, err);
+ LY_CHECK_RET(ret);
+
+ /* Parse lyb_data and set the lyb_value and lyb_value_len. */
+ lyb_parse_union(lyb_data, lyb_data_len, &type_idx, &lyb_value, &lyb_value_len);
+ LY_CHECK_RET(ret);
+
+ /* Store lyb_data to subvalue. */
+ ret = union_subvalue_assignment(lyb_data, lyb_data_len,
+ &subvalue->original, &subvalue->orig_len, options);
+ LY_CHECK_RET(ret);
+
+ if (lyb_value) {
+ /* Resolve prefix_data and set format. */
+ ret = lyplg_type_prefix_data_new(ctx, lyb_value, lyb_value_len, LY_VALUE_LYB, prefix_data, &subvalue->format,
+ &subvalue->prefix_data);
+ LY_CHECK_RET(ret);
+ assert(subvalue->format == LY_VALUE_LYB);
+ } else {
+ /* The lyb_parse_union() did not find lyb_value.
+ * Just set format.
+ */
+ subvalue->format = LY_VALUE_LYB;
+ }
+
+ /* Use the specific type to store the value. */
+ ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lysc_type_union *type_u = (struct lysc_type_union *)type;
+ struct lyd_value_union *subvalue;
+
+ *err = NULL;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, subvalue);
+ LY_CHECK_ERR_GOTO(!subvalue, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+ subvalue->hints = hints;
+ subvalue->ctx_node = ctx_node;
+
+ if (format == LY_VALUE_LYB) {
+ ret = lyb_fill_subvalue(ctx, type_u, value, value_len,
+ prefix_data, subvalue, &options, unres, err);
+ LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ } else {
+ /* Store @p value to subvalue. */
+ ret = union_subvalue_assignment(value, value_len,
+ &subvalue->original, &subvalue->orig_len, &options);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store format-specific data for later prefix resolution */
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &subvalue->format,
+ &subvalue->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, NULL, unres, err);
+ LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ }
+
+ /* store canonical value, if any (use the specific type value) */
+ r = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical);
+ LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+ lyplg_type_free_union(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
+ const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_union *type_u = (struct lysc_type_union *)type;
+ struct lyd_value_union *subvalue = storage->subvalue;
+
+ *err = NULL;
+
+ /* because of types that do not store their own type as realtype (leafref), we are not able to call their
+ * validate callback (there is no way to get the type TODO could be added to struct lyd_value_union), so
+ * we have to perform union value storing again from scratch */
+ subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
+
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
+ LY_CHECK_RET(ret);
+
+ /* success, update the canonical value, if any generated */
+ lydict_remove(ctx, storage->_canonical);
+ LY_CHECK_RET(lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical));
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->subvalue->value.realtype != val2->subvalue->value.realtype) {
+ return LY_ENOT;
+ }
+ return val1->subvalue->value.realtype->plugin->compare(&val1->subvalue->value, &val2->subvalue->value);
+}
+
+/**
+ * @brief Create LYB data for printing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type_u Compiled type of union.
+ * @param[in] subvalue Union value.
+ * @param[in] prefix_data Format-specific data for resolving any
+ * prefixes (see ly_resolve_prefix()).
+ * @param[out] value_len Length of returned data.
+ * @return Pointer to created LYB data. Caller must release.
+ * @return NULL in case of error.
+ */
+static const void *
+lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct lyd_value_union *subvalue,
+ void *prefix_data, size_t *value_len)
+{
+ void *ret = NULL;
+ LY_ERR retval;
+ struct ly_err_item *err;
+ uint64_t num = 0;
+ uint32_t type_idx;
+ ly_bool dynamic;
+ size_t pval_len;
+ void *pval;
+
+ /* Find out the index number (type_idx). The call should succeed
+ * because the union_find_type() has already been called in the
+ * lyplg_type_store_union().
+ */
+ if (!ctx) {
+ assert(subvalue->ctx_node);
+ ctx = subvalue->ctx_node->module->ctx;
+ }
+ subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
+ retval = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, &type_idx, NULL, &err);
+ LY_CHECK_RET((retval != LY_SUCCESS) && (retval != LY_EINCOMPLETE), NULL);
+
+ /* Print subvalue in LYB format. */
+ pval = (void *)subvalue->value.realtype->plugin->print(NULL, &subvalue->value, LY_VALUE_LYB, prefix_data, &dynamic,
+ &pval_len);
+ LY_CHECK_RET(!pval, NULL);
+
+ /* Create LYB data. */
+ *value_len = IDX_SIZE + pval_len;
+ ret = malloc(*value_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ num = type_idx;
+ num = htole64(num);
+ memcpy(ret, &num, IDX_SIZE);
+ memcpy((char *)ret + IDX_SIZE, pval, pval_len);
+
+ if (dynamic) {
+ free(pval);
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ const void *ret;
+ struct lyd_value_union *subvalue = value->subvalue;
+ struct lysc_type_union *type_u = (struct lysc_type_union *)value->realtype;
+ size_t lyb_data_len = 0;
+
+ if ((format == LY_VALUE_LYB) && (subvalue->format == LY_VALUE_LYB)) {
+ /* The return value is already ready. */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = subvalue->orig_len;
+ }
+ return subvalue->original;
+ } else if ((format == LY_VALUE_LYB) && (subvalue->format != LY_VALUE_LYB)) {
+ /* The return LYB data must be created. */
+ *dynamic = 1;
+ ret = lyb_union_print(ctx, type_u, subvalue, prefix_data, &lyb_data_len);
+ if (value_len) {
+ *value_len = lyb_data_len;
+ }
+ return ret;
+ }
+
+ assert(format != LY_VALUE_LYB);
+ ret = (void *)subvalue->value.realtype->plugin->print(ctx, &subvalue->value, format, prefix_data, dynamic, value_len);
+ if (!value->_canonical && (format == LY_VALUE_CANON)) {
+ /* the canonical value is supposed to be stored now */
+ lydict_insert(ctx, subvalue->value._canonical, 0, (const char **)&value->_canonical);
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_value_union *orig_val = original->subvalue, *dup_val;
+
+ /* init dup value */
+ memset(dup, 0, sizeof *dup);
+ dup->realtype = original->realtype;
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ dup->subvalue = dup_val;
+
+ ret = orig_val->value.realtype->plugin->duplicate(ctx, &orig_val->value, &dup_val->value);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (orig_val->orig_len) {
+ dup_val->original = calloc(1, orig_val->orig_len);
+ LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup_val->original, orig_val->original, orig_val->orig_len);
+ } else {
+ dup_val->original = strdup("");
+ LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+ dup_val->orig_len = orig_val->orig_len;
+
+ dup_val->format = orig_val->format;
+ dup_val->ctx_node = orig_val->ctx_node;
+ dup_val->hints = orig_val->hints;
+ ret = lyplg_type_prefix_data_dup(ctx, orig_val->format, orig_val->prefix_data, &dup_val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ if (ret) {
+ lyplg_type_free_union(ctx, dup);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_union *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ if (val->value.realtype) {
+ val->value.realtype->plugin->free(ctx, &val->value);
+ }
+ lyplg_type_prefix_data_free(val->format, val->prefix_data);
+ free(val->original);
+
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for union type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_union[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UNION_STR,
+
+ .plugin.id = "libyang 2 - union,version 1",
+ .plugin.store = lyplg_type_store_union,
+ .plugin.validate = lyplg_type_validate_union,
+ .plugin.compare = lyplg_type_compare_union,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_union,
+ .plugin.duplicate = lyplg_type_dup_union,
+ .plugin.free = lyplg_type_free_union,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
new file mode 100644
index 0000000..a15e5b7
--- /dev/null
+++ b/src/plugins_types/xpath1.0.c
@@ -0,0 +1,521 @@
+/**
+ * @file xpath1.0.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-yang-types xpath1.0 type plugin.
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/* internal headers */
+#include "xml.h"
+#include "xpath.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesXpath10 xpath1.0 (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the XPath expression |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_nametest, const struct lys_module **context_mod,
+ const struct ly_ctx *resolve_ctx, LY_VALUE_FORMAT resolve_format, const void *resolve_prefix_data,
+ LY_VALUE_FORMAT get_format, void *get_prefix_data, char **token_p, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *str_begin, *str_next, *prefix;
+ ly_bool is_prefix, has_prefix = 0;
+ char *str = NULL;
+ void *mem;
+ uint32_t len, str_len = 0, pref_len;
+ const struct lys_module *mod;
+
+ str_begin = token;
+
+ while (!(ret = ly_value_prefix_next(str_begin, token + tok_len, &len, &is_prefix, &str_next)) && len) {
+ if (!is_prefix) {
+ if (!has_prefix && is_nametest && (get_format == LY_VALUE_XML) && *context_mod) {
+ /* prefix is always needed, get it in the target format */
+ prefix = lyplg_type_get_prefix(*context_mod, get_format, get_prefix_data);
+ if (!prefix) {
+ ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
+ goto cleanup;
+ }
+
+ /* append the nametest and prefix */
+ mem = realloc(str, str_len + strlen(prefix) + 1 + len + 1);
+ LY_CHECK_ERR_GOTO(!mem, ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory."), cleanup);
+ str = mem;
+ str_len += sprintf(str + str_len, "%s:%.*s", prefix, len, str_begin);
+ } else {
+ /* just append the string, we may get the first expression node without a prefix but since this
+ * is not strictly forbidden, allow it */
+ mem = realloc(str, str_len + len + 1);
+ LY_CHECK_ERR_GOTO(!mem, ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory."), cleanup);
+ str = mem;
+ str_len += sprintf(str + str_len, "%.*s", len, str_begin);
+ }
+ } else {
+ /* remember there was a prefix found */
+ has_prefix = 1;
+
+ /* resolve the module in the original format */
+ mod = lyplg_type_identity_module(resolve_ctx, NULL, str_begin, len, resolve_format, resolve_prefix_data);
+ if (!mod && is_nametest) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to resolve prefix \"%.*s\".", len, str_begin);
+ goto cleanup;
+ }
+
+ if (is_nametest && ((get_format == LY_VALUE_JSON) || (get_format == LY_VALUE_LYB)) && (*context_mod == mod)) {
+ /* inherit the prefix and do not print it again */
+ } else {
+ if (mod) {
+ /* get the prefix in the target format */
+ prefix = lyplg_type_get_prefix(mod, get_format, get_prefix_data);
+ if (!prefix) {
+ ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
+ goto cleanup;
+ }
+ pref_len = strlen(prefix);
+ } else {
+ /* invalid prefix, just copy it */
+ prefix = str_begin;
+ pref_len = len;
+ }
+
+ /* append the prefix */
+ mem = realloc(str, str_len + pref_len + 2);
+ LY_CHECK_ERR_GOTO(!mem, ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory."), cleanup);
+ str = mem;
+ str_len += sprintf(str + str_len, "%.*s:", (int)pref_len, prefix);
+ }
+
+ if (is_nametest) {
+ /* update context module */
+ *context_mod = mod;
+ }
+ }
+
+ str_begin = str_next;
+ }
+
+cleanup:
+ if (ret) {
+ free(str);
+ } else {
+ *token_p = str;
+ }
+ return ret;
+}
+
+/**
+ * @brief Print xpath1.0 subexpression in the specific format.
+ *
+ * @param[in,out] cur_idx Current index of the next token in the expression.
+ * @param[in] end_tok End token (including) that finishes this subexpression parsing. If 0, parse until the end.
+ * @param[in] context_mod Current context module, some formats (::LY_VALUE_JSON and ::LY_VALUE_LYB) inherit this module
+ * instead of printing it again.
+ * @param[in] xp_val xpath1.0 value structure.
+ * @param[in] format Format to print in.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in,out] str_value Printed value, appended to.
+ * @param[in,out] str_len Length of @p str_value, updated.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xpath10_print_subexpr_r(uint16_t *cur_idx, enum lyxp_token end_tok, const struct lys_module *context_mod,
+ const struct lyd_value_xpath10 *xp_val, LY_VALUE_FORMAT format, void *prefix_data, char **str_value,
+ uint32_t *str_len, struct ly_err_item **err)
+{
+ enum lyxp_token cur_tok, sub_end_tok;
+ char *str_tok;
+ void *mem;
+ const char *cur_exp_ptr;
+ ly_bool is_nt;
+ const struct lys_module *orig_context_mod = context_mod;
+
+ while (*cur_idx < xp_val->exp->used) {
+ cur_tok = xp_val->exp->tokens[*cur_idx];
+ cur_exp_ptr = xp_val->exp->expr + xp_val->exp->tok_pos[*cur_idx];
+
+ if ((cur_tok == LYXP_TOKEN_NAMETEST) || (cur_tok == LYXP_TOKEN_LITERAL)) {
+ /* tokens that may include prefixes, get them in the target format */
+ is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
+ LY_CHECK_RET(lyplg_type_xpath10_print_token(cur_exp_ptr, xp_val->exp->tok_len[*cur_idx], is_nt, &context_mod,
+ xp_val->ctx, xp_val->format, xp_val->prefix_data, format, prefix_data, &str_tok, err));
+
+ /* append the converted token */
+ mem = realloc(*str_value, *str_len + strlen(str_tok) + 1);
+ LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ *str_value = mem;
+ *str_len += sprintf(*str_value + *str_len, "%s", str_tok);
+ free(str_tok);
+
+ /* token processed */
+ ++(*cur_idx);
+ } else {
+ if ((cur_tok == LYXP_TOKEN_OPER_LOG) || (cur_tok == LYXP_TOKEN_OPER_UNI) || (cur_tok == LYXP_TOKEN_OPER_MATH)) {
+ /* copy the token with spaces around */
+ mem = realloc(*str_value, *str_len + 1 + xp_val->exp->tok_len[*cur_idx] + 2);
+ LY_CHECK_GOTO(!mem, error_mem);
+ *str_value = mem;
+ *str_len += sprintf(*str_value + *str_len, " %.*s ", (int)xp_val->exp->tok_len[*cur_idx], cur_exp_ptr);
+
+ /* reset context mod */
+ context_mod = orig_context_mod;
+ } else {
+ /* just copy the token */
+ mem = realloc(*str_value, *str_len + xp_val->exp->tok_len[*cur_idx] + 1);
+ LY_CHECK_GOTO(!mem, error_mem);
+ *str_value = mem;
+ *str_len += sprintf(*str_value + *str_len, "%.*s", (int)xp_val->exp->tok_len[*cur_idx], cur_exp_ptr);
+ }
+
+ /* token processed but keep it in cur_tok */
+ ++(*cur_idx);
+
+ if (end_tok && (cur_tok == end_tok)) {
+ /* end token found */
+ break;
+ } else if ((cur_tok == LYXP_TOKEN_BRACK1) || (cur_tok == LYXP_TOKEN_PAR1)) {
+ sub_end_tok = (cur_tok == LYXP_TOKEN_BRACK1) ? LYXP_TOKEN_BRACK2 : LYXP_TOKEN_PAR2;
+
+ /* parse the subexpression separately, use the current context mod */
+ LY_CHECK_RET(xpath10_print_subexpr_r(cur_idx, sub_end_tok, context_mod, xp_val, format, prefix_data,
+ str_value, str_len, err));
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+
+error_mem:
+ return ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_print_xpath10_value(const struct lyd_value_xpath10 *xp_val, LY_VALUE_FORMAT format, void *prefix_data,
+ char **str_value, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint16_t expr_idx = 0;
+ uint32_t str_len = 0;
+
+ *str_value = NULL;
+ *err = NULL;
+
+ /* recursively print the expression */
+ ret = xpath10_print_subexpr_r(&expr_idx, 0, NULL, xp_val, format, prefix_data, str_value, &str_len, err);
+
+ if (ret) {
+ free(*str_value);
+ *str_value = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_xpath10(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres), struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_xpath10 *val;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse */
+ ret = lyxp_expr_parse(ctx, value_len ? value : "", value_len, 1, &val->exp);
+ LY_CHECK_GOTO(ret, cleanup);
+ val->ctx = ctx;
+
+ if (ctx_node && !strcmp(ctx_node->name, "parent-reference") && !strcmp(ctx_node->module->name, "ietf-yang-schema-mount")) {
+ /* special case, this type uses prefix-namespace mapping provided directly in data, keep empty for now */
+ val->format = format = LY_VALUE_STR_NS;
+ ret = ly_set_new((struct ly_set **)&val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* store format-specific data and context for later prefix resolution */
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ switch (format) {
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ break;
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ /* JSON format with prefix is the canonical one */
+ ret = lyplg_type_print_xpath10_value(val, LY_VALUE_JSON, NULL, &canon, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ break;
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_xpath10(ctx, storage);
+ } else if (val->format == LY_VALUE_STR_NS) {
+ /* needs validation */
+ return LY_EINCOMPLETE;
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the xpath1.0 ietf-yang-types type.
+ */
+static LY_ERR
+lyplg_type_validate_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *UNUSED(type),
+ const struct lyd_node *ctx_node, const struct lyd_node *UNUSED(tree), struct lyd_value *storage,
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_value_xpath10 *val;
+ struct ly_set *set = NULL;
+ uint32_t i;
+ const char *pref, *uri;
+ struct lyxml_ns *ns;
+
+ *err = NULL;
+ LYD_VALUE_GET(storage, val);
+
+ if (val->format != LY_VALUE_STR_NS) {
+ /* nothing to validate */
+ return LY_SUCCESS;
+ }
+
+ /* the XML namespace set must exist */
+ assert(val->prefix_data);
+
+ /* special handling of this particular node */
+ assert(!strcmp(LYD_NAME(ctx_node), "parent-reference") &&
+ !strcmp(ctx_node->schema->module->name, "ietf-yang-schema-mount"));
+
+ /* get all the prefix mappings */
+ if ((ret = lyd_find_xpath(ctx_node, "../../../namespace", &set))) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < set->count; ++i) {
+ assert(!strcmp(LYD_NAME(lyd_child(set->dnodes[i])), "prefix"));
+ pref = lyd_get_value(lyd_child(set->dnodes[i]));
+
+ if (!lyd_child(set->dnodes[i])->next) {
+ /* missing URI - invalid mapping, skip */
+ continue;
+ }
+ assert(!strcmp(LYD_NAME(lyd_child(set->dnodes[i])->next), "uri"));
+ uri = lyd_get_value(lyd_child(set->dnodes[i])->next);
+
+ /* create new ns */
+ ns = calloc(1, sizeof *ns);
+ if (!ns) {
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ ns->prefix = strdup(pref);
+ ns->uri = strdup(uri);
+ if (!ns->prefix || !ns->uri) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ ns->depth = 1;
+
+ /* add into the XML namespace set */
+ if ((ret = ly_set_add(val->prefix_data, ns, 1, NULL))) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ if (ret == LY_EMEM) {
+ ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, LY_EMEM_MSG);
+ } else if (ret) {
+ ly_err_new(err, ret, LYVE_DATA, NULL, NULL, "%s", ly_errmsg(LYD_CTX(ctx_node)));
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_xpath10 *val;
+ char *ret;
+ struct ly_err_item *err = NULL;
+
+ LYD_VALUE_GET(value, val);
+
+ /* LY_VALUE_STR_NS should never be transformed */
+ if ((val->format == LY_VALUE_STR_NS) || (format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) ||
+ (format == LY_VALUE_LYB)) {
+ /* canonical */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+ }
+
+ /* print in the specific format */
+ if (lyplg_type_print_xpath10_value(val, format, prefix_data, &ret, &err)) {
+ if (err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ }
+ return NULL;
+ }
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_value_xpath10 *orig_val, *dup_val;
+
+ /* init dup value */
+ memset(dup, 0, sizeof *dup);
+ dup->realtype = original->realtype;
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ dup_val->ctx = ctx;
+
+ LYD_VALUE_GET(original, orig_val);
+ ret = lyxp_expr_dup(ctx, orig_val->exp, 0, 0, &dup_val->exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lyplg_type_prefix_data_dup(ctx, orig_val->format, orig_val->prefix_data, &dup_val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ dup_val->format = orig_val->format;
+
+cleanup:
+ if (ret) {
+ lyplg_type_free_xpath10(ctx, dup);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_xpath10 *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ lyxp_expr_free(ctx, val->exp);
+ lyplg_type_prefix_data_free(val->format, val->prefix_data);
+
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for xpath1.0 type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_xpath10[] = {
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "xpath1.0",
+
+ .plugin.id = "libyang 2 - xpath1.0, version 1",
+ .plugin.store = lyplg_type_store_xpath10,
+ .plugin.validate = lyplg_type_validate_xpath10,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_xpath10,
+ .plugin.duplicate = lyplg_type_dup_xpath10,
+ .plugin.free = lyplg_type_free_xpath10,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/printer_data.c b/src/printer_data.c
new file mode 100644
index 0000000..ea330c7
--- /dev/null
+++ b/src/printer_data.c
@@ -0,0 +1,159 @@
+/**
+ * @file printer_data.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic data printers functions.
+ *
+ * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "printer_data.h"
+
+#include <stdio.h>
+
+#include "common.h"
+#include "log.h"
+#include "out.h"
+#include "out_internal.h"
+#include "printer_internal.h"
+#include "tree_data.h"
+
+static LY_ERR
+lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ switch (format) {
+ case LYD_XML:
+ ret = xml_print_data(out, root, options);
+ break;
+ case LYD_JSON:
+ ret = json_print_data(out, root, options);
+ break;
+ case LYD_LYB:
+ ret = lyb_print_data(out, root, options);
+ break;
+ case LYD_UNKNOWN:
+ LOGINT(root ? LYD_CTX(root) : NULL);
+ ret = LY_EINT;
+ break;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_all(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_CHECK_ARG_RET(NULL, out, !(options & LYD_PRINT_WITHSIBLINGS), LY_EINVAL);
+
+ /* reset the number of printed bytes */
+ out->func_printed = 0;
+
+ if (root) {
+ /* get first top-level sibling */
+ while (root->parent) {
+ root = lyd_parent(root);
+ }
+ while (root->prev->next) {
+ root = root->prev;
+ }
+ }
+
+ /* print each top-level sibling */
+ LY_CHECK_RET(lyd_print_(out, root, format, options | LYD_PRINT_WITHSIBLINGS));
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_tree(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_CHECK_ARG_RET(NULL, out, !(options & LYD_PRINT_WITHSIBLINGS), LY_EINVAL);
+
+ /* reset the number of printed bytes */
+ out->func_printed = 0;
+
+ /* print the subtree */
+ LY_CHECK_RET(lyd_print_(out, root, format, options));
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_mem(char **strp, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR ret;
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, strp, LY_EINVAL);
+
+ /* init */
+ *strp = NULL;
+
+ LY_CHECK_RET(ly_out_new_memory(strp, 0, &out));
+ ret = lyd_print_(out, root, format, options);
+ ly_out_free(out, NULL, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_fd(int fd, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR ret;
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, fd != -1, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_fd(fd, &out));
+ ret = lyd_print_(out, root, format, options);
+ ly_out_free(out, NULL, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_file(FILE *f, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR ret;
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, f, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_file(f, &out));
+ ret = lyd_print_(out, root, format, options);
+ ly_out_free(out, NULL, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_path(const char *path, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR ret;
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, path, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_filepath(path, &out));
+ ret = lyd_print_(out, root, format, options);
+ ly_out_free(out, NULL, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_print_clb(ly_write_clb writeclb, void *user_data, const struct lyd_node *root, LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR ret;
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, writeclb, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_clb(writeclb, user_data, &out));
+ ret = lyd_print_(out, root, format, options);
+ ly_out_free(out, NULL, 0);
+ return ret;
+}
diff --git a/src/printer_data.h b/src/printer_data.h
new file mode 100644
index 0000000..fb2a5fb
--- /dev/null
+++ b/src/printer_data.h
@@ -0,0 +1,196 @@
+/**
+ * @file printer_data.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Data printers for libyang
+ *
+ * Copyright (c) 2015-2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PRINTER_DATA_H_
+#define LY_PRINTER_DATA_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "log.h"
+#include "out.h"
+#include "tree_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_out;
+
+/**
+ * @page howtoDataPrinters Printing Data
+ *
+ * Data printers allows to serialize internal representation of a data tree in a specific format. libyang
+ * supports the following data formats for printing:
+ *
+ * - XML
+ *
+ * Basic format as specified in rules of mapping YANG modeled data to XML in
+ * [RFC 6020](http://tools.ietf.org/html/rfc6020).
+ *
+ * - JSON
+ *
+ * The alternative data format available in RESTCONF protocol. Specification of JSON encoding of data modeled by YANG
+ * can be found in [RFC 7951](https://tools.ietf.org/html/rfc7951).
+ *
+ * By default, both formats are printed with indentation (formatting), which can be avoided by ::LYD_PRINT_SHRINK
+ * [printer option](@ref dataprinterflags)). Other options adjust e.g. [with-defaults mode](@ref howtoDataWD).
+ *
+ * Besides the legacy functions from libyang 1.x (::lyd_print_clb(), ::lyd_print_fd(), ::lyd_print_file(), ::lyd_print_mem()
+ * and ::lyd_print_path()) printing data into the specified output, there are also new functions using
+ * [output handler](@ref howtoOutput) introduced in libyang 2.0. In contrast to
+ * [schema printers](@ref howtoSchemaPrinters), there is no limit of the functionality and the functions can be used
+ * interchangeable. The only think to note is that the new functions ::lyd_print_all() and ::lyd_print_tree() does not
+ * accept ::LYD_PRINT_WITHSIBLINGS [printer option](@ref dataprinterflags)) since this flag differentiate the functions
+ * themselves.
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_print_all()
+ * - ::lyd_print_tree()
+ * - ::lyd_print_mem()
+ * - ::lyd_print_fd()
+ * - ::lyd_print_file()
+ * - ::lyd_print_path()
+ * - ::lyd_print_clb()
+ */
+
+/**
+ * @ingroup datatree
+ * @defgroup dataprinterflags Data printer flags
+ *
+ * Options to change default behavior of the data printers.
+ *
+ * @{
+ */
+#define LYD_PRINT_WITHSIBLINGS 0x01 /**< Flag for printing also the (following) sibling nodes of the data node.
+ The flag is not allowed for ::lyd_print_all() and ::lyd_print_tree(). */
+#define LYD_PRINT_SHRINK LY_PRINT_SHRINK /**< Flag for output without indentation and formatting new lines. */
+#define LYD_PRINT_KEEPEMPTYCONT 0x04 /**< Preserve empty non-presence containers */
+#define LYD_PRINT_WD_MASK 0xF0 /**< Mask for with-defaults modes */
+#define LYD_PRINT_WD_EXPLICIT 0x00 /**< Explicit with-defaults mode. Only the data explicitly being present in
+ the data tree are printed, so the implicitly added default nodes are
+ not printed. Note that this is the default value when no WD option is
+ specified. */
+#define LYD_PRINT_WD_TRIM 0x10 /**< Trim mode avoids printing the nodes with the value equal to their
+ default value */
+#define LYD_PRINT_WD_ALL 0x20 /**< With this option, all the nodes are printed as none of them are
+ considered default */
+#define LYD_PRINT_WD_ALL_TAG 0x40 /**< Same as ::LYD_PRINT_WD_ALL but also adds attribute 'default' with value 'true' to
+ all nodes that has its default value. The 'default' attribute has namespace:
+ urn:ietf:params:xml:ns:netconf:default:1.0 and thus the attributes are
+ printed only when the ietf-netconf-with-defaults module is present in libyang
+ context (but in that case this namespace is always printed). */
+#define LYD_PRINT_WD_IMPL_TAG 0x80 /**< Same as ::LYD_PRINT_WD_ALL_TAG but the attributes are added only to the nodes that
+ are not explicitly present in the original data tree despite their
+ value is equal to their default value. There is the same limitation regarding
+ the presence of ietf-netconf-with-defaults module in libyang context. */
+/**
+ * @}
+ */
+
+/**
+ * @brief Print the whole data tree of the root, including all the siblings.
+ *
+ * @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
+ * @param[in] root The root element of the tree to print, can be any sibling.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags) except ::LYD_PRINT_WITHSIBLINGS.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_all(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Print the selected data subtree.
+ *
+ * @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
+ * @param[in] root The root element of the subtree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags) except ::LYD_PRINT_WITHSIBLINGS.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_tree(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Print data tree in the specified format.
+ *
+ * @param[out] strp Pointer to store the resulting dump.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_mem(char **strp, const struct lyd_node *root, LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Print data tree in the specified format.
+ *
+ * @param[in] fd File descriptor where to print the data.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_fd(int fd, const struct lyd_node *root, LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Print data tree in the specified format.
+ *
+ * @param[in] f File stream where to print the data.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_file(FILE *f, const struct lyd_node *root, LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Print data tree in the specified format.
+ *
+ * @param[in] path File path where to print the data.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_path(const char *path, const struct lyd_node *root, LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Print data tree in the specified format.
+ *
+ * @param[in] writeclb Callback function to write the data (see write(1)).
+ * @param[in] user_data Optional caller-specific argument to be passed to the \p writeclb callback.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_print_clb(ly_write_clb writeclb, void *user_data, const struct lyd_node *root,
+ LYD_FORMAT format, uint32_t options);
+
+/**
+ * @brief Check whether the node should be printed based on the printing options.
+ *
+ * @param[in] node Node to check.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return 0 if not,
+ * @return non-0 if should be printed.
+ */
+LIBYANG_API_DECL ly_bool lyd_node_should_print(const struct lyd_node *node, uint32_t options);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PRINTER_DATA_H_ */
diff --git a/src/printer_internal.h b/src/printer_internal.h
new file mode 100644
index 0000000..c835567
--- /dev/null
+++ b/src/printer_internal.h
@@ -0,0 +1,218 @@
+/**
+ * @file printer_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internal structures and functions for libyang
+ *
+ * Copyright (c) 2015-2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PRINTER_INTERNAL_H_
+#define LY_PRINTER_INTERNAL_H_
+
+#include "out.h"
+#include "plugins_exts.h"
+#include "printer_data.h"
+#include "printer_schema.h"
+
+struct lysp_module;
+struct lysp_submodule;
+
+/**
+ * @brief Generic YANG schema printer context
+ *
+ * Note that the YANG extensions API provides getter to the members for the extension plugins.
+ */
+struct lyspr_ctx {
+ struct ly_out *out; /**< output specification */
+ uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+ uint16_t flags; /**< internal flags for use by printer */
+ uint32_t options; /**< Schema output options (see @ref schemaprinterflags). */
+ const struct lys_module *module; /**< schema to print */
+};
+
+/**
+ * @brief YANG schema provided from plugin extension for printer_tree.
+ *
+ * The YANG extensions API provides setting functions.
+ */
+struct lyspr_tree_schema {
+ ly_bool compiled; /**< Flag if it is a compiled schema. */
+
+ union {
+ struct lysc_node *ctree; /**< Compiled schema. */
+ struct lysp_node *ptree; /**< Parsed schema. */
+ };
+ union {
+ lyplg_ext_sprinter_ctree_override_clb cn_overr; /**< Override clb function for compiled node. */
+ lyplg_ext_sprinter_ptree_override_clb pn_overr; /**< Override clb function for parsed node. */
+ };
+};
+
+/**
+ * @brief Context used between plugin extension and printer_tree.
+ *
+ * The YANG extensions API provides setting functions.
+ */
+struct lyspr_tree_ctx {
+ struct lyspr_tree_schema *schemas; /**< Parsed or compiled schemas ([sized array](@ref sizedarrays)) */
+ void *plugin_priv; /**< Private data from plugin which printer_tree does not use. */
+
+ void (*free_plugin_priv)(void *plugin_priv); /**< Release function for lyspr_tree_ctx.plugin_priv. */
+};
+
+/**
+ * @brief YANG printer of the parsed module. Full YANG printer.
+ *
+ * @param[in] out Output specification.
+ * @param[in] modp Parsed module to print.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR yang_print_parsed_module(struct ly_out *out, const struct lysp_module *modp, uint32_t options);
+
+/**
+ * @brief Helper macros for data printers
+ */
+#define DO_FORMAT (!(pctx->options & LY_PRINT_SHRINK))
+#define LEVEL pctx->level /**< current level */
+#define INDENT (DO_FORMAT ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
+#define LEVEL_INC LEVEL++ /**< increase indentation level */
+#define LEVEL_DEC LEVEL-- /**< decrease indentation level */
+
+#define XML_NS_INDENT 8
+
+/**
+ * @brief YANG printer of the parsed submodule. Full YANG printer.
+ *
+ * @param[in] out Output specification.
+ * @param[in] submodp Parsed submodule to print.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR yang_print_parsed_submodule(struct ly_out *out, const struct lysp_submodule *submodp, uint32_t options);
+
+/**
+ * @brief YANG printer of the compiled schemas.
+ *
+ * This printer provides information about modules how they are understood by libyang.
+ * Despite the format is inspired by YANG, it is not fully compatible and should not be
+ * used as a standard YANG format.
+ *
+ * @param[in] out Output specification.
+ * @param[in] module Schema to be printed (the compiled member is used).
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR yang_print_compiled(struct ly_out *out, const struct lys_module *module, uint32_t options);
+
+/**
+ * @brief YANG printer of the compiled schema node
+ *
+ * This printer provides information about modules how they are understood by libyang.
+ * Despite the format is inspired by YANG, it is not fully compatible and should not be
+ * used as a standard YANG format.
+ *
+ * @param[in] out Output specification.
+ * @param[in] node Schema node to be printed including all its substatements.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR yang_print_compiled_node(struct ly_out *out, const struct lysc_node *node, uint32_t options);
+
+/**
+ * @brief YIN printer of the parsed module. Full YIN printer.
+ *
+ * @param[in] out Output specification.
+ * @param[in] modp Parsed module to print.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR yin_print_parsed_module(struct ly_out *out, const struct lysp_module *modp, uint32_t options);
+
+/**
+ * @brief YIN printer of the parsed submodule. Full YIN printer.
+ *
+ * @param[in] out Output specification.
+ * @param[in] submodp Parsed submodule to print.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR yin_print_parsed_submodule(struct ly_out *out, const struct lysp_submodule *submodp, uint32_t options);
+
+/**
+ * @brief Full YANG Tree Diagram printer.
+ *
+ * The module should be compiled and the @ref contextoptions must be set to LY_CTX_SET_PRIV_PARSED.
+ * If not, the printer will use parsed (unresolved) YANG schema tree, which means,
+ * for example, that `grouping` sections will be on the output.
+ *
+ * @param[in] out Output specification.
+ * @param[in] module Main module.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for ::LYS_OUT_TREE printer.
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR tree_print_module(struct ly_out *out, const struct lys_module *module, uint32_t options,
+ size_t line_length);
+
+/**
+ * @brief YANG Tree Diagram printer of the parsed submodule. Full Tree printer.
+ *
+ * @param[in] out Output specification.
+ * @param[in] submodp Parsed submodule to print.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for ::LYS_OUT_TREE printer.
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR tree_print_parsed_submodule(struct ly_out *out, const struct lysp_submodule *submodp, uint32_t options,
+ size_t line_length);
+
+/**
+ * @brief YANG Tree Diagram printer of the compiled schema node.
+ *
+ * @param[in] out Output specification.
+ * @param[in] node Schema node to be printed including all its substatements.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for ::LYS_OUT_TREE printer.
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR tree_print_compiled_node(struct ly_out *out, const struct lysc_node *node, uint32_t options,
+ size_t line_length);
+
+/**
+ * @brief XML printer of YANG data.
+ *
+ * @param[in] out Output specification.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options);
+
+/**
+ * @brief JSON printer of YANG data.
+ *
+ * @param[in] out Output specification.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options);
+
+/**
+ * @brief LYB printer of YANG data.
+ *
+ * @param[in] out Output structure.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed.
+ */
+LY_ERR lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options);
+
+#endif /* LY_PRINTER_INTERNAL_H_ */
diff --git a/src/printer_json.c b/src/printer_json.c
new file mode 100644
index 0000000..327799b
--- /dev/null
+++ b/src/printer_json.c
@@ -0,0 +1,1010 @@
+/**
+ * @file printer_json.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief JSON printer for libyang data structure
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "context.h"
+#include "log.h"
+#include "out.h"
+#include "out_internal.h"
+#include "parser_data.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_types.h"
+#include "printer_data.h"
+#include "printer_internal.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+/**
+ * @brief JSON printer context.
+ */
+struct jsonpr_ctx {
+ struct ly_out *out; /**< output specification */
+ const struct lyd_node *root; /**< root node of the subtree being printed */
+ const struct lyd_node *parent; /**< parent of the node being printed */
+ uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+ uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
+ const struct ly_ctx *ctx; /**< libyang context */
+
+ uint16_t level_printed; /* level where some data were already printed */
+ struct ly_set open; /* currently open array(s) */
+ const struct lyd_node *print_sibling_metadata;
+};
+
+/**
+ * @brief Mark that something was already written in the current level,
+ * used to decide if a comma is expected between the items
+ */
+#define LEVEL_PRINTED pctx->level_printed = pctx->level
+
+#define PRINT_COMMA \
+ if (pctx->level_printed >= pctx->level) { \
+ ly_print_(pctx->out, ",%s", (DO_FORMAT ? "\n" : "")); \
+ }
+
+static LY_ERR json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node);
+
+/**
+ * @brief Compare 2 nodes, despite it is regular data node or an opaq node, and
+ * decide if they corresponds to the same schema node.
+ *
+ * @return 1 - matching nodes, 0 - non-matching nodes
+ */
+static int
+matching_node(const struct lyd_node *node1, const struct lyd_node *node2)
+{
+ assert(node1 || node2);
+
+ if (!node1 || !node2) {
+ return 0;
+ } else if (node1->schema != node2->schema) {
+ return 0;
+ }
+ if (!node1->schema) {
+ /* compare node names */
+ struct lyd_node_opaq *onode1 = (struct lyd_node_opaq *)node1;
+ struct lyd_node_opaq *onode2 = (struct lyd_node_opaq *)node2;
+
+ if ((onode1->name.name != onode2->name.name) || (onode1->name.prefix != onode2->name.prefix)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Open the JSON array ('[') for the specified @p node
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node First node of the array.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_array_open(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ ly_print_(pctx->out, "[%s", DO_FORMAT ? "\n" : "");
+ LY_CHECK_RET(ly_set_add(&pctx->open, (void *)node, 0, NULL));
+ LEVEL_INC;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get know if the array for the provided @p node is currently open.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to check.
+ * @return 1 in case the printer is currently in the array belonging to the provided @p node.
+ * @return 0 in case the provided @p node is not connected with the currently open array (or there is no open array).
+ */
+static int
+is_open_array(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ if (pctx->open.count && matching_node(node, pctx->open.dnodes[pctx->open.count - 1])) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @brief Close the most inner JSON array.
+ *
+ * @param[in] ctx JSON printer context.
+ */
+static void
+json_print_array_close(struct jsonpr_ctx *pctx)
+{
+ LEVEL_DEC;
+ ly_set_rm_index(&pctx->open, pctx->open.count - 1, NULL);
+ ly_print_(pctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
+}
+
+/**
+ * @brief Get the node's module name to use as the @p node prefix in JSON.
+ *
+ * @param[in] node Node to process.
+ * @return The name of the module where the @p node belongs, it can be NULL in case the module name
+ * cannot be determined (source format is XML and the refered namespace is unknown/not implemented in the current context).
+ */
+static const char *
+node_prefix(const struct lyd_node *node)
+{
+ if (node->schema) {
+ return node->schema->module->name;
+ } else {
+ struct lyd_node_opaq *onode = (struct lyd_node_opaq *)node;
+ const struct lys_module *mod;
+
+ switch (onode->format) {
+ case LY_VALUE_JSON:
+ return onode->name.module_name;
+ case LY_VALUE_XML:
+ mod = ly_ctx_get_module_implemented_ns(onode->ctx, onode->name.module_ns);
+ if (!mod) {
+ return NULL;
+ }
+ return mod->name;
+ default:
+ /* cannot be created */
+ LOGINT(LYD_CTX(node));
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Compare 2 nodes if the belongs to the same module (if they come from the same namespace)
+ *
+ * Accepts both regulard a well as opaq nodes.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @return 0 in case the nodes' modules are the same
+ * @return 1 in case the nodes belongs to different modules
+ */
+int
+json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
+{
+ assert(node1 || node2);
+
+ if (!node1 || !node2) {
+ return 1;
+ } else if (node1->schema && node2->schema) {
+ if (node1->schema->module == node2->schema->module) {
+ /* belongs to the same module */
+ return 0;
+ } else {
+ /* different modules */
+ return 1;
+ }
+ } else {
+ const char *pref1 = node_prefix(node1);
+ const char *pref2 = node_prefix(node2);
+
+ if ((pref1 && pref2) && (pref1 == pref2)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+}
+
+/**
+ * @brief Print the @p text as JSON string - encode special characters and add quotation around the string.
+ *
+ * @param[in] out The output handler.
+ * @param[in] text The string to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_string(struct ly_out *out, const char *text)
+{
+ uint64_t i, n;
+
+ if (!text) {
+ return LY_SUCCESS;
+ }
+
+ ly_write_(out, "\"", 1);
+ for (i = n = 0; text[i]; i++) {
+ const unsigned char ascii = text[i];
+
+ if (ascii < 0x20) {
+ /* control character */
+ ly_print_(out, "\\u%.4X", ascii);
+ } else {
+ switch (ascii) {
+ case '"':
+ ly_print_(out, "\\\"");
+ break;
+ case '\\':
+ ly_print_(out, "\\\\");
+ break;
+ default:
+ ly_write_(out, &text[i], 1);
+ n++;
+ }
+ }
+ }
+ ly_write_(out, "\"", 1);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print JSON object's member name, ending by ':'. It resolves if the prefix is supposed to be printed.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node The data node being printed.
+ * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr)
+{
+ PRINT_COMMA;
+ if ((LEVEL == 1) || json_nscmp(node, pctx->parent)) {
+ /* print "namespace" */
+ ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "",
+ node_prefix(node), node->schema->name, DO_FORMAT ? " " : "");
+ } else {
+ ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", node->schema->name, DO_FORMAT ? " " : "");
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief More generic alternative to json_print_member() to print some special cases of the member names.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] parent Parent node to compare modules deciding if the prefix is printed.
+ * @param[in] format Format to decide how to process the @p prefix.
+ * @param[in] name Name structure to provide name and prefix to print. If NULL, only "" name is printed.
+ * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VALUE_FORMAT format,
+ const struct ly_opaq_name *name, ly_bool is_attr)
+{
+ const char *module_name = NULL, *name_str;
+
+ PRINT_COMMA;
+
+ /* determine prefix string */
+ if (name) {
+ const struct lys_module *mod;
+
+ switch (format) {
+ case LY_VALUE_JSON:
+ module_name = name->module_name;
+ break;
+ case LY_VALUE_XML:
+ mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
+ if (mod) {
+ module_name = mod->name;
+ }
+ break;
+ default:
+ /* cannot be created */
+ LOGINT_RET(pctx->ctx);
+ }
+
+ name_str = name->name;
+ } else {
+ name_str = "";
+ }
+
+ /* print the member */
+ if (module_name && (!parent || (node_prefix(parent) != module_name))) {
+ ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "", module_name, name_str, DO_FORMAT ? " " : "");
+ } else {
+ ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", name_str, DO_FORMAT ? " " : "");
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print data value.
+ *
+ * @param[in] pctx JSON printer context.
+ * @param[in] ctx Context used to print the value.
+ * @param[in] val Data value to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val)
+{
+ ly_bool dynamic;
+ LY_DATA_TYPE basetype;
+ const char *value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
+
+ basetype = val->realtype->basetype;
+
+print_val:
+ /* leafref is not supported */
+ switch (basetype) {
+ case LY_TYPE_UNION:
+ /* use the resolved type */
+ basetype = val->subvalue->value.realtype->basetype;
+ goto print_val;
+
+ case LY_TYPE_BINARY:
+ case LY_TYPE_STRING:
+ case LY_TYPE_BITS:
+ case LY_TYPE_ENUM:
+ case LY_TYPE_INST:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ case LY_TYPE_DEC64:
+ case LY_TYPE_IDENT:
+ json_print_string(pctx->out, value);
+ break;
+
+ case LY_TYPE_INT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_BOOL:
+ ly_print_(pctx->out, "%s", value[0] ? value : "null");
+ break;
+
+ case LY_TYPE_EMPTY:
+ ly_print_(pctx->out, "[null]");
+ break;
+
+ default:
+ /* error */
+ LOGINT_RET(pctx->ctx);
+ }
+
+ if (dynamic) {
+ free((char *)value);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print all the attributes of the opaq node.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Opaq node where the attributes are placed.
+ * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node, const struct lys_module *wdmod)
+{
+ struct lyd_attr *attr;
+
+ if (wdmod) {
+ ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
+ LEVEL_PRINTED;
+ }
+
+ for (attr = node->attr; attr; attr = attr->next) {
+ json_print_member2(pctx, &node->node, attr->format, &attr->name, 0);
+
+ if (attr->hints & (LYD_VALHINT_STRING | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM | LYD_VALHINT_NUM64)) {
+ json_print_string(pctx->out, attr->value);
+ } else if (attr->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) {
+ ly_print_(pctx->out, "%s", attr->value[0] ? attr->value : "null");
+ } else if (attr->hints & LYD_VALHINT_EMPTY) {
+ ly_print_(pctx->out, "[null]");
+ } else {
+ /* unknown value format with no hints, use universal string */
+ json_print_string(pctx->out, attr->value);
+ }
+ LEVEL_PRINTED;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print all the metadata of the node.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Node where the metadata are placed.
+ * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_metadata(struct jsonpr_ctx *pctx, const struct lyd_node *node, const struct lys_module *wdmod)
+{
+ struct lyd_meta *meta;
+
+ if (wdmod) {
+ ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
+ LEVEL_PRINTED;
+ }
+
+ for (meta = node->meta; meta; meta = meta->next) {
+ PRINT_COMMA;
+ ly_print_(pctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value));
+ LEVEL_PRINTED;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print attributes/metadata of the given @p node. Accepts both regular as well as opaq nodes.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node where the attributes/metadata are placed.
+ * @param[in] inner Flag if the @p node is an inner node in the tree.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool inner)
+{
+ const struct lys_module *wdmod = NULL;
+
+ if (node->schema && (node->schema->nodetype != LYS_CONTAINER) && (node->flags & LYD_DEFAULT) &&
+ (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
+ /* we have implicit OR explicit default node */
+ /* get with-defaults module */
+ wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults");
+ }
+
+ if (node->schema && (node->meta || wdmod)) {
+ if (inner) {
+ LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1));
+ } else {
+ LY_CHECK_RET(json_print_member(pctx, node, 1));
+ }
+ ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
+ LEVEL_INC;
+ LY_CHECK_RET(json_print_metadata(pctx, node, wdmod));
+ LEVEL_DEC;
+ ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
+ LEVEL_PRINTED;
+ } else if (!node->schema && ((struct lyd_node_opaq *)node)->attr) {
+ if (inner) {
+ LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1));
+ } else {
+ LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), ((struct lyd_node_opaq *)node)->format,
+ &((struct lyd_node_opaq *)node)->name, 1));
+ }
+ ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
+ LEVEL_INC;
+ LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node, wdmod));
+ LEVEL_DEC;
+ ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
+ LEVEL_PRINTED;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print leaf data node including its metadata.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ LY_CHECK_RET(json_print_member(pctx, node, 0));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LEVEL_PRINTED;
+
+ /* print attributes as sibling */
+ json_print_attributes(pctx, node, 0);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print anydata/anyxml content.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] any Anydata node to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_any_content(struct jsonpr_ctx *pctx, struct lyd_node_any *any)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *iter;
+ const struct lyd_node *prev_parent;
+ uint32_t prev_opts, temp_lo = 0;
+
+ assert(any->schema->nodetype & LYD_NODE_ANY);
+
+ if ((any->schema->nodetype == LYS_ANYDATA) && (any->value_type != LYD_ANYDATA_DATATREE)) {
+ LOGINT_RET(pctx->ctx);
+ }
+
+ if (any->value_type == LYD_ANYDATA_LYB) {
+ uint32_t parser_options = LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT;
+
+ /* turn logging off */
+ ly_temp_log_options(&temp_lo);
+
+ /* try to parse it into a data tree */
+ if (lyd_parse_data_mem(pctx->ctx, any->value.mem, LYD_LYB, parser_options, 0, &iter) == LY_SUCCESS) {
+ /* successfully parsed */
+ free(any->value.mem);
+ any->value.tree = iter;
+ any->value_type = LYD_ANYDATA_DATATREE;
+ }
+
+ /* turn logging on again */
+ ly_temp_log_options(NULL);
+ }
+
+ switch (any->value_type) {
+ case LYD_ANYDATA_DATATREE:
+ /* print as an object */
+ ly_print_(pctx->out, "{%s", DO_FORMAT ? "\n" : "");
+ LEVEL_INC;
+
+ /* close opening tag and print data */
+ prev_parent = pctx->parent;
+ prev_opts = pctx->options;
+ pctx->parent = &any->node;
+ pctx->options &= ~LYD_PRINT_WITHSIBLINGS;
+ LY_LIST_FOR(any->value.tree, iter) {
+ ret = json_print_node(pctx, iter);
+ LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
+ }
+ pctx->parent = prev_parent;
+ pctx->options = prev_opts;
+
+ /* terminate the object */
+ LEVEL_DEC;
+ if (DO_FORMAT) {
+ ly_print_(pctx->out, "\n%*s}", INDENT);
+ } else {
+ ly_print_(pctx->out, "}");
+ }
+ break;
+ case LYD_ANYDATA_JSON:
+ if (!any->value.json) {
+ /* no content */
+ if (any->schema->nodetype == LYS_ANYXML) {
+ ly_print_(pctx->out, "null");
+ } else {
+ ly_print_(pctx->out, "{}");
+ }
+ } else {
+ /* print without escaping special characters */
+ ly_print_(pctx->out, "%s", any->value.json);
+ }
+ break;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ if (!any->value.str) {
+ /* no content */
+ if (any->schema->nodetype == LYS_ANYXML) {
+ ly_print_(pctx->out, "null");
+ } else {
+ ly_print_(pctx->out, "{}");
+ }
+ } else {
+ /* print as a string */
+ json_print_string(pctx->out, any->value.str);
+ }
+ break;
+ case LYD_ANYDATA_LYB:
+ /* LYB format is not supported */
+ LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as JSON.", any->value_type);
+ break;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print content of a single container/list data node including its metadata.
+ * The envelope specific to nodes are expected to be printed by the caller.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_inner(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ struct lyd_node *child;
+ const struct lyd_node *prev_parent;
+ struct lyd_node_opaq *opaq = NULL;
+ ly_bool has_content = 0;
+
+ LY_LIST_FOR(lyd_child(node), child) {
+ if (lyd_node_should_print(child, pctx->options)) {
+ break;
+ }
+ }
+ if (node->meta || child) {
+ has_content = 1;
+ }
+ if (!node->schema) {
+ opaq = (struct lyd_node_opaq *)node;
+ }
+
+ if ((node->schema && (node->schema->nodetype == LYS_LIST)) ||
+ (opaq && (opaq->hints != LYD_HINT_DATA) && (opaq->hints & LYD_NODEHINT_LIST))) {
+ ly_print_(pctx->out, "%s%*s{%s", (is_open_array(pctx, node) && (pctx->level_printed >= pctx->level)) ?
+ (DO_FORMAT ? ",\n" : ",") : "", INDENT, (DO_FORMAT && has_content) ? "\n" : "");
+ } else {
+ ly_print_(pctx->out, "%s{%s", (is_open_array(pctx, node) && (pctx->level_printed >= pctx->level)) ? "," : "",
+ (DO_FORMAT && has_content) ? "\n" : "");
+ }
+ LEVEL_INC;
+
+ json_print_attributes(pctx, node, 1);
+
+ /* print children */
+ prev_parent = pctx->parent;
+ pctx->parent = node;
+ LY_LIST_FOR(lyd_child(node), child) {
+ LY_CHECK_RET(json_print_node(pctx, child));
+ }
+ pctx->parent = prev_parent;
+
+ LEVEL_DEC;
+ if (DO_FORMAT && has_content) {
+ ly_print_(pctx->out, "\n%*s}", INDENT);
+ } else {
+ ly_print_(pctx->out, "}");
+ }
+ LEVEL_PRINTED;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print container data node including its metadata.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to print.
+ * @return LY_ERR value.
+ */
+static int
+json_print_container(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ LY_CHECK_RET(json_print_member(pctx, node, 0));
+ LY_CHECK_RET(json_print_inner(pctx, node));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print anydata/anyxml data node including its metadata.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to print.
+ * @return LY_ERR value.
+ */
+static int
+json_print_any(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ LY_CHECK_RET(json_print_member(pctx, node, 0));
+ LY_CHECK_RET(json_print_any_content(pctx, (struct lyd_node_any *)node));
+ LEVEL_PRINTED;
+
+ /* print attributes as sibling */
+ json_print_attributes(pctx, node, 0);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check whether a node is the last printed instance of a (leaf-)list.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Last printed node.
+ * @return Whether it is the last printed instance or not.
+ */
+static ly_bool
+json_print_array_is_last_inst(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ if (!is_open_array(pctx, node)) {
+ /* no array open */
+ return 0;
+ }
+
+ if ((pctx->root == node) && !(pctx->options & LYD_PRINT_WITHSIBLINGS)) {
+ /* the only printed instance */
+ return 1;
+ }
+
+ if (!node->next || (node->next->schema != node->schema)) {
+ /* last instance */
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Print single leaf-list or list instance.
+ *
+ * In case of list, metadata are printed inside the list object. For the leaf-list,
+ * metadata are marked in the context for later printing after closing the array next to it using
+ * json_print_metadata_leaflist().
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ const struct lys_module *wdmod = NULL;
+
+ if (!is_open_array(pctx, node)) {
+ LY_CHECK_RET(json_print_member(pctx, node, 0));
+ LY_CHECK_RET(json_print_array_open(pctx, node));
+ if (node->schema->nodetype == LYS_LEAFLIST) {
+ ly_print_(pctx->out, "%*s", INDENT);
+ }
+ } else if (node->schema->nodetype == LYS_LEAFLIST) {
+ ly_print_(pctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
+ }
+
+ if (node->schema->nodetype == LYS_LIST) {
+ /* print list's content */
+ LY_CHECK_RET(json_print_inner(pctx, node));
+ } else {
+ assert(node->schema->nodetype == LYS_LEAFLIST);
+
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+
+ if (!pctx->print_sibling_metadata) {
+ if ((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
+ /* we have implicit OR explicit default node, get with-defaults module */
+ wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults");
+ }
+ if (node->meta || wdmod) {
+ /* we will be printing metadata for these siblings */
+ pctx->print_sibling_metadata = node;
+ }
+ }
+ }
+
+ if (json_print_array_is_last_inst(pctx, node)) {
+ json_print_array_close(pctx);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print leaf-list's metadata in case they were marked in the last call to json_print_leaf_list().
+ * This function is supposed to be called when the leaf-list array is closed.
+ *
+ * @param[in] ctx JSON printer context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
+{
+ const struct lyd_node *prev, *node, *iter;
+ const struct lys_module *wdmod = NULL;
+
+ if (!pctx->print_sibling_metadata) {
+ return LY_SUCCESS;
+ }
+
+ if (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG)) {
+ /* get with-defaults module */
+ wdmod = ly_ctx_get_module_implemented(pctx->ctx, "ietf-netconf-with-defaults");
+ }
+
+ /* node is the first instance of the leaf-list */
+ for (node = pctx->print_sibling_metadata, prev = pctx->print_sibling_metadata->prev;
+ prev->next && matching_node(node, prev);
+ node = prev, prev = node->prev) {}
+
+ LY_CHECK_RET(json_print_member(pctx, node, 1));
+ ly_print_(pctx->out, "[%s", (DO_FORMAT ? "\n" : ""));
+ LEVEL_INC;
+ LY_LIST_FOR(node, iter) {
+ PRINT_COMMA;
+ if (iter->meta || (iter->flags & LYD_DEFAULT)) {
+ ly_print_(pctx->out, "%*s%s", INDENT, DO_FORMAT ? "{\n" : "{");
+ LEVEL_INC;
+ LY_CHECK_RET(json_print_metadata(pctx, iter, (iter->flags & LYD_DEFAULT) ? wdmod : NULL));
+ LEVEL_DEC;
+ ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
+ } else {
+ ly_print_(pctx->out, "null");
+ }
+ LEVEL_PRINTED;
+ if (!matching_node(iter, iter->next)) {
+ break;
+ }
+ }
+ LEVEL_DEC;
+ ly_print_(pctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
+ LEVEL_PRINTED;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print opaq data node including its attributes.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Opaq node to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_opaq(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
+{
+ ly_bool first = 1, last = 1;
+
+ if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
+ if (node->prev->next && matching_node(node->prev, &node->node)) {
+ first = 0;
+ }
+ if (node->next && matching_node(&node->node, node->next)) {
+ last = 0;
+ }
+ }
+
+ if (first) {
+ LY_CHECK_RET(json_print_member2(pctx, pctx->parent, node->format, &node->name, 0));
+
+ if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
+ LY_CHECK_RET(json_print_array_open(pctx, &node->node));
+ }
+ if (node->hints & LYD_NODEHINT_LEAFLIST) {
+ ly_print_(pctx->out, "%*s", INDENT);
+ }
+ } else if (node->hints & LYD_NODEHINT_LEAFLIST) {
+ ly_print_(pctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
+ }
+ if (node->child || (node->hints & LYD_NODEHINT_LIST)) {
+ LY_CHECK_RET(json_print_inner(pctx, &node->node));
+ LEVEL_PRINTED;
+ } else {
+ if (node->hints & LYD_VALHINT_EMPTY) {
+ ly_print_(pctx->out, "[null]");
+ } else if ((node->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) && !(node->hints & LYD_VALHINT_NUM64)) {
+ ly_print_(pctx->out, "%s", node->value);
+ } else {
+ /* string or a large number */
+ ly_print_(pctx->out, "\"%s\"", node->value);
+ }
+ LEVEL_PRINTED;
+
+ /* attributes */
+ json_print_attributes(pctx, (const struct lyd_node *)node, 0);
+
+ }
+ if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
+ json_print_array_close(pctx);
+ LEVEL_PRINTED;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print all the types of data node including its metadata.
+ *
+ * @param[in] ctx JSON printer context.
+ * @param[in] node Data node to print.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
+{
+ if (!lyd_node_should_print(node, pctx->options)) {
+ if (json_print_array_is_last_inst(pctx, node)) {
+ json_print_array_close(pctx);
+ }
+ return LY_SUCCESS;
+ }
+
+ if (!node->schema) {
+ LY_CHECK_RET(json_print_opaq(pctx, (const struct lyd_node_opaq *)node));
+ } else {
+ switch (node->schema->nodetype) {
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ case LYS_CONTAINER:
+ LY_CHECK_RET(json_print_container(pctx, node));
+ break;
+ case LYS_LEAF:
+ LY_CHECK_RET(json_print_leaf(pctx, node));
+ break;
+ case LYS_LEAFLIST:
+ case LYS_LIST:
+ LY_CHECK_RET(json_print_leaf_list(pctx, node));
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ LY_CHECK_RET(json_print_any(pctx, node));
+ break;
+ default:
+ LOGINT(pctx->ctx);
+ return EXIT_FAILURE;
+ }
+ }
+
+ pctx->level_printed = pctx->level;
+
+ if (pctx->print_sibling_metadata && !matching_node(node->next, pctx->print_sibling_metadata)) {
+ json_print_metadata_leaflist(pctx);
+ pctx->print_sibling_metadata = NULL;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
+{
+ const struct lyd_node *node;
+ struct jsonpr_ctx pctx = {0};
+ const char *delimiter = (options & LYD_PRINT_SHRINK) ? "" : "\n";
+
+ if (!root) {
+ ly_print_(out, "{}%s", delimiter);
+ ly_print_flush(out);
+ return LY_SUCCESS;
+ }
+
+ pctx.out = out;
+ pctx.parent = NULL;
+ pctx.level = 1;
+ pctx.level_printed = 0;
+ pctx.options = options;
+ pctx.ctx = LYD_CTX(root);
+
+ /* start */
+ ly_print_(pctx.out, "{%s", delimiter);
+
+ /* content */
+ LY_LIST_FOR(root, node) {
+ pctx.root = node;
+ LY_CHECK_RET(json_print_node(&pctx, node));
+ if (!(options & LYD_PRINT_WITHSIBLINGS)) {
+ break;
+ }
+ }
+
+ /* end */
+ ly_print_(out, "%s}%s", delimiter, delimiter);
+
+ assert(!pctx.open.count);
+ ly_set_erase(&pctx.open, NULL);
+
+ ly_print_flush(out);
+ return LY_SUCCESS;
+}
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
new file mode 100644
index 0000000..686c2d8
--- /dev/null
+++ b/src/printer_lyb.c
@@ -0,0 +1,1335 @@
+/**
+ * @file printer_lyb.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief LYB printer for libyang data structure
+ *
+ * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "lyb.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "hash_table.h"
+#include "log.h"
+#include "out.h"
+#include "out_internal.h"
+#include "plugins_exts/metadata.h"
+#include "printer_data.h"
+#include "printer_internal.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+
+static LY_ERR lyb_print_siblings(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx);
+
+/**
+ * @brief Hash table equal callback for checking hash equality only.
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lyb_hash_equal_cb(void *UNUSED(val1_p), void *UNUSED(val2_p), ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ /* for this purpose, if hash matches, the value does also, we do not want 2 values to have the same hash */
+ return 1;
+}
+
+/**
+ * @brief Hash table equal callback for checking value pointer equality only.
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lyb_ptr_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct lysc_node *val1 = *(struct lysc_node **)val1_p;
+ struct lysc_node *val2 = *(struct lysc_node **)val2_p;
+
+ if (val1 == val2) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Check that sibling collision hash is safe to insert into hash table.
+ *
+ * @param[in] ht Hash table.
+ * @param[in] sibling Hashed sibling.
+ * @param[in] ht_col_id Sibling hash collision ID.
+ * @param[in] compare_col_id Last collision ID to compare with.
+ * @return LY_SUCCESS when the whole hash sequence does not collide,
+ * @return LY_EEXIST when the whole hash sequence sollides.
+ */
+static LY_ERR
+lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, LYB_HASH ht_col_id, LYB_HASH compare_col_id)
+{
+ struct lysc_node **col_node;
+
+ /* get the first node inserted with last hash col ID ht_col_id */
+ if (lyht_find(ht, &sibling, lyb_get_hash(sibling, ht_col_id), (void **)&col_node)) {
+ /* there is none. valid situation */
+ return LY_SUCCESS;
+ }
+
+ lyht_set_cb(ht, lyb_ptr_equal_cb);
+ do {
+ int64_t j;
+
+ for (j = (int64_t)compare_col_id; j > -1; --j) {
+ if (lyb_get_hash(sibling, j) != lyb_get_hash(*col_node, j)) {
+ /* one non-colliding hash */
+ break;
+ }
+ }
+ if (j == -1) {
+ /* all whole hash sequences of nodes inserted with last hash col ID compare_col_id collide */
+ lyht_set_cb(ht, lyb_hash_equal_cb);
+ return LY_EEXIST;
+ }
+
+ /* get next node inserted with last hash col ID ht_col_id */
+ } while (!lyht_find_next_with_collision_cb(ht, col_node, lyb_get_hash(*col_node, ht_col_id), lyb_hash_equal_cb,
+ (void **)&col_node));
+
+ lyht_set_cb(ht, lyb_hash_equal_cb);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Hash all the siblings and add them also into a separate hash table.
+ *
+ * @param[in] sibling Any sibling in all the siblings on one level.
+ * @param[out] ht_p Created hash table.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
+{
+ struct hash_table *ht;
+ const struct lysc_node *parent;
+ const struct lys_module *mod;
+ LYB_HASH i;
+ uint32_t getnext_opts;
+
+ ht = lyht_new(1, sizeof(struct lysc_node *), lyb_hash_equal_cb, NULL, 1);
+ LY_CHECK_ERR_RET(!ht, LOGMEM(sibling->module->ctx), LY_EMEM);
+
+ getnext_opts = 0;
+ if (sibling->flags & LYS_IS_OUTPUT) {
+ getnext_opts = LYS_GETNEXT_OUTPUT;
+ }
+
+ parent = lysc_data_parent(sibling);
+ mod = sibling->module;
+
+ sibling = NULL;
+ while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, getnext_opts))) {
+ /* find the first non-colliding hash (or specifically non-colliding hash sequence) */
+ for (i = 0; i < LYB_HASH_BITS; ++i) {
+ /* check that we are not colliding with nodes inserted with a lower collision ID than ours */
+ int64_t j;
+
+ for (j = (int64_t)i - 1; j > -1; --j) {
+ if (lyb_hash_sequence_check(ht, sibling, (LYB_HASH)j, i)) {
+ break;
+ }
+ }
+ if (j > -1) {
+ /* some check failed, we must use a higher collision ID */
+ continue;
+ }
+
+ /* try to insert node with the current collision ID */
+ if (!lyht_insert_with_resize_cb(ht, &sibling, lyb_get_hash(sibling, i), lyb_ptr_equal_cb, NULL)) {
+ /* success, no collision */
+ break;
+ }
+
+ /* make sure we really cannot insert it with this hash col ID (meaning the whole hash sequence is colliding) */
+ if (i && !lyb_hash_sequence_check(ht, sibling, i, i)) {
+ /* it can be inserted after all, even though there is already a node with the same last collision ID */
+ lyht_set_cb(ht, lyb_ptr_equal_cb);
+ if (lyht_insert(ht, &sibling, lyb_get_hash(sibling, i), NULL)) {
+ LOGINT(sibling->module->ctx);
+ lyht_set_cb(ht, lyb_hash_equal_cb);
+ lyht_free(ht);
+ return LY_EINT;
+ }
+ lyht_set_cb(ht, lyb_hash_equal_cb);
+ break;
+ }
+ /* there is still another colliding schema node with the same hash sequence, try higher collision ID */
+ }
+
+ if (i == LYB_HASH_BITS) {
+ /* wow */
+ LOGINT(sibling->module->ctx);
+ lyht_free(ht);
+ return LY_EINT;
+ }
+ }
+
+ /* change val equal callback so that the HT is usable for finding value hashes */
+ lyht_set_cb(ht, lyb_ptr_equal_cb);
+
+ *ht_p = ht;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Find node hash in a hash table.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] node Node to find.
+ * @param[out] hash_p First non-colliding hash found.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_hash_find(struct hash_table *ht, struct lysc_node *node, LYB_HASH *hash_p)
+{
+ LYB_HASH hash;
+ uint32_t i;
+
+ for (i = 0; i < LYB_HASH_BITS; ++i) {
+ hash = lyb_get_hash(node, i);
+ if (!hash) {
+ LOGINT_RET(node->module->ctx);
+ }
+
+ if (!lyht_find(ht, &node, hash, NULL)) {
+ /* success, no collision */
+ break;
+ }
+ }
+ /* cannot happen, we already calculated the hash */
+ if (i == LYB_HASH_BITS) {
+ LOGINT_RET(node->module->ctx);
+ }
+
+ *hash_p = hash;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Write metadata about siblings.
+ *
+ * @param[in] out Out structure.
+ * @param[in] sib Contains metadata that is written.
+ */
+static LY_ERR
+lyb_write_sibling_meta(struct ly_out *out, struct lyd_lyb_sibling *sib)
+{
+ uint8_t meta_buf[LYB_META_BYTES];
+ uint64_t num = 0;
+
+ /* write the meta chunk information */
+ num = htole64((uint64_t)sib->written & LYB_SIZE_MAX);
+ memcpy(meta_buf, &num, LYB_SIZE_BYTES);
+ num = htole64((uint64_t)sib->inner_chunks & LYB_INCHUNK_MAX);
+ memcpy(meta_buf + LYB_SIZE_BYTES, &num, LYB_INCHUNK_BYTES);
+
+ LY_CHECK_RET(ly_write_skipped(out, sib->position, (char *)&meta_buf, LYB_META_BYTES));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Write LYB data fully handling the metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] buf Source buffer.
+ * @param[in] count Number of bytes to write.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write(struct ly_out *out, const uint8_t *buf, size_t count, struct lylyb_ctx *lybctx)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_lyb_sibling *full, *iter;
+ size_t to_write;
+
+ while (1) {
+ /* check for full data chunks */
+ to_write = count;
+ full = NULL;
+ LY_ARRAY_FOR(lybctx->siblings, u) {
+ /* we want the innermost chunks resolved first, so replace previous full chunks */
+ if (lybctx->siblings[u].written + to_write >= LYB_SIZE_MAX) {
+ /* full chunk, do not write more than allowed */
+ to_write = LYB_SIZE_MAX - lybctx->siblings[u].written;
+ full = &lybctx->siblings[u];
+ }
+ }
+
+ if (!full && !count) {
+ break;
+ }
+
+ /* we are actually writing some data, not just finishing another chunk */
+ if (to_write) {
+ LY_CHECK_RET(ly_write_(out, (char *)buf, to_write));
+
+ LY_ARRAY_FOR(lybctx->siblings, u) {
+ /* increase all written counters */
+ lybctx->siblings[u].written += to_write;
+ assert(lybctx->siblings[u].written <= LYB_SIZE_MAX);
+ }
+ /* decrease count/buf */
+ count -= to_write;
+ buf += to_write;
+ }
+
+ if (full) {
+ /* write the meta information (inner chunk count and chunk size) */
+ LY_CHECK_RET(lyb_write_sibling_meta(out, full));
+
+ /* zero written and inner chunks */
+ full->written = 0;
+ full->inner_chunks = 0;
+
+ /* skip space for another chunk size */
+ LY_CHECK_RET(ly_write_skip(out, LYB_META_BYTES, &full->position));
+
+ /* increase inner chunk count */
+ for (iter = &lybctx->siblings[0]; iter != full; ++iter) {
+ if (iter->inner_chunks == LYB_INCHUNK_MAX) {
+ LOGINT(lybctx->ctx);
+ return LY_EINT;
+ }
+ ++iter->inner_chunks;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Stop the current "siblings" - write its final metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_stop_siblings(struct ly_out *out, struct lylyb_ctx *lybctx)
+{
+ /* write the meta chunk information */
+ lyb_write_sibling_meta(out, &LYB_LAST_SIBLING(lybctx));
+
+ LY_ARRAY_DECREMENT(lybctx->siblings);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Start a new "siblings" - skip bytes for its metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_start_siblings(struct ly_out *out, struct lylyb_ctx *lybctx)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ u = LY_ARRAY_COUNT(lybctx->siblings);
+ if (u == lybctx->sibling_size) {
+ LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->siblings, u + LYB_SIBLING_STEP, LY_EMEM);
+ lybctx->sibling_size = u + LYB_SIBLING_STEP;
+ }
+
+ LY_ARRAY_INCREMENT(lybctx->siblings);
+ LYB_LAST_SIBLING(lybctx).written = 0;
+ LYB_LAST_SIBLING(lybctx).inner_chunks = 0;
+
+ /* another inner chunk */
+ for (u = 0; u < LY_ARRAY_COUNT(lybctx->siblings) - 1; ++u) {
+ if (lybctx->siblings[u].inner_chunks == LYB_INCHUNK_MAX) {
+ LOGINT(lybctx->ctx);
+ return LY_EINT;
+ }
+ ++lybctx->siblings[u].inner_chunks;
+ }
+
+ LY_CHECK_RET(ly_write_skip(out, LYB_META_BYTES, &LYB_LAST_SIBLING(lybctx).position));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Write a number.
+ *
+ * @param[in] num Number to write.
+ * @param[in] bytes Actual accessible bytes of @p num.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_number(uint64_t num, size_t bytes, struct ly_out *out, struct lylyb_ctx *lybctx)
+{
+ /* correct byte order */
+ num = htole64(num);
+
+ return lyb_write(out, (uint8_t *)&num, bytes, lybctx);
+}
+
+/**
+ * @brief Write a string.
+ *
+ * @param[in] str String to write.
+ * @param[in] str_len Length of @p str.
+ * @param[in] len_size Size of @p str_len in bytes.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_string(const char *str, size_t str_len, uint8_t len_size, struct ly_out *out, struct lylyb_ctx *lybctx)
+{
+ ly_bool error;
+
+ if (!str) {
+ str = "";
+ LY_CHECK_ERR_RET(str_len, LOGINT(lybctx->ctx), LY_EINT);
+ }
+
+ if (!str_len) {
+ str_len = strlen(str);
+ }
+
+ switch (len_size) {
+ case sizeof(uint8_t):
+ error = str_len > UINT8_MAX;
+ break;
+ case sizeof(uint16_t):
+ error = str_len > UINT16_MAX;
+ break;
+ case sizeof(uint32_t):
+ error = str_len > UINT32_MAX;
+ break;
+ case sizeof(uint64_t):
+ error = str_len > UINT64_MAX;
+ break;
+ default:
+ error = 1;
+ }
+ if (error) {
+ LOGINT(lybctx->ctx);
+ return LY_EINT;
+ }
+
+ LY_CHECK_RET(lyb_write_number(str_len, len_size, out, lybctx));
+
+ LY_CHECK_RET(lyb_write(out, (const uint8_t *)str, str_len, lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print YANG module info.
+ *
+ * @param[in] out Out structure.
+ * @param[in] mod Module to print.
+ * @param[in] with_features Whether to also print enabled features or not.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_model(struct ly_out *out, const struct lys_module *mod, ly_bool with_features, struct lylyb_ctx *lybctx)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint16_t revision;
+ struct ly_set feat_set = {0};
+ struct lysp_feature *f = NULL;
+ uint32_t i = 0;
+ int r;
+
+ /* model name length and model name */
+ LY_CHECK_GOTO(rc = lyb_write_string(mod->name, 0, sizeof(uint16_t), out, lybctx), cleanup);
+
+ /* model revision as XXXX XXXX XXXX XXXX (2B) (year is offset from 2000)
+ * YYYY YYYM MMMD DDDD */
+ revision = 0;
+ if (mod->revision) {
+ r = atoi(mod->revision);
+ r -= LYB_REV_YEAR_OFFSET;
+ r <<= LYB_REV_YEAR_SHIFT;
+
+ revision |= r;
+
+ r = atoi(mod->revision + ly_strlen_const("YYYY-"));
+ r <<= LYB_REV_MONTH_SHIFT;
+
+ revision |= r;
+
+ r = atoi(mod->revision + ly_strlen_const("YYYY-MM-"));
+
+ revision |= r;
+ }
+ LY_CHECK_GOTO(rc = lyb_write_number(revision, sizeof revision, out, lybctx), cleanup);
+
+ if (with_features) {
+ /* collect enabled module features */
+ while ((f = lysp_feature_next(f, mod->parsed, &i))) {
+ if (f->flags & LYS_FENABLED) {
+ LY_CHECK_GOTO(rc = ly_set_add(&feat_set, f, 1, NULL), cleanup);
+ }
+ }
+
+ /* print enabled feature count and their names */
+ LY_CHECK_GOTO(rc = lyb_write_number(feat_set.count, sizeof(uint16_t), out, lybctx), cleanup);
+ for (i = 0; i < feat_set.count; ++i) {
+ f = feat_set.objs[i];
+ LY_CHECK_GOTO(rc = lyb_write_string(f->name, 0, sizeof(uint16_t), out, lybctx), cleanup);
+ }
+ }
+
+ /* fill cached hashes, if not already */
+ lyb_cache_module_hash(mod);
+
+cleanup:
+ ly_set_erase(&feat_set, NULL);
+ return rc;
+}
+
+/**
+ * @brief Print all used YANG modules.
+ *
+ * @param[in] out Out structure.
+ * @param[in] root Data root.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_data_models(struct ly_out *out, const struct lyd_node *root, struct lylyb_ctx *lybctx)
+{
+ struct ly_set *set;
+ LY_ARRAY_COUNT_TYPE u;
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *mod;
+ const struct lyd_node *elem, *node;
+ uint32_t i;
+
+ LY_CHECK_RET(ly_set_new(&set));
+
+ /* collect all data node modules */
+ LY_LIST_FOR(root, elem) {
+ LYD_TREE_DFS_BEGIN(elem, node) {
+ if (node->schema) {
+ mod = node->schema->module;
+ ret = ly_set_add(set, mod, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add also their modules deviating or augmenting them */
+ LY_ARRAY_FOR(mod->deviated_by, u) {
+ ret = ly_set_add(set, mod->deviated_by[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ LY_ARRAY_FOR(mod->augmented_by, u) {
+ ret = ly_set_add(set, mod->augmented_by[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* only top-level nodes are processed */
+ LYD_TREE_DFS_continue = 1;
+ }
+
+ LYD_TREE_DFS_END(elem, node);
+ }
+ }
+
+ /* now write module count on 2 bytes */
+ LY_CHECK_GOTO(ret = lyb_write_number(set->count, 2, out, lybctx), cleanup);
+
+ /* and all the used models */
+ for (i = 0; i < set->count; ++i) {
+ LY_CHECK_GOTO(ret = lyb_print_model(out, set->objs[i], 1, lybctx), cleanup);
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ return ret;
+}
+
+/**
+ * @brief Print LYB magic number.
+ *
+ * @param[in] out Out structure.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_magic_number(struct ly_out *out)
+{
+ /* 'l', 'y', 'b' - 0x6c7962 */
+ char magic_number[] = {'l', 'y', 'b'};
+
+ LY_CHECK_RET(ly_write_(out, magic_number, 3));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print LYB header.
+ *
+ * @param[in] out Out structure.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_header(struct ly_out *out)
+{
+ uint8_t byte = 0;
+
+ /* version, future flags */
+ byte |= LYB_VERSION_NUM;
+
+ LY_CHECK_RET(ly_write_(out, (char *)&byte, 1));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print prefix data.
+ *
+ * @param[in] out Out structure.
+ * @param[in] format Value prefix format.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_prefix_data(struct ly_out *out, LY_VALUE_FORMAT format, const void *prefix_data, struct lylyb_ctx *lybctx)
+{
+ const struct ly_set *set;
+ const struct lyxml_ns *ns;
+ uint32_t i;
+
+ switch (format) {
+ case LY_VALUE_XML:
+ set = prefix_data;
+ if (!set) {
+ /* no prefix data */
+ i = 0;
+ LY_CHECK_RET(lyb_write(out, (uint8_t *)&i, 1, lybctx));
+ break;
+ }
+ if (set->count > UINT8_MAX) {
+ LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of prefixes is %u.", UINT8_MAX);
+ return LY_EINT;
+ }
+
+ /* write number of prefixes on 1 byte */
+ LY_CHECK_RET(lyb_write_number(set->count, 1, out, lybctx));
+
+ /* write all the prefixes */
+ for (i = 0; i < set->count; ++i) {
+ ns = set->objs[i];
+
+ /* prefix */
+ LY_CHECK_RET(lyb_write_string(ns->prefix, 0, sizeof(uint16_t), out, lybctx));
+
+ /* namespace */
+ LY_CHECK_RET(lyb_write_string(ns->uri, 0, sizeof(uint16_t), out, lybctx));
+ }
+ break;
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ /* nothing to print */
+ break;
+ default:
+ LOGINT_RET(lybctx->ctx);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print term node.
+ *
+ * @param[in] term Node to print.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_term_value(struct lyd_node_term *term, struct ly_out *out, struct lylyb_ctx *lybctx)
+{
+ LY_ERR ret = LY_SUCCESS;
+ ly_bool dynamic = 0;
+ void *value;
+ size_t value_len = 0;
+ int32_t lyb_data_len;
+ lyplg_type_print_clb print;
+
+ assert(term->value.realtype && term->value.realtype->plugin && term->value.realtype->plugin->print &&
+ term->schema);
+
+ /* Get length of LYB data to print. */
+ lyb_data_len = term->value.realtype->plugin->lyb_data_len;
+
+ /* Get value and also print its length only if size is not fixed. */
+ print = term->value.realtype->plugin->print;
+ if (lyb_data_len < 0) {
+ /* Variable-length data. */
+
+ /* Get value and its length from plugin. */
+ value = (void *)print(term->schema->module->ctx, &term->value,
+ LY_VALUE_LYB, NULL, &dynamic, &value_len);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (value_len > UINT32_MAX) {
+ LOGERR(lybctx->ctx, LY_EINT, "The maximum length of the LYB data "
+ "from a term node must not exceed %lu.", UINT32_MAX);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+
+ /* Print the length of the data as 64-bit unsigned integer. */
+ ret = lyb_write_number(value_len, sizeof(uint64_t), out, lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* Fixed-length data. */
+
+ /* Get value from plugin. */
+ value = (void *)print(term->schema->module->ctx, &term->value,
+ LY_VALUE_LYB, NULL, &dynamic, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* Copy the length from the compiled node. */
+ value_len = lyb_data_len;
+ }
+
+ /* Print value. */
+ if (value_len > 0) {
+ /* Print the value simply as it is. */
+ ret = lyb_write(out, value, value_len, lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (dynamic) {
+ free(value);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Print YANG node metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Data node whose metadata to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_metadata(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ uint8_t count = 0;
+ const struct lys_module *wd_mod = NULL;
+ struct lyd_meta *iter;
+
+ /* with-defaults */
+ if (node->schema->nodetype & LYD_NODE_TERM) {
+ if (((node->flags & LYD_DEFAULT) && (lybctx->print_options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
+ ((lybctx->print_options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
+ /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
+ wd_mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
+ }
+ }
+
+ /* count metadata */
+ if (wd_mod) {
+ ++count;
+ }
+ for (iter = node->meta; iter; iter = iter->next) {
+ if (count == UINT8_MAX) {
+ LOGERR(lybctx->lybctx->ctx, LY_EINT, "Maximum supported number of data node metadata is %u.", UINT8_MAX);
+ return LY_EINT;
+ }
+ ++count;
+ }
+
+ /* write number of metadata on 1 byte */
+ LY_CHECK_RET(lyb_write(out, &count, 1, lybctx->lybctx));
+
+ if (wd_mod) {
+ /* write the "default" metadata */
+ LY_CHECK_RET(lyb_print_model(out, wd_mod, 0, lybctx->lybctx));
+ LY_CHECK_RET(lyb_write_string("default", 0, sizeof(uint16_t), out, lybctx->lybctx));
+ LY_CHECK_RET(lyb_write_string("true", 0, sizeof(uint16_t), out, lybctx->lybctx));
+ }
+
+ /* write all the node metadata */
+ LY_LIST_FOR(node->meta, iter) {
+ /* model */
+ LY_CHECK_RET(lyb_print_model(out, iter->annotation->module, 0, lybctx->lybctx));
+
+ /* annotation name with length */
+ LY_CHECK_RET(lyb_write_string(iter->name, 0, sizeof(uint16_t), out, lybctx->lybctx));
+
+ /* metadata value */
+ LY_CHECK_RET(lyb_write_string(lyd_get_meta_value(iter), 0, sizeof(uint64_t), out, lybctx->lybctx));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print opaque node attributes.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Opaque node whose attributes to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struct lylyb_ctx *lybctx)
+{
+ uint8_t count = 0;
+ struct lyd_attr *iter;
+
+ for (iter = node->attr; iter; iter = iter->next) {
+ if (count == UINT8_MAX) {
+ LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of data node attributes is %u.", UINT8_MAX);
+ return LY_EINT;
+ }
+ ++count;
+ }
+
+ /* write number of attributes on 1 byte */
+ LY_CHECK_RET(lyb_write(out, &count, 1, lybctx));
+
+ /* write all the attributes */
+ LY_LIST_FOR(node->attr, iter) {
+ /* prefix */
+ LY_CHECK_RET(lyb_write_string(iter->name.prefix, 0, sizeof(uint16_t), out, lybctx));
+
+ /* namespace */
+ LY_CHECK_RET(lyb_write_string(iter->name.module_name, 0, sizeof(uint16_t), out, lybctx));
+
+ /* name */
+ LY_CHECK_RET(lyb_write_string(iter->name.name, 0, sizeof(uint16_t), out, lybctx));
+
+ /* format */
+ LY_CHECK_RET(lyb_write_number(iter->format, 1, out, lybctx));
+
+ /* value prefixes */
+ LY_CHECK_RET(lyb_print_prefix_data(out, iter->format, iter->val_prefix_data, lybctx));
+
+ /* value */
+ LY_CHECK_RET(lyb_write_string(iter->value, 0, sizeof(uint64_t), out, lybctx));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print schema node hash.
+ *
+ * @param[in] out Out structure.
+ * @param[in] schema Schema node whose hash to print.
+ * @param[in,out] sibling_ht Cached hash table for these siblings, created if NULL.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lylyb_ctx *lybctx)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t i;
+ LYB_HASH hash;
+ struct lyd_lyb_sib_ht *sib_ht;
+ struct lysc_node *first_sibling;
+
+ if (!schema) {
+ /* opaque node, write empty hash */
+ hash = 0;
+ LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx));
+ return LY_SUCCESS;
+ }
+
+ /* create whole sibling HT if not already created and saved */
+ if (!*sibling_ht) {
+ /* get first schema data sibling */
+ first_sibling = (struct lysc_node *)lys_getnext(NULL, lysc_data_parent(schema), schema->module->compiled,
+ (schema->flags & LYS_IS_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0);
+ LY_ARRAY_FOR(lybctx->sib_hts, u) {
+ if (lybctx->sib_hts[u].first_sibling == first_sibling) {
+ /* we have already created a hash table for these siblings */
+ *sibling_ht = lybctx->sib_hts[u].ht;
+ break;
+ }
+ }
+
+ if (!*sibling_ht) {
+ /* we must create sibling hash table */
+ LY_CHECK_RET(lyb_hash_siblings(first_sibling, sibling_ht));
+
+ /* and save it */
+ LY_ARRAY_NEW_RET(lybctx->ctx, lybctx->sib_hts, sib_ht, LY_EMEM);
+
+ sib_ht->first_sibling = first_sibling;
+ sib_ht->ht = *sibling_ht;
+ }
+ }
+
+ /* get our hash */
+ LY_CHECK_RET(lyb_hash_find(*sibling_ht, schema, &hash));
+
+ /* write the hash */
+ LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx));
+
+ if (hash & LYB_HASH_COLLISION_ID) {
+ /* no collision for this hash, we are done */
+ return LY_SUCCESS;
+ }
+
+ /* written hash was a collision, write also all the preceding hashes */
+ for (i = 0; !(hash & (LYB_HASH_COLLISION_ID >> i)); ++i) {}
+
+ for ( ; i; --i) {
+ hash = lyb_get_hash(schema, i - 1);
+ if (!hash) {
+ return LY_EINT;
+ }
+ assert(hash & (LYB_HASH_COLLISION_ID >> (i - 1)));
+
+ LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print header for non-opaq node.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_header(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ /* write any metadata */
+ LY_CHECK_RET(lyb_print_metadata(out, node, lybctx));
+
+ /* write node flags */
+ LY_CHECK_RET(lyb_write_number(node->flags, sizeof node->flags, out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print LYB node type.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_lyb_type(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ enum lylyb_node_type lyb_type;
+
+ if (node->flags & LYD_EXT) {
+ assert(node->schema);
+ lyb_type = LYB_NODE_EXT;
+ } else if (!node->schema) {
+ lyb_type = LYB_NODE_OPAQ;
+ } else if (!lysc_data_parent(node->schema)) {
+ lyb_type = LYB_NODE_TOP;
+ } else {
+ lyb_type = LYB_NODE_CHILD;
+ }
+
+ LY_CHECK_RET(lyb_write_number(lyb_type, 1, out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print inner node.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_inner(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ /* write necessary basic data */
+ LY_CHECK_RET(lyb_print_node_header(out, node, lybctx));
+
+ /* recursively write all the descendants */
+ LY_CHECK_RET(lyb_print_siblings(out, lyd_child(node), lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print opaque node and its descendants.
+ *
+ * @param[in] out Out structure.
+ * @param[in] opaq Node to print.
+ * @param[in] lyd_lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_opaq(struct ly_out *out, const struct lyd_node_opaq *opaq, struct lyd_lyb_ctx *lyd_lybctx)
+{
+ struct lylyb_ctx *lybctx = lyd_lybctx->lybctx;
+
+ /* write attributes */
+ LY_CHECK_RET(lyb_print_attributes(out, opaq, lybctx));
+
+ /* write node flags */
+ LY_CHECK_RET(lyb_write_number(opaq->flags, sizeof opaq->flags, out, lybctx));
+
+ /* prefix */
+ LY_CHECK_RET(lyb_write_string(opaq->name.prefix, 0, sizeof(uint16_t), out, lybctx));
+
+ /* module reference */
+ LY_CHECK_RET(lyb_write_string(opaq->name.module_name, 0, sizeof(uint16_t), out, lybctx));
+
+ /* name */
+ LY_CHECK_RET(lyb_write_string(opaq->name.name, 0, sizeof(uint16_t), out, lybctx));
+
+ /* value */
+ LY_CHECK_RET(lyb_write_string(opaq->value, 0, sizeof(uint64_t), out, lybctx));
+
+ /* format */
+ LY_CHECK_RET(lyb_write_number(opaq->format, 1, out, lybctx));
+
+ /* value prefixes */
+ LY_CHECK_RET(lyb_print_prefix_data(out, opaq->format, opaq->val_prefix_data, lybctx));
+
+ /* recursively write all the descendants */
+ LY_CHECK_RET(lyb_print_siblings(out, opaq->child, lyd_lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print anydata or anyxml node.
+ *
+ * @param[in] anydata Node to print.
+ * @param[in] out Out structure.
+ * @param[in] lyd_lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_any(struct ly_out *out, struct lyd_node_any *anydata, struct lyd_lyb_ctx *lyd_lybctx)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LYD_ANYDATA_VALUETYPE value_type;
+ int len;
+ char *buf = NULL;
+ const char *str;
+ struct ly_out *out2 = NULL;
+ struct lylyb_ctx *lybctx = lyd_lybctx->lybctx;
+
+ if ((anydata->schema->nodetype == LYS_ANYDATA) && (anydata->value_type != LYD_ANYDATA_DATATREE)) {
+ LOGINT_RET(lybctx->ctx);
+ }
+
+ if (anydata->value_type == LYD_ANYDATA_DATATREE) {
+ /* will be printed as a nested LYB data tree because the used modules need to be written */
+ value_type = LYD_ANYDATA_LYB;
+ } else {
+ value_type = anydata->value_type;
+ }
+
+ /* write necessary basic data */
+ LY_CHECK_RET(lyb_print_node_header(out, (struct lyd_node *)anydata, lyd_lybctx));
+
+ /* first byte is type */
+ LY_CHECK_GOTO(ret = lyb_write_number(value_type, sizeof value_type, out, lybctx), cleanup);
+
+ if (anydata->value_type == LYD_ANYDATA_DATATREE) {
+ /* print LYB data tree to memory */
+ LY_CHECK_GOTO(ret = ly_out_new_memory(&buf, 0, &out2), cleanup);
+ LY_CHECK_GOTO(ret = lyb_print_data(out2, anydata->value.tree, LYD_PRINT_WITHSIBLINGS), cleanup);
+
+ len = lyd_lyb_data_length(buf);
+ assert(len != -1);
+ str = buf;
+ } else if (anydata->value_type == LYD_ANYDATA_LYB) {
+ len = lyd_lyb_data_length(anydata->value.mem);
+ assert(len != -1);
+ str = anydata->value.mem;
+ } else {
+ len = strlen(anydata->value.str);
+ str = anydata->value.str;
+ }
+
+ /* followed by the content */
+ LY_CHECK_GOTO(ret = lyb_write_string(str, (size_t)len, sizeof(uint64_t), out, lybctx), cleanup);
+
+cleanup:
+ ly_out_free(out2, NULL, 1);
+ return ret;
+}
+
+/**
+ * @brief Print leaf node.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_leaf(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ /* write necessary basic data */
+ LY_CHECK_RET(lyb_print_node_header(out, node, lybctx));
+
+ /* write term value */
+ LY_CHECK_RET(lyb_print_term_value((struct lyd_node_term *)node, out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print all leaflist nodes which belong to same schema.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @param[out] printed_node Last node that was printed by this function.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_leaflist(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx,
+ const struct lyd_node **printed_node)
+{
+ const struct lysc_node *schema;
+
+ /* register a new sibling */
+ LY_CHECK_RET(lyb_write_start_siblings(out, lybctx->lybctx));
+
+ schema = node->schema;
+
+ /* write all the siblings */
+ LY_LIST_FOR(node, node) {
+ if (schema != node->schema) {
+ /* all leaflist nodes was printed */
+ break;
+ }
+
+ /* write leaf data */
+ LY_CHECK_RET(lyb_print_node_leaf(out, node, lybctx));
+ *printed_node = node;
+ }
+
+ /* finish this sibling */
+ LY_CHECK_RET(lyb_write_stop_siblings(out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print all list nodes which belong to same schema.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @param[out] printed_node Last node that was printed by this function.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node_list(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx,
+ const struct lyd_node **printed_node)
+{
+ const struct lysc_node *schema;
+
+ /* register a new sibling */
+ LY_CHECK_RET(lyb_write_start_siblings(out, lybctx->lybctx));
+
+ schema = node->schema;
+
+ LY_LIST_FOR(node, node) {
+ if (schema != node->schema) {
+ /* all list nodes was printed */
+ break;
+ }
+
+ /* write necessary basic data */
+ LY_CHECK_RET(lyb_print_node_header(out, node, lybctx));
+
+ /* recursively write all the descendants */
+ LY_CHECK_RET(lyb_print_siblings(out, lyd_child(node), lybctx));
+
+ *printed_node = node;
+ }
+
+ /* finish this sibling */
+ LY_CHECK_RET(lyb_write_stop_siblings(out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print node.
+ *
+ * @param[in] out Out structure.
+ * @param[in,out] printed_node Current data node to print. Sets to the last printed node.
+ * @param[in,out] sibling_ht Cached hash table for these siblings, created if NULL.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct hash_table **sibling_ht,
+ struct lyd_lyb_ctx *lybctx)
+{
+ const struct lyd_node *node = *printed_node;
+
+ /* write node type */
+ LY_CHECK_RET(lyb_print_lyb_type(out, node, lybctx));
+
+ /* write model info first */
+ if (node->schema && ((node->flags & LYD_EXT) || !lysc_data_parent(node->schema))) {
+ LY_CHECK_RET(lyb_print_model(out, node->schema->module, 0, lybctx->lybctx));
+ }
+
+ if (node->flags & LYD_EXT) {
+ /* write schema node name */
+ LY_CHECK_RET(lyb_write_string(node->schema->name, 0, sizeof(uint16_t), out, lybctx->lybctx));
+ } else {
+ /* write schema hash */
+ LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx->lybctx));
+ }
+
+ if (!node->schema) {
+ LY_CHECK_RET(lyb_print_node_opaq(out, (struct lyd_node_opaq *)node, lybctx));
+ } else if (node->schema->nodetype & LYS_LEAFLIST) {
+ LY_CHECK_RET(lyb_print_node_leaflist(out, node, lybctx, &node));
+ } else if (node->schema->nodetype == LYS_LIST) {
+ LY_CHECK_RET(lyb_print_node_list(out, node, lybctx, &node));
+ } else if (node->schema->nodetype & LYD_NODE_ANY) {
+ LY_CHECK_RET(lyb_print_node_any(out, (struct lyd_node_any *)node, lybctx));
+ } else if (node->schema->nodetype & LYD_NODE_INNER) {
+ LY_CHECK_RET(lyb_print_node_inner(out, node, lybctx));
+ } else {
+ LY_CHECK_RET(lyb_print_node_leaf(out, node, lybctx));
+ }
+
+ *printed_node = node;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print siblings.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_siblings(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ struct hash_table *sibling_ht = NULL;
+ const struct lys_module *prev_mod = NULL;
+ ly_bool top_level;
+
+ top_level = !LY_ARRAY_COUNT(lybctx->lybctx->siblings);
+
+ LY_CHECK_RET(lyb_write_start_siblings(out, lybctx->lybctx));
+
+ if (top_level) {
+ /* write all the siblings */
+ LY_LIST_FOR(node, node) {
+ /* do not reuse sibling hash tables from different modules */
+ if (!node->schema || (node->schema->module != prev_mod)) {
+ sibling_ht = NULL;
+ prev_mod = node->schema ? node->schema->module : NULL;
+ }
+
+ LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
+
+ if (!(lybctx->print_options & LYD_PRINT_WITHSIBLINGS)) {
+ break;
+ }
+ }
+ } else {
+ LY_LIST_FOR(node, node) {
+ LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
+ }
+ }
+
+ LY_CHECK_RET(lyb_write_stop_siblings(out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint8_t zero = 0;
+ struct lyd_lyb_ctx *lybctx;
+ const struct ly_ctx *ctx = root ? LYD_CTX(root) : NULL;
+
+ lybctx = calloc(1, sizeof *lybctx);
+ LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+ lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
+ LY_CHECK_ERR_RET(!lybctx->lybctx, LOGMEM(ctx), LY_EMEM);
+
+ lybctx->print_options = options;
+ if (root) {
+ lybctx->lybctx->ctx = ctx;
+
+ if (root->schema && lysc_data_parent(root->schema)) {
+ LOGERR(lybctx->lybctx->ctx, LY_EINVAL, "LYB printer supports only printing top-level nodes.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
+
+ /* LYB magic number */
+ LY_CHECK_GOTO(ret = lyb_print_magic_number(out), cleanup);
+
+ /* LYB header */
+ LY_CHECK_GOTO(ret = lyb_print_header(out), cleanup);
+
+ /* all used models */
+ LY_CHECK_GOTO(ret = lyb_print_data_models(out, root, lybctx->lybctx), cleanup);
+
+ ret = lyb_print_siblings(out, root, lybctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* ending zero byte */
+ LY_CHECK_GOTO(ret = lyb_write(out, &zero, sizeof zero, lybctx->lybctx), cleanup);
+
+cleanup:
+ lyd_lyb_ctx_free((struct lyd_ctx *)lybctx);
+ return ret;
+}
diff --git a/src/printer_schema.c b/src/printer_schema.c
new file mode 100644
index 0000000..075c519
--- /dev/null
+++ b/src/printer_schema.c
@@ -0,0 +1,222 @@
+/**
+ * @file printer_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic schema printers functions.
+ *
+ * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "printer_schema.h"
+
+#include <stdio.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+#include "out_internal.h"
+#include "printer_internal.h"
+#include "tree_schema.h"
+
+LIBYANG_API_DEF LY_ERR
+lys_print_module(struct ly_out *out, const struct lys_module *module, LYS_OUTFORMAT format, size_t line_length,
+ uint32_t options)
+{
+ LY_ERR ret;
+
+ LY_CHECK_ARG_RET(NULL, out, module, LY_EINVAL);
+
+ /* reset number of printed bytes */
+ out->func_printed = 0;
+
+ switch (format) {
+ case LYS_OUT_YANG:
+ if (!module->parsed) {
+ LOGERR(module->ctx, LY_EINVAL, "Module \"%s\" parsed module missing.", module->name);
+ ret = LY_EINVAL;
+ break;
+ }
+
+ ret = yang_print_parsed_module(out, module->parsed, options);
+ break;
+ case LYS_OUT_YANG_COMPILED:
+ if (!module->compiled) {
+ LOGERR(module->ctx, LY_EINVAL, "Module \"%s\" compiled module missing.", module->name);
+ ret = LY_EINVAL;
+ break;
+ }
+
+ ret = yang_print_compiled(out, module, options);
+ break;
+ case LYS_OUT_YIN:
+ if (!module->parsed) {
+ LOGERR(module->ctx, LY_EINVAL, "Module \"%s\" parsed module missing.", module->name);
+ ret = LY_EINVAL;
+ break;
+ }
+
+ ret = yin_print_parsed_module(out, module->parsed, options);
+ break;
+ case LYS_OUT_TREE:
+ if (!module->parsed) {
+ LOGERR(module->ctx, LY_EINVAL, "Module \"%s\" parsed module missing.", module->name);
+ ret = LY_EINVAL;
+ break;
+ }
+ ret = tree_print_module(out, module, options, line_length);
+ break;
+ /* TODO not yet implemented
+ case LYS_OUT_INFO:
+ ret = info_print_model(out, module, target_node);
+ break;
+ */
+ default:
+ LOGERR(module->ctx, LY_EINVAL, "Unsupported output format.");
+ ret = LY_EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_submodule(struct ly_out *out, const struct lysp_submodule *submodule, LYS_OUTFORMAT format,
+ size_t line_length, uint32_t options)
+{
+ LY_ERR ret;
+
+ LY_CHECK_ARG_RET(NULL, out, submodule, LY_EINVAL);
+
+ /* reset number of printed bytes */
+ out->func_printed = 0;
+
+ switch (format) {
+ case LYS_OUT_YANG:
+ ret = yang_print_parsed_submodule(out, submodule, options);
+ break;
+ case LYS_OUT_YIN:
+ ret = yin_print_parsed_submodule(out, submodule, options);
+ break;
+ case LYS_OUT_TREE:
+ ret = tree_print_parsed_submodule(out, submodule, options, line_length);
+ break;
+ /* TODO not yet implemented
+ case LYS_OUT_INFO:
+ ret = info_print_model(out, module, target_node);
+ break;
+ */
+ default:
+ LOGERR(submodule->mod->ctx, LY_EINVAL, "Unsupported output format.");
+ ret = LY_EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+lys_print_(struct ly_out *out, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options)
+{
+ LY_ERR ret;
+
+ LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
+
+ ret = lys_print_module(out, module, format, 0, options);
+
+ ly_out_free(out, NULL, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options)
+{
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, strp, module, LY_EINVAL);
+
+ /* init */
+ *strp = NULL;
+
+ LY_CHECK_RET(ly_out_new_memory(strp, 0, &out));
+ return lys_print_(out, module, format, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options)
+{
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, fd != -1, module, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_fd(fd, &out));
+ return lys_print_(out, module, format, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options)
+{
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, f, module, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_file(f, &out));
+ return lys_print_(out, module, format, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options)
+{
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, path, module, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_filepath(path, &out));
+ return lys_print_(out, module, format, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_clb(ly_write_clb writeclb, void *user_data, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options)
+{
+ struct ly_out *out;
+
+ LY_CHECK_ARG_RET(NULL, writeclb, module, LY_EINVAL);
+
+ LY_CHECK_RET(ly_out_new_clb(writeclb, user_data, &out));
+ return lys_print_(out, module, format, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_print_node(struct ly_out *out, const struct lysc_node *node, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR ret;
+
+ LY_CHECK_ARG_RET(NULL, out, node, LY_EINVAL);
+
+ /* reset number of printed bytes */
+ out->func_printed = 0;
+
+ switch (format) {
+ case LYS_OUT_YANG_COMPILED:
+ ret = yang_print_compiled_node(out, node, options);
+ break;
+ /* TODO not yet implemented
+ case LYS_OUT_YIN:
+ ret = yin_print_parsed(out, module);
+ break;
+ */
+ case LYS_OUT_TREE:
+ ret = tree_print_compiled_node(out, node, options, line_length);
+ break;
+ default:
+ LOGERR(NULL, LY_EINVAL, "Unsupported output format.");
+ ret = LY_EINVAL;
+ break;
+ }
+
+ return ret;
+}
diff --git a/src/printer_schema.h b/src/printer_schema.h
new file mode 100644
index 0000000..471d38e
--- /dev/null
+++ b/src/printer_schema.h
@@ -0,0 +1,232 @@
+/**
+ * @file printer_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Schema printers for libyang
+ *
+ * Copyright (c) 2015-2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PRINTER_SCHEMA_H_
+#define LY_PRINTER_SCHEMA_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "log.h"
+#include "out.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_out;
+struct lys_module;
+struct lysc_node;
+struct lysp_submodule;
+
+/**
+ * @page howtoSchemaPrinters Module Printers
+ *
+ * Schema printers allows to serialize internal representations of a schema module in a specific format. libyang
+ * supports the following schema formats for printing:
+ *
+ * - YANG
+ *
+ * Basic YANG schemas format described in [RFC 6020](http://tools.ietf.org/html/rfc6020) and
+ * [RFC 7951](http://tools.ietf.org/html/rfc7951) (so both YANG 1.0 and YANG 1.1 versions are supported).
+ *
+ * - YANG compiled
+ *
+ * Syntactically, this format is based on standard YANG format. In contrast to standard YANG format, YANG compiled format
+ * represents the module how it is used by libyang - with all uses expanded, augments and deviations applied, etc.
+ * (more details about the compiled modules can be found on @ref howtoContext page).
+ *
+ * - YIN
+ *
+ * Alternative XML-based format to YANG - YANG Independent Notation. The details can be found in
+ * [RFC 6020](http://tools.ietf.org/html/rfc6020#section-11) and
+ * [RFC 7951](http://tools.ietf.org/html/rfc7951#section-13).
+ *
+ * - Tree Diagram
+ *
+ * Simple tree diagram providing overview of the module. The details can be found in
+ * [RFC 8340](https://tools.ietf.org/html/rfc8340).
+ *
+ * For simpler transition from libyang 1.x (and for some simple use cases), there are functions (::lys_print_clb(),
+ * ::lys_print_fd(), ::lys_print_file() and ::lys_print_mem()) to print the complete module into the specified output. But note,
+ * that these functions are limited to print only the complete module.
+ *
+ * The full functionality of the schema printers is available via functions using [output handler](@ref howtoOutput). Besides
+ * the ::lys_print_module() function to print the complete module, there are functions to print a submodule
+ * (::lys_print_submodule()) or a subtree (::lys_print_node()). Note that these functions might not support all the output
+ * formats mentioned above.
+ *
+ * Functions List
+ * --------------
+ * - ::lys_print_module()
+ * - ::lys_print_submodule()
+ * - ::lys_print_node()
+ *
+ * - ::lys_print_clb()
+ * - ::lys_print_fd()
+ * - ::lys_print_file()
+ * - ::lys_print_mem()
+ * - ::lys_print_path()
+ */
+
+/**
+ * @addtogroup schematree
+ * @{
+ */
+
+/**
+ * @defgroup schemaprinterflags Schema output options
+ *
+ * Options to change default behavior of the schema printers.
+ *
+ * @{
+ */
+#define LYS_PRINT_SHRINK LY_PRINT_SHRINK /**< Flag for output without indentation and formatting new lines. */
+#define LYS_PRINT_NO_SUBSTMT 0x10 /**< Print only top-level/referede node information,
+ do not print information from the substatements */
+
+/** @} schemaprinterflags */
+
+/**
+ * @brief Schema output formats accepted by libyang [printer functions](@ref howtoSchemaPrinters).
+ */
+typedef enum {
+ LYS_OUT_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
+ LYS_OUT_YANG = 1, /**< YANG schema output format */
+ LYS_OUT_YANG_COMPILED = 2, /**< YANG schema output format of the compiled schema tree */
+ LYS_OUT_YIN = 3, /**< YIN schema output format */
+ LYS_OUT_TREE /**< Tree schema output format */
+} LYS_OUTFORMAT;
+
+/**
+ * @brief Schema module printer.
+ *
+ * @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
+ * @param[in] module Main module with the parsed schema to print.
+ * @param[in] format Output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_module(struct ly_out *out, const struct lys_module *module, LYS_OUTFORMAT format,
+ size_t line_length, uint32_t options);
+
+/**
+ * @brief Schema submodule printer.
+ *
+ * @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
+ * @param[in] submodule Parsed submodule to print.
+ * @param[in] format Output format (LYS_OUT_YANG_COMPILED is not supported).
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_submodule(struct ly_out *out, const struct lysp_submodule *submodule, LYS_OUTFORMAT format,
+ size_t line_length, uint32_t options);
+
+/**
+ * @brief Print schema tree in the specified format into a memory block.
+ * It is up to caller to free the returned string by free().
+ *
+ * This is just a wrapper around ::lys_print_module() for simple use cases.
+ * In case of a complex use cases, use lys_print with ly_out output handler.
+ *
+ * @param[out] strp Pointer to store the resulting dump.
+ * @param[in] module Schema tree to print.
+ * @param[in] format Schema output format.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options);
+
+/**
+ * @brief Print schema tree in the specified format into a file descriptor.
+ *
+ * This is just a wrapper around ::lys_print_module() for simple use cases.
+ * In case of a complex use cases, use lys_print with ly_out output handler.
+ *
+ * @param[in] fd File descriptor where to print the data.
+ * @param[in] module Schema tree to print.
+ * @param[in] format Schema output format.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options);
+
+/**
+ * @brief Print schema tree in the specified format into a file stream.
+ *
+ * This is just a wrapper around ::lys_print_module() for simple use cases.
+ * In case of a complex use cases, use lys_print with ly_out output handler.
+ *
+ * @param[in] module Schema tree to print.
+ * @param[in] f File stream where to print the schema.
+ * @param[in] format Schema output format.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, uint32_t options);
+
+/**
+ * @brief Print schema tree in the specified format into a file.
+ *
+ * This is just a wrapper around ::lys_print_module() for simple use cases.
+ * In case of a complex use cases, use lys_print with ly_out output handler.
+ *
+ * @param[in] path File where to print the schema.
+ * @param[in] module Schema tree to print.
+ * @param[in] format Schema output format.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format,
+ uint32_t options);
+
+/**
+ * @brief Print schema tree in the specified format using a provided callback.
+ *
+ * This is just a wrapper around ::lys_print_module() for simple use cases.
+ * In case of a complex use cases, use lys_print with ly_out output handler.
+ *
+ * @param[in] module Schema tree to print.
+ * @param[in] writeclb Callback function to write the data (see write(1)).
+ * @param[in] user_data Optional caller-specific argument to be passed to the \p writeclb callback.
+ * @param[in] format Schema output format.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_clb(ly_write_clb writeclb, void *user_data, const struct lys_module *module,
+ LYS_OUTFORMAT format, uint32_t options);
+
+/**
+ * @brief Schema node printer.
+ *
+ * @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
+ * @param[in] node Schema node to print.
+ * @param[in] format Output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lys_print_node(struct ly_out *out, const struct lysc_node *node, LYS_OUTFORMAT format,
+ size_t line_length, uint32_t options);
+
+/** @} schematree */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PRINTER_SCHEMA_H_ */
diff --git a/src/printer_tree.c b/src/printer_tree.c
new file mode 100644
index 0000000..6a7e7ce
--- /dev/null
+++ b/src/printer_tree.c
@@ -0,0 +1,4673 @@
+/**
+ * @file printer_tree.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief RFC tree printer for libyang data structure
+ *
+ * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * @section TRP_DESIGN Design
+ *
+ * @code
+ * +---------+ +---------+ +---------+
+ * output | trp | | trb | | tro |
+ * <---+ Print +<---+ Browse +<-->+ Obtain |
+ * | | | | | |
+ * +---------+ +----+----+ +---------+
+ * ^
+ * |
+ * +----+----+
+ * | trm |
+ * | Manager |
+ * | |
+ * +----+----+
+ * ^
+ * | input
+ * +
+ * @endcode
+ *
+ * @subsection TRP_GLOSSARY Glossary
+ *
+ * @subsubsection TRP_trm trm
+ * Manager functions are at the peak of abstraction. They are
+ * able to print individual sections of the YANG tree diagram
+ * (eg module, notifications, rpcs ...) and they call
+ * Browse functions (@ref TRP_trb).
+ *
+ * @subsubsection TRP_trb trb
+ * Browse functions contain a general algorithm (Preorder DFS)
+ * for traversing the tree. It does not matter what data type
+ * the tree contains (@ref lysc_node or @ref lysp_node), because it
+ * requires a ready-made getter functions for traversing the tree
+ * (@ref trt_fp_all) and transformation function to its own node
+ * data type (@ref trt_node). These getter functions are generally
+ * referred to as @ref TRP_tro. Browse functions can repeatedly
+ * traverse nodes in the tree, for example, to calculate the alignment
+ * gap before the nodes \<type\> in the YANG Tree Diagram.
+ * The obtained @ref trt_node is passed to the @ref TRP_trp functions
+ * to print the Tree diagram.
+ *
+ * @subsubsection TRP_tro tro
+ * Functions that provide an extra wrapper for the libyang library.
+ * The Obtain functions are further specialized according to whether
+ * they operate on lysp_tree (@ref TRP_trop) or lysc_tree
+ * (@ref TRP_troc). If they are general algorithms, then they have the
+ * prefix \b tro_. The Obtain functions provide information to
+ * @ref TRP_trb functions for printing the Tree diagram.
+ *
+ * @subsubsection TRP_trop trop
+ * Functions for Obtaining information from Parsed schema tree.
+ *
+ * @subsubsection TRP_troc troc
+ * Functions for Obtaining information from Compiled schema tree.
+ *
+ * @subsubsection TRP_trp trp
+ * Print functions take care of the printing YANG diagram. They can
+ * also split one node into multiple lines if the node does not fit
+ * on one line.
+ *
+ * @subsubsection TRP_trt trt
+ * Data type marking in the printer_tree module.
+ *
+ * @subsubsection TRP_trg trg
+ * General functions.
+ *
+ * @subsection TRP_ADJUSTMENTS Adjustments
+ * It is assumed that the changes are likely to take place mainly for
+ * @ref TRP_tro, @ref TRP_trop or @ref TRP_troc functions because
+ * they are the only ones dependent on libyang implementation.
+ * In special cases, changes will also need to be made to the
+ * @ref TRP_trp functions if a special algorithm is needed to print
+ * (right now this is prepared for printing list's keys
+ * and if-features).
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "out_internal.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+#include "printer_internal.h"
+#include "printer_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+/**
+ * @brief List of available actions.
+ */
+typedef enum {
+ TRD_PRINT = 0, /**< Normal behavior. It just prints. */
+ TRD_CHAR_COUNT /**< Characters will be counted instead of printing. */
+} trt_ly_out_clb_arg_flag;
+
+/**
+ * @brief Structure is passed as 'writeclb' argument
+ * to the ::ly_out_new_clb().
+ */
+struct ly_out_clb_arg {
+ trt_ly_out_clb_arg_flag mode; /**< flag specifying which action to take. */
+ struct ly_out *out; /**< The ly_out pointer delivered to the printer tree module via the main interface. */
+ size_t counter; /**< Counter of printed characters. */
+ LY_ERR last_error; /**< The last error that occurred. If no error has occurred, it will be ::LY_SUCCESS. */
+};
+
+/**
+ * @brief Initialize struct ly_out_clb_arg with default settings.
+ */
+#define TRP_INIT_LY_OUT_CLB_ARG(MODE, OUT, COUNTER, LAST_ERROR) \
+ (struct ly_out_clb_arg) { \
+ .mode = MODE, .out = OUT, \
+ .counter = COUNTER, .last_error = LAST_ERROR \
+ }
+
+/**********************************************************************
+ * Print getters
+ *********************************************************************/
+
+/**
+ * @brief Callback functions that prints special cases.
+ *
+ * It just groups together tree context with trt_fp_print.
+ */
+struct trt_cf_print {
+ const struct trt_tree_ctx *ctx; /**< Context of libyang tree. */
+
+ void (*pf)(const struct trt_tree_ctx *, struct ly_out *); /**< Pointing to function which printing list's keys or features. */
+};
+
+/**
+ * @brief Callback functions for printing special cases.
+ *
+ * Functions with the suffix 'trp' can print most of the text on
+ * output, just by setting the pointer to the string. But in some
+ * cases, it's not that simple, because its entire string is fragmented
+ * in memory. For example, for printing list's keys or if-features.
+ * However, this depends on how the libyang library is implemented.
+ * This implementation of the printer_tree module goes through
+ * a lysp tree, but if it goes through a lysc tree, these special cases
+ * would be different.
+ * Functions must print including spaces or delimiters between names.
+ */
+struct trt_fp_print {
+ void (*print_features_names)(const struct trt_tree_ctx *, struct ly_out *); /**< Print list of features without {}? wrapper. */
+ void (*print_keys)(const struct trt_tree_ctx *, struct ly_out *); /**< Print list's keys without [] wrapper. */
+};
+
+/**
+ * @brief Package which only groups getter function.
+ */
+struct trt_pck_print {
+ const struct trt_tree_ctx *tree_ctx; /**< Context of libyang tree. */
+ struct trt_fp_print fps; /**< Print function. */
+};
+
+/**
+ * @brief Initialize struct trt_pck_print by parameters.
+ */
+#define TRP_INIT_PCK_PRINT(TREE_CTX, FP_PRINT) \
+ (struct trt_pck_print) {.tree_ctx = TREE_CTX, .fps = FP_PRINT}
+
+/**********************************************************************
+ * Indent
+ *********************************************************************/
+
+/**
+ * @brief Constants which are defined in the RFC or are observable
+ * from the pyang tool.
+ */
+typedef enum {
+ TRD_INDENT_EMPTY = 0, /**< If the node is a case node, there is no space before the \<name\>. */
+ TRD_INDENT_LONG_LINE_BREAK = 2, /**< The new line should be indented so that it starts below \<name\> with
+ a whitespace offset of at least two characters. */
+ TRD_INDENT_LINE_BEGIN = 2, /**< Indent below the keyword (module, augment ...). */
+ TRD_INDENT_BTW_SIBLINGS = 2, /**< Indent between | and | characters. */
+ TRD_INDENT_BEFORE_KEYS = 1, /**< "..."___\<keys\>. */
+ TRD_INDENT_BEFORE_TYPE = 4, /**< "..."___\<type\>, but if mark is set then indent == 3. */
+ TRD_INDENT_BEFORE_IFFEATURES = 1 /**< "..."___\<iffeatures\>. */
+} trt_cnf_indent;
+
+/**
+ * @brief Type of indent in node.
+ */
+typedef enum {
+ TRD_INDENT_IN_NODE_NORMAL = 0, /**< Node fits on one line. */
+ TRD_INDENT_IN_NODE_DIVIDED, /**< The node must be split into multiple rows. */
+ TRD_INDENT_IN_NODE_FAILED /**< Cannot be crammed into one line. The condition for the maximum line length is violated. */
+} trt_indent_in_node_type;
+
+/** Constant to indicate the need to break a line. */
+#define TRD_LINEBREAK -1
+
+/**
+ * @brief Records the alignment between the individual
+ * elements of the node.
+ *
+ * @see trp_default_indent_in_node, trp_try_normal_indent_in_node
+ */
+struct trt_indent_in_node {
+ trt_indent_in_node_type type; /**< Type of indent in node. */
+ int16_t btw_name_opts; /**< Indent between node name and \<opts\>. */
+ int16_t btw_opts_type; /**< Indent between \<opts\> and \<type\>. */
+ int16_t btw_type_iffeatures; /**< Indent between type and features. Ignored if \<type\> missing. */
+};
+
+/**
+ * @brief Type of wrappers to be printed.
+ */
+typedef enum {
+ TRD_WRAPPER_TOP = 0, /**< Related to the module. */
+ TRD_WRAPPER_BODY /**< Related to e.g. Augmentations or Groupings */
+} trd_wrapper_type;
+
+/**
+ * @brief For resolving sibling symbol ('|') placement.
+ *
+ * Bit indicates where the sibling symbol must be printed.
+ * This place is in multiples of ::TRD_INDENT_BTW_SIBLINGS.
+ *
+ * @see TRP_INIT_WRAPPER_TOP, TRP_INIT_WRAPPER_BODY,
+ * trp_wrapper_set_mark, trp_wrapper_set_shift,
+ * trp_wrapper_if_last_sibling, trp_wrapper_eq, trp_print_wrapper
+ */
+struct trt_wrapper {
+ trd_wrapper_type type; /**< Location of the wrapper. */
+ uint64_t bit_marks1; /**< The set bits indicate where the '|' character is to be printed.
+ It follows that the maximum immersion of the printable node is 64. */
+ uint32_t actual_pos; /**< Actual position in bit_marks. */
+};
+
+/**
+ * @brief Get wrapper related to the module section.
+ *
+ * @code
+ * module: <module-name>
+ * +--<node>
+ * |
+ * @endcode
+ */
+#define TRP_INIT_WRAPPER_TOP \
+ (struct trt_wrapper) { \
+ .type = TRD_WRAPPER_TOP, .actual_pos = 0, .bit_marks1 = 0 \
+ }
+
+/**
+ * @brief Get wrapper related to subsection
+ * e.g. Augmenations or Groupings.
+ *
+ * @code
+ * module: <module-name>
+ * +--<node>
+ *
+ * augment <target-node>:
+ * +--<node>
+ * @endcode
+ */
+#define TRP_INIT_WRAPPER_BODY \
+ (struct trt_wrapper) { \
+ .type = TRD_WRAPPER_BODY, .actual_pos = 0, .bit_marks1 = 0 \
+ }
+
+/**
+ * @brief Package which only groups wrapper and indent in node.
+ */
+struct trt_pck_indent {
+ struct trt_wrapper wrapper; /**< Coded " | | " sequence. */
+ struct trt_indent_in_node in_node; /**< Indent in node. */
+};
+
+/**
+ * @brief Initialize struct trt_pck_indent by parameters.
+ */
+#define TRP_INIT_PCK_INDENT(WRAPPER, INDENT_IN_NODE) \
+ (struct trt_pck_indent){ \
+ .wrapper = WRAPPER, .in_node = INDENT_IN_NODE \
+ }
+
+/**********************************************************************
+ * flags
+ *********************************************************************/
+
+#define TRD_FLAGS_TYPE_EMPTY "--"
+#define TRD_FLAGS_TYPE_RW "rw"
+#define TRD_FLAGS_TYPE_RO "ro"
+#define TRD_FLAGS_TYPE_RPC_INPUT_PARAMS "-w"
+#define TRD_FLAGS_TYPE_USES_OF_GROUPING "-u"
+#define TRD_FLAGS_TYPE_RPC "-x"
+#define TRD_FLAGS_TYPE_NOTIF "-n"
+#define TRD_FLAGS_TYPE_MOUNT_POINT "mp"
+
+/**********************************************************************
+ * node_name and opts
+ *********************************************************************/
+
+#define TRD_NODE_NAME_PREFIX_CHOICE "("
+#define TRD_NODE_NAME_PREFIX_CASE ":("
+#define TRD_NODE_NAME_TRIPLE_DOT "..."
+
+/**
+ * @brief Type of the node.
+ *
+ * Used mainly to complete the correct \<opts\> next to or
+ * around the \<name\>.
+ */
+typedef enum {
+ TRD_NODE_ELSE = 0, /**< For some node which does not require special treatment. \<name\> */
+ TRD_NODE_CASE, /**< For case node. :(\<name\>) */
+ TRD_NODE_CHOICE, /**< For choice node. (\<name\>) */
+ TRD_NODE_TRIPLE_DOT /**< For collapsed sibling nodes and their children. Special case which doesn't belong here very well. */
+} trt_node_type;
+
+#define TRD_NODE_OPTIONAL "?" /**< For an optional leaf, anydata, or anyxml. \<name\>? */
+#define TRD_NODE_CONTAINER "!" /**< For a presence container. \<name\>! */
+#define TRD_NODE_LISTLEAFLIST "*" /**< For a leaf-list or list. \<name\>* */
+
+/**
+ * @brief Type of node and his name.
+ *
+ * @see TRP_EMPTY_NODE_NAME, TRP_NODE_NAME_IS_EMPTY,
+ * trp_print_node_name, trp_mark_is_used, trp_print_opts_keys
+ */
+struct trt_node_name {
+ trt_node_type type; /**< Type of the node relevant for printing. */
+ ly_bool keys; /**< Set to 1 if [\<keys\>] are to be printed. Valid for some types only. */
+ const char *module_prefix; /**< If the node is augmented into the tree from another module,
+ so this is the prefix of that module. */
+ const char *str; /**< Name of the node. */
+ const char *add_opts; /**< Additional opts symbol from plugin. */
+ const char *opts; /**< The \<opts\> symbol. */
+};
+
+/**
+ * @brief Create struct trt_node_name as empty.
+ */
+#define TRP_EMPTY_NODE_NAME \
+ (struct trt_node_name) { \
+ .type = TRD_NODE_ELSE, .keys = 0, .module_prefix = NULL, .str = NULL, .opts = NULL, .add_opts = NULL \
+ }
+
+/**
+ * @brief Check if struct trt_node_name is empty.
+ */
+#define TRP_NODE_NAME_IS_EMPTY(NODE_NAME) \
+ !NODE_NAME.str
+
+/**********************************************************************
+ * type
+ *********************************************************************/
+
+/**
+ * @brief Type of the \<type\>
+ */
+typedef enum {
+ TRD_TYPE_NAME = 0, /**< Type is just a name that does not require special treatment. */
+ TRD_TYPE_TARGET, /**< Should have a form "-> TARGET", where TARGET is the leafref path. */
+ TRD_TYPE_LEAFREF, /**< This type is set automatically by the 'trp' algorithm.
+ So set type as ::TRD_TYPE_TARGET. */
+ TRD_TYPE_EMPTY /**< Type is not used at all. */
+} trt_type_type;
+
+/**
+ * @brief \<type\> in the \<node\>.
+ *
+ * @see TRP_EMPTY_TRT_TYPE, TRP_TRT_TYPE_IS_EMPTY, trp_print_type
+ */
+struct trt_type {
+ trt_type_type type; /**< Type of the \<type\>. */
+ const char *str; /**< Path or name of the type. */
+};
+
+/**
+ * @brief Create empty struct trt_type.
+ */
+#define TRP_EMPTY_TRT_TYPE \
+ (struct trt_type) {.type = TRD_TYPE_EMPTY, .str = NULL}
+
+/**
+ * @brief Check if struct trt_type is empty.
+ */
+#define TRP_TRT_TYPE_IS_EMPTY(TYPE_OF_TYPE) \
+ TYPE_OF_TYPE.type == TRD_TYPE_EMPTY
+
+/**
+ * @brief Initialize struct trt_type by parameters.
+ */
+#define TRP_INIT_TRT_TYPE(TYPE_OF_TYPE, STRING) \
+ (struct trt_type) {.type = TYPE_OF_TYPE, .str = STRING}
+
+/**
+ * @brief If-feature type.
+ */
+typedef enum {
+ TRD_IFF_NON_PRESENT = 0, /**< iffeatures are not present. */
+ TRD_IFF_PRESENT, /**< iffeatures are present and will be printed by
+ trt_fp_print.print_features_names callback */
+ TRD_IFF_OVERR /**< iffeatures are override by plugin */
+} trt_iffeatures_type;
+
+/**
+ * @brief \<if-features\>.
+ */
+struct trt_iffeatures {
+ trt_iffeatures_type type; /**< Type of iffeature. */
+ char *str; /**< iffeatures string ready to print. Set if TRD_IFF_OVERR is set. */
+};
+
+/**
+ * @brief Create empty iffeatures.
+ */
+#define TRP_EMPTY_TRT_IFFEATURES \
+ (struct trt_iffeatures) {.type = TRD_IFF_NON_PRESENT}
+
+/**
+ * @brief Check if iffeatures is empty.
+ *
+ * @param[in] IFF_TYPE value from trt_iffeatures.type.
+ * @return 1 if is empty.
+ */
+#define TRP_EMPTY_TRT_IFFEATURES_IS_EMPTY(IFF_TYPE) \
+ (IFF_TYPE == TRD_IFF_NON_PRESENT)
+
+/**********************************************************************
+ * node
+ *********************************************************************/
+
+/**
+ * @brief \<node\> data for printing.
+ *
+ * It contains RFC's:
+ * \<status\>--\<flags\> \<name\>\<opts\> \<type\> \<if-features\>.
+ * Item \<opts\> is moved to part struct trt_node_name.
+ * For printing [\<keys\>] and if-features is required special
+ * functions which prints them.
+ *
+ * @see TRP_EMPTY_NODE, trp_node_is_empty, trp_node_body_is_empty,
+ * trp_print_node_up_to_name, trp_print_divided_node_up_to_name,
+ * trp_print_node
+ */
+struct trt_node {
+ const char *status; /**< \<status\>. */
+ const char *flags; /**< \<flags\>. */
+ struct trt_node_name name; /**< \<node\> with \<opts\> mark or [\<keys\>]. */
+ struct trt_type type; /**< \<type\> contains the name of the type or type for leafref. */
+ struct trt_iffeatures iffeatures; /**< \<if-features\>. */
+ ly_bool last_one; /**< Information about whether the node is the last. */
+};
+
+/**
+ * @brief Create struct trt_node as empty.
+ */
+#define TRP_EMPTY_NODE \
+ (struct trt_node) { \
+ .status = NULL, \
+ .flags = NULL, \
+ .name = TRP_EMPTY_NODE_NAME, \
+ .type = TRP_EMPTY_TRT_TYPE, \
+ .iffeatures = TRP_EMPTY_TRT_IFFEATURES, \
+ .last_one = 1 \
+ }
+
+/**
+ * @brief Package which only groups indent and node.
+ */
+struct trt_pair_indent_node {
+ struct trt_indent_in_node indent;
+ struct trt_node node;
+};
+
+/**
+ * @brief Initialize struct trt_pair_indent_node by parameters.
+ */
+#define TRP_INIT_PAIR_INDENT_NODE(INDENT_IN_NODE, NODE) \
+ (struct trt_pair_indent_node) { \
+ .indent = INDENT_IN_NODE, .node = NODE \
+ }
+
+/**********************************************************************
+ * statement
+ *********************************************************************/
+
+#define TRD_KEYWORD_MODULE "module"
+#define TRD_KEYWORD_SUBMODULE "submodule"
+#define TRD_KEYWORD_AUGMENT "augment"
+#define TRD_KEYWORD_RPC "rpcs"
+#define TRD_KEYWORD_NOTIF "notifications"
+#define TRD_KEYWORD_GROUPING "grouping"
+
+/**
+ * @brief Main sign of the tree nodes.
+ *
+ * @see TRP_EMPTY_KEYWORD_STMT, TRP_KEYWORD_STMT_IS_EMPTY
+ * trt_print_keyword_stmt_begin, trt_print_keyword_stmt_str,
+ * trt_print_keyword_stmt_end, trp_print_keyword_stmt
+ */
+struct trt_keyword_stmt {
+ const char *section_name; /**< String containing section name. */
+ const char *argument; /**< Name or path located begind section name. */
+ ly_bool has_node; /**< Flag if section has any nodes. */
+};
+
+/**
+ * @brief Create struct trt_keyword_stmt as empty.
+ */
+#define TRP_EMPTY_KEYWORD_STMT \
+ (struct trt_keyword_stmt) {.section_name = NULL, .argument = NULL, .has_node = 0}
+
+/**********************************************************************
+ * Modify getters
+ *********************************************************************/
+
+struct trt_parent_cache;
+
+/**
+ * @brief Functions that change the state of the tree_ctx structure.
+ *
+ * The 'trop' or 'troc' functions are set here, which provide data
+ * for the 'trp' printing functions and are also called from the
+ * 'trb' browsing functions when walking through a tree. These callback
+ * functions need to be checked or reformulated if changes to the
+ * libyang library affect the printing tree. For all, if the value
+ * cannot be returned, its empty version obtained by relevant TRP_EMPTY
+ * macro is returned.
+ */
+struct trt_fp_modify_ctx {
+ ly_bool (*parent)(struct trt_tree_ctx *); /**< Jump to parent node. Return true if parent exists. */
+ struct trt_node (*first_sibling)(struct trt_parent_cache, struct trt_tree_ctx *); /**< Jump on the first of the siblings. */
+ struct trt_node (*next_sibling)(struct trt_parent_cache, struct trt_tree_ctx *); /**< Jump to next sibling of the current node. */
+ struct trt_node (*next_child)(struct trt_parent_cache, struct trt_tree_ctx *); /**< Jump to the child of the current node. */
+};
+
+/**
+ * @brief Create modify functions for compiled tree.
+ */
+#define TRP_TRT_FP_MODIFY_COMPILED \
+ (struct trt_fp_modify_ctx) { \
+ .parent = troc_modi_parent, \
+ .first_sibling = troc_modi_first_sibling, \
+ .next_sibling = troc_modi_next_sibling, \
+ .next_child = troc_modi_next_child, \
+ }
+
+/**
+ * @brief Create modify functions for parsed tree.
+ */
+#define TRP_TRT_FP_MODIFY_PARSED \
+ (struct trt_fp_modify_ctx) { \
+ .parent = trop_modi_parent, \
+ .first_sibling = trop_modi_first_sibling, \
+ .next_sibling = trop_modi_next_sibling, \
+ .next_child = trop_modi_next_child, \
+ }
+
+/**********************************************************************
+ * Read getters
+ *********************************************************************/
+
+/**
+ * @brief Functions that do not change the state of the tree_structure.
+ *
+ * For details see trt_fp_modify_ctx.
+ */
+struct trt_fp_read {
+ struct trt_keyword_stmt (*module_name)(const struct trt_tree_ctx *); /**< Get name of the module. */
+ struct trt_node (*node)(struct trt_parent_cache, struct trt_tree_ctx *); /**< Get current node. */
+ ly_bool (*if_sibling_exists)(const struct trt_tree_ctx *); /**< Check if node's sibling exists. */
+ ly_bool (*if_parent_exists)(const struct trt_tree_ctx *); /**< Check if node's parent exists. */
+};
+
+/**
+ * @brief Create read functions for compiled tree.
+ */
+#define TRP_TRT_FP_READ_COMPILED \
+ (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 \
+ }
+
+/**
+ * @brief Create read functions for parsed tree.
+ */
+#define TRP_TRT_FP_READ_PARSED \
+ (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 \
+ }
+
+/**********************************************************************
+ * All getters
+ *********************************************************************/
+
+/**
+ * @brief A set of all necessary functions that must be provided
+ * for the printer.
+ */
+struct trt_fp_all {
+ struct trt_fp_modify_ctx modify; /**< Function pointers which modify state of trt_tree_ctx. */
+ struct trt_fp_read read; /**< Function pointers which only reads state of trt_tree_ctx. */
+ struct trt_fp_print print; /**< Functions pointers for printing special items in node. */
+};
+
+/**********************************************************************
+ * Printer context
+ *********************************************************************/
+
+/**
+ * @brief Main structure for @ref TRP_trp part.
+ */
+struct trt_printer_ctx {
+ struct ly_out *out; /**< Handler to printing. */
+ struct trt_fp_all fp; /**< @ref TRP_tro functions callbacks. */
+ size_t max_line_length; /**< The maximum number of characters that can be
+ printed on one line, including the last. */
+};
+
+/**********************************************************************
+ * Tro functions
+ *********************************************************************/
+
+/**
+ * @brief The name of the section to which the node belongs.
+ */
+typedef enum {
+ TRD_SECT_MODULE = 0, /**< The node belongs to the "module: <module_name>:" label. */
+ TRD_SECT_AUGMENT, /**< The node belongs to some "augment <target-node>:" label. */
+ TRD_SECT_RPCS, /**< The node belongs to the "rpcs:" label. */
+ TRD_SECT_NOTIF, /**< The node belongs to the "notifications:" label. */
+ TRD_SECT_GROUPING, /**< The node belongs to some "grouping <grouping-name>:" label. */
+ TRD_SECT_PLUG_DATA /**< The node belongs to some plugin section. */
+} trt_actual_section;
+
+/**
+ * @brief Types of nodes that have some effect on their children.
+ */
+typedef enum {
+ TRD_ANCESTOR_ELSE = 0, /**< Everything not listed. */
+ TRD_ANCESTOR_RPC_INPUT, /**< ::LYS_INPUT */
+ TRD_ANCESTOR_RPC_OUTPUT, /**< ::LYS_OUTPUT */
+ TRD_ANCESTOR_NOTIF /**< ::LYS_NOTIF */
+} trt_ancestor_type;
+
+/**
+ * @brief Saved information when browsing the tree downwards.
+ *
+ * This structure helps prevent frequent retrieval of information
+ * from the tree. Functions @ref TRP_trb are designed to preserve
+ * this structures during their recursive calls. This functions do not
+ * interfere in any way with this data. This structure
+ * is used by @ref TRP_trop functions which, thanks to this
+ * structure, can return a node with the correct data. The word
+ * \b parent is in the structure name, because this data refers to
+ * the last parent and at the same time the states of its
+ * ancestors data. Only the function jumping on the child
+ * (next_child(...)) creates this structure, because the pointer
+ * to the current node moves down the tree. It's like passing
+ * the genetic code to children. Some data must be inherited and
+ * there are two approaches to this problem. Either it will always
+ * be determined which inheritance states belong to the current node
+ * (which can lead to regular travel to the root node) or
+ * the inheritance states will be stored during the recursive calls.
+ * So the problem was solved by the second option. Why does
+ * the structure contain this data? Because it walks through
+ * the lysp tree. For walks through the lysc tree is trt_parent_cache
+ * useless.
+ *
+ * @see TRO_EMPTY_PARENT_CACHE, tro_parent_cache_for_child
+ */
+struct trt_parent_cache {
+ trt_ancestor_type ancestor; /**< Some types of nodes have a special effect on their children. */
+ uint16_t lys_status; /**< Inherited status CURR, DEPRC, OBSLT. */
+ uint16_t lys_config; /**< Inherited config W or R. */
+ const struct lysp_node_list *last_list; /**< The last ::LYS_LIST passed. */
+};
+
+/**
+ * @brief Return trt_parent_cache filled with default values.
+ */
+#define TRP_EMPTY_PARENT_CACHE \
+ (struct trt_parent_cache) { \
+ .ancestor = TRD_ANCESTOR_ELSE, .lys_status = LYS_STATUS_CURR, \
+ .lys_config = LYS_CONFIG_W, .last_list = NULL \
+ }
+
+/**
+ * @brief Node override from plugin.
+ */
+struct lyplg_ext_sprinter_tree_node_override {
+ const char *flags; /**< Override for \<flags\>. */
+ const char *add_opts; /**< Additional symbols for \<opts\>. */
+};
+
+/**
+ * @brief Context for plugin extension.
+ */
+struct trt_plugin_ctx {
+ struct lyspr_tree_ctx *ctx; /**< Pointer to main context. */
+ struct lyspr_tree_schema *schema; /**< Current schema to print. */
+ ly_bool filtered; /**< Flag if current node is filtered. */
+ struct lyplg_ext_sprinter_tree_node_override node_overr; /**< Current node override. */
+ ly_bool last_schema; /**< Flag if schema is last. */
+ ly_bool last_error; /**< Last error from plugin. */
+};
+
+/**
+ * @brief Main structure for browsing the libyang tree
+ */
+struct trt_tree_ctx {
+ ly_bool lysc_tree; /**< The lysc nodes are used for browsing through the tree.
+ It is assumed that once set, it does not change.
+ If it is true then trt_tree_ctx.pn and
+ trt_tree_ctx.tpn are not used.
+ If it is false then trt_tree_ctx.cn is not used. */
+ trt_actual_section section; /**< To which section pn points. */
+ const struct lysp_module *pmod; /**< Parsed YANG schema tree. */
+ const struct lysc_module *cmod; /**< Compiled YANG schema tree. */
+ const struct lysp_node *pn; /**< Actual pointer to parsed node. */
+ const struct lysp_node *tpn; /**< Pointer to actual top-node. */
+ const struct lysc_node *cn; /**< Actual pointer to compiled node. */
+ LY_ERR last_error; /**< Error value during printing. */
+
+ struct trt_plugin_ctx plugin_ctx; /**< Context for plugin. */
+};
+
+/**
+ * @brief Create empty node override.
+ */
+#define TRP_TREE_CTX_EMPTY_NODE_OVERR \
+ (struct lyplg_ext_sprinter_tree_node_override) { \
+ .flags = NULL, \
+ .add_opts = NULL, \
+ }
+
+/**
+ * @brief Check if lysp node is available from
+ * the current compiled node.
+ *
+ * Use only if trt_tree_ctx.lysc_tree is set to true.
+ */
+#define TRP_TREE_CTX_LYSP_NODE_PRESENT(CN) \
+ (CN->priv)
+
+/**
+ * @brief Get lysp_node from trt_tree_ctx.cn.
+ *
+ * Use only if :TRP_TREE_CTX_LYSP_NODE_PRESENT returns true
+ * for that node.
+ */
+#define TRP_TREE_CTX_GET_LYSP_NODE(CN) \
+ ((const struct lysp_node *)CN->priv)
+
+/** Getter function for ::trop_node_charptr(). */
+typedef const char *(*trt_get_charptr_func)(const struct lysp_node *pn);
+
+/**
+ * @brief Simple getter functions for lysp and lysc nodes.
+ *
+ * This structure is useful if we have a general algorithm
+ * (tro function) that can be used for both lysc and lysp nodes.
+ * Thanks to this structure, we prevent code redundancy.
+ * We don't have to write basically the same algorithm twice
+ * for lysp and lysc trees.
+ */
+struct tro_getters {
+ uint16_t (*nodetype)(const void *); /**< Get nodetype. */
+ const void *(*next)(const void *); /**< Get sibling. */
+ const void *(*parent)(const void *); /**< Get parent. */
+ const void *(*child)(const void *); /**< Get child. */
+ const void *(*actions)(const void *); /**< Get actions. */
+ const void *(*action_input)(const void *); /**< Get input action from action node. */
+ const void *(*action_output)(const void *); /**< Get output action from action node. */
+ const void *(*notifs)(const void *); /**< Get notifs. */
+};
+
+/**********************************************************************
+ * Definition of the general Trg functions
+ *********************************************************************/
+
+/**
+ * @brief Print a substring but limited to the maximum length.
+ * @param[in] str is pointer to source.
+ * @param[in] len is number of characters to be printed.
+ * @param[in,out] out is output handler.
+ * @return str parameter shifted by len.
+ */
+static const char *
+trg_print_substr(const char *str, size_t len, struct ly_out *out)
+{
+ for (size_t i = 0; i < len; i++) {
+ ly_print_(out, "%c", str[0]);
+ str++;
+ }
+ return str;
+}
+
+/**
+ * @brief Pointer is not NULL and does not point to an empty string.
+ * @param[in] str is pointer to string to be checked.
+ * @return 1 if str pointing to non empty string otherwise 0.
+ */
+static ly_bool
+trg_charptr_has_data(const char *str)
+{
+ return (str) && (str[0] != '\0');
+}
+
+/**
+ * @brief Check if @p word in @p src is present where words are
+ * delimited by @p delim.
+ * @param[in] src is source where words are separated by @p delim.
+ * @param[in] word to be searched.
+ * @param[in] delim is delimiter between @p words in @p src.
+ * @return 1 if src contains @p word otherwise 0.
+ */
+static ly_bool
+trg_word_is_present(const char *src, const char *word, char delim)
+{
+ const char *hit;
+
+ if ((!src) || (src[0] == '\0') || (!word)) {
+ return 0;
+ }
+
+ hit = strstr(src, word);
+
+ if (hit) {
+ /* word was founded at the begin of src
+ * OR it match somewhere after delim
+ */
+ if ((hit == src) || (hit[-1] == delim)) {
+ /* end of word was founded at the end of src
+ * OR end of word was match somewhere before delim
+ */
+ char delim_or_end = (hit + strlen(word))[0];
+
+ if ((delim_or_end == '\0') || (delim_or_end == delim)) {
+ return 1;
+ }
+ }
+ /* after -> hit is just substr and it's not the whole word */
+ /* jump to the next word */
+ for ( ; (src[0] != '\0') && (src[0] != delim); src++) {}
+ /* skip delim */
+ src = src[0] == '\0' ? src : src + 1;
+ /* continue with searching */
+ return trg_word_is_present(src, word, delim);
+ } else {
+ return 0;
+ }
+}
+
+/**********************************************************************
+ * Definition of printer functions
+ *********************************************************************/
+
+/**
+ * @brief Write callback for ::ly_out_new_clb().
+ *
+ * @param[in] user_data is type of struct ly_out_clb_arg.
+ * @param[in] buf contains input characters
+ * @param[in] count is number of characters in buf.
+ * @return Number of printed bytes.
+ * @return Negative value in case of error.
+ */
+static ssize_t
+trp_ly_out_clb_func(void *user_data, const void *buf, size_t count)
+{
+ LY_ERR erc = LY_SUCCESS;
+ struct ly_out_clb_arg *data = (struct ly_out_clb_arg *)user_data;
+
+ switch (data->mode) {
+ case TRD_PRINT:
+ erc = ly_write_(data->out, buf, count);
+ break;
+ case TRD_CHAR_COUNT:
+ data->counter = data->counter + count;
+ break;
+ default:
+ break;
+ }
+
+ if (erc != LY_SUCCESS) {
+ data->last_error = erc;
+ return -1;
+ } else {
+ return count;
+ }
+}
+
+/**
+ * @brief Check that indent in node can be considered as equivalent.
+ * @param[in] first is the first indent in node.
+ * @param[in] second is the second indent in node.
+ * @return 1 if indents are equivalent otherwise 0.
+ */
+static ly_bool
+trp_indent_in_node_are_eq(struct trt_indent_in_node first, struct trt_indent_in_node second)
+{
+ const ly_bool a = first.type == second.type;
+ const ly_bool b = first.btw_name_opts == second.btw_name_opts;
+ const ly_bool c = first.btw_opts_type == second.btw_opts_type;
+ const ly_bool d = first.btw_type_iffeatures == second.btw_type_iffeatures;
+
+ return a && b && c && d;
+}
+
+/**
+ * @brief Setting space character because node is last sibling.
+ * @param[in] wr is wrapper over which the shift operation
+ * is to be performed.
+ * @return New shifted wrapper.
+ */
+static struct trt_wrapper
+trp_wrapper_set_shift(struct trt_wrapper wr)
+{
+ assert(wr.actual_pos < 64);
+ /* +--<node>
+ * +--<node>
+ */
+ wr.actual_pos++;
+ return wr;
+}
+
+/**
+ * @brief Setting '|' symbol because node is divided or
+ * it is not last sibling.
+ * @param[in] wr is source of wrapper.
+ * @return New wrapper which is marked at actual position and shifted.
+ */
+static struct trt_wrapper
+trp_wrapper_set_mark(struct trt_wrapper wr)
+{
+ assert(wr.actual_pos < 64);
+ wr.bit_marks1 |= 1U << wr.actual_pos;
+ return trp_wrapper_set_shift(wr);
+}
+
+/**
+ * @brief Setting ' ' symbol if node is last sibling otherwise set '|'.
+ * @param[in] wr is actual wrapper.
+ * @param[in] last_one is flag. Value 1 saying if the node is the last
+ * and has no more siblings.
+ * @return New wrapper for the actual node.
+ */
+static struct trt_wrapper
+trp_wrapper_if_last_sibling(struct trt_wrapper wr, ly_bool last_one)
+{
+ return last_one ? trp_wrapper_set_shift(wr) : trp_wrapper_set_mark(wr);
+}
+
+/**
+ * @brief Test if the wrappers are equivalent.
+ * @param[in] first is the first wrapper.
+ * @param[in] second is the second wrapper.
+ * @return 1 if the wrappers are equivalent otherwise 0.
+ */
+static ly_bool
+trp_wrapper_eq(struct trt_wrapper first, struct trt_wrapper second)
+{
+ const ly_bool a = first.type == second.type;
+ const ly_bool b = first.bit_marks1 == second.bit_marks1;
+ const ly_bool c = first.actual_pos == second.actual_pos;
+
+ return a && b && c;
+}
+
+/**
+ * @brief Print " | " sequence on line.
+ * @param[in] wr is wrapper to be printed.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_wrapper(struct trt_wrapper wr, struct ly_out *out)
+{
+ uint32_t lb;
+
+ if (wr.type == TRD_WRAPPER_TOP) {
+ lb = TRD_INDENT_LINE_BEGIN;
+ } else if (wr.type == TRD_WRAPPER_BODY) {
+ lb = TRD_INDENT_LINE_BEGIN * 2;
+ } else {
+ lb = TRD_INDENT_LINE_BEGIN;
+ }
+
+ ly_print_(out, "%*c", lb, ' ');
+
+ if (trp_wrapper_eq(wr, TRP_INIT_WRAPPER_TOP)) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < wr.actual_pos; i++) {
+ /** Test if the bit on the index is set. */
+ if ((wr.bit_marks1 >> i) & 1U) {
+ ly_print_(out, "|");
+ } else {
+ ly_print_(out, " ");
+ }
+
+ if (i != wr.actual_pos) {
+ ly_print_(out, "%*c", TRD_INDENT_BTW_SIBLINGS, ' ');
+ }
+ }
+}
+
+/**
+ * @brief Check if struct trt_node is empty.
+ * @param[in] node is item to test.
+ * @return 1 if node is considered empty otherwise 0.
+ */
+static ly_bool
+trp_node_is_empty(const struct trt_node *node)
+{
+ const ly_bool a = TRP_EMPTY_TRT_IFFEATURES_IS_EMPTY(node->iffeatures.type);
+ const ly_bool b = TRP_TRT_TYPE_IS_EMPTY(node->type);
+ const ly_bool c = TRP_NODE_NAME_IS_EMPTY(node->name);
+ const ly_bool d = node->flags == NULL;
+ const ly_bool e = node->status == NULL;
+
+ return a && b && c && d && e;
+}
+
+/**
+ * @brief Check if [\<keys\>], \<type\> and
+ * \<iffeatures\> are empty/not_set.
+ * @param[in] node is item to test.
+ * @return 1 if node has no \<keys\> \<type\> or \<iffeatures\>
+ * otherwise 0.
+ */
+static ly_bool
+trp_node_body_is_empty(const struct trt_node *node)
+{
+ const ly_bool a = TRP_EMPTY_TRT_IFFEATURES_IS_EMPTY(node->iffeatures.type);
+ const ly_bool b = TRP_TRT_TYPE_IS_EMPTY(node->type);
+ const ly_bool c = !node->name.keys;
+
+ return a && b && c;
+}
+
+/**
+ * @brief Print entire struct trt_node_name structure.
+ * @param[in] node_name is item to print.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_node_name(struct trt_node_name node_name, struct ly_out *out)
+{
+ const char *mod_prefix;
+ const char *colon;
+ const char trd_node_name_suffix_choice[] = ")";
+ const char trd_node_name_suffix_case[] = ")";
+
+ if (TRP_NODE_NAME_IS_EMPTY(node_name)) {
+ return;
+ }
+
+ if (node_name.module_prefix) {
+ mod_prefix = node_name.module_prefix;
+ colon = ":";
+ } else {
+ mod_prefix = "";
+ colon = "";
+ }
+
+ switch (node_name.type) {
+ case TRD_NODE_ELSE:
+ ly_print_(out, "%s%s%s", mod_prefix, colon, node_name.str);
+ break;
+ case TRD_NODE_CASE:
+ ly_print_(out, "%s%s%s%s%s", TRD_NODE_NAME_PREFIX_CASE, mod_prefix, colon, node_name.str, trd_node_name_suffix_case);
+ break;
+ case TRD_NODE_CHOICE:
+ ly_print_(out, "%s%s%s%s%s", TRD_NODE_NAME_PREFIX_CHOICE, mod_prefix, colon, node_name.str, trd_node_name_suffix_choice);
+ break;
+ case TRD_NODE_TRIPLE_DOT:
+ ly_print_(out, "%s", TRD_NODE_NAME_TRIPLE_DOT);
+ break;
+ default:
+ break;
+ }
+
+ if (node_name.add_opts) {
+ ly_print_(out, "%s", node_name.add_opts);
+ }
+ if (node_name.opts) {
+ ly_print_(out, "%s", node_name.opts);
+ }
+}
+
+/**
+ * @brief Check if mark (?, !, *, /, @) is implicitly contained in
+ * struct trt_node_name.
+ * @param[in] node_name is structure containing the 'mark'.
+ * @return 1 if contain otherwise 0.
+ */
+static ly_bool
+trp_mark_is_used(struct trt_node_name node_name)
+{
+ if (TRP_NODE_NAME_IS_EMPTY(node_name)) {
+ return 0;
+ } else if (node_name.keys) {
+ return 0;
+ }
+
+ switch (node_name.type) {
+ case TRD_NODE_ELSE:
+ case TRD_NODE_CASE:
+ return 0;
+ default:
+ if (node_name.add_opts || node_name.opts) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/**
+ * @brief Print opts keys.
+ * @param[in] node_name contains type of the node with his name.
+ * @param[in] btw_name_opts is number of spaces between name and [keys].
+ * @param[in] cf is basically a pointer to the function that prints
+ * the keys.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_opts_keys(struct trt_node_name node_name, int16_t btw_name_opts, struct trt_cf_print cf, struct ly_out *out)
+{
+ if (!node_name.keys) {
+ return;
+ }
+
+ /* <name><mark>___<keys>*/
+ if (btw_name_opts > 0) {
+ ly_print_(out, "%*c", btw_name_opts, ' ');
+ }
+ ly_print_(out, "[");
+ cf.pf(cf.ctx, out);
+ ly_print_(out, "]");
+}
+
+/**
+ * @brief Print entire struct trt_type structure.
+ * @param[in] type is item to print.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_type(struct trt_type type, struct ly_out *out)
+{
+ if (TRP_TRT_TYPE_IS_EMPTY(type)) {
+ return;
+ }
+
+ switch (type.type) {
+ case TRD_TYPE_NAME:
+ ly_print_(out, "%s", type.str);
+ break;
+ case TRD_TYPE_TARGET:
+ ly_print_(out, "-> %s", type.str);
+ break;
+ case TRD_TYPE_LEAFREF:
+ ly_print_(out, "leafref");
+ default:
+ break;
+ }
+}
+
+/**
+ * @brief Print all iffeatures of node
+ *
+ * @param[in] iff is iffeatures to print.
+ * @param[in] cf is basically a pointer to the function that prints the list of features.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_iffeatures(struct trt_iffeatures iff, struct trt_cf_print cf, struct ly_out *out)
+{
+ if (iff.type == TRD_IFF_PRESENT) {
+ ly_print_(out, "{");
+ cf.pf(cf.ctx, out);
+ ly_print_(out, "}?");
+ } else if (iff.type == TRD_IFF_OVERR) {
+ ly_print_(out, "%s", iff.str);
+ }
+}
+
+/**
+ * @brief Print just \<status\>--\<flags\> \<name\> with opts mark.
+ * @param[in] node contains items to print.
+ * @param[in] out is output handler.
+ */
+static void
+trp_print_node_up_to_name(const struct trt_node *node, struct ly_out *out)
+{
+ if (node->name.type == TRD_NODE_TRIPLE_DOT) {
+ trp_print_node_name(node->name, out);
+ return;
+ }
+ /* <status>--<flags> */
+ ly_print_(out, "%s", node->status);
+ ly_print_(out, "--");
+ /* If the node is a case node, there is no space before the <name>
+ * also case node has no flags.
+ */
+ if (node->flags && (node->name.type != TRD_NODE_CASE)) {
+ ly_print_(out, "%s", node->flags);
+ ly_print_(out, " ");
+ }
+ /* <name> */
+ trp_print_node_name(node->name, out);
+}
+
+/**
+ * @brief Print alignment (spaces) instead of
+ * \<status\>--\<flags\> \<name\> for divided node.
+ * @param[in] node contains items to print.
+ * @param[in] out is output handler.
+ */
+static void
+trp_print_divided_node_up_to_name(const struct trt_node *node, struct ly_out *out)
+{
+ uint32_t space = strlen(node->flags);
+
+ if (node->name.type == TRD_NODE_CASE) {
+ /* :(<name> */
+ space += strlen(TRD_NODE_NAME_PREFIX_CASE);
+ } else if (node->name.type == TRD_NODE_CHOICE) {
+ /* (<name> */
+ space += strlen(TRD_NODE_NAME_PREFIX_CHOICE);
+ } else {
+ /* _<name> */
+ space += strlen(" ");
+ }
+
+ /* <name>
+ * __
+ */
+ space += TRD_INDENT_LONG_LINE_BREAK;
+
+ ly_print_(out, "%*c", space, ' ');
+}
+
+/**
+ * @brief Print struct trt_node structure.
+ * @param[in] node is item to print.
+ * @param[in] pck package of functions for
+ * printing [\<keys\>] and \<iffeatures\>.
+ * @param[in] indent is the indent in node.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_node(const struct trt_node *node, struct trt_pck_print pck, struct trt_indent_in_node indent, struct ly_out *out)
+{
+ ly_bool triple_dot;
+ ly_bool divided;
+ struct trt_cf_print cf_print_keys;
+ struct trt_cf_print cf_print_iffeatures;
+
+ if (trp_node_is_empty(node)) {
+ return;
+ }
+
+ /* <status>--<flags> <name><opts> <type> <if-features> */
+ triple_dot = node->name.type == TRD_NODE_TRIPLE_DOT;
+ divided = indent.type == TRD_INDENT_IN_NODE_DIVIDED;
+
+ if (triple_dot) {
+ trp_print_node_name(node->name, out);
+ return;
+ } else if (!divided) {
+ trp_print_node_up_to_name(node, out);
+ } else {
+ trp_print_divided_node_up_to_name(node, out);
+ }
+
+ /* <opts> */
+ /* <name>___<opts>*/
+ cf_print_keys.ctx = pck.tree_ctx;
+ cf_print_keys.pf = pck.fps.print_keys;
+
+ trp_print_opts_keys(node->name, indent.btw_name_opts, cf_print_keys, out);
+
+ /* <opts>__<type> */
+ if (indent.btw_opts_type > 0) {
+ ly_print_(out, "%*c", indent.btw_opts_type, ' ');
+ }
+
+ /* <type> */
+ trp_print_type(node->type, out);
+
+ /* <type>__<iffeatures> */
+ if (indent.btw_type_iffeatures > 0) {
+ ly_print_(out, "%*c", indent.btw_type_iffeatures, ' ');
+ }
+
+ /* <iffeatures> */
+ cf_print_iffeatures.ctx = pck.tree_ctx;
+ cf_print_iffeatures.pf = pck.fps.print_features_names;
+
+ trp_print_iffeatures(node->iffeatures, cf_print_iffeatures, out);
+}
+
+/**
+ * @brief Print keyword based on trt_keyword_stmt.type.
+ * @param[in] ks is keyword statement to print.
+ * @param[in,out] out is output handler
+ */
+static void
+trt_print_keyword_stmt_begin(struct trt_keyword_stmt ks, struct ly_out *out)
+{
+ if (!strcmp(ks.section_name, TRD_KEYWORD_MODULE) ||
+ !strcmp(ks.section_name, TRD_KEYWORD_SUBMODULE)) {
+ ly_print_(out, "%s: ", ks.section_name);
+ return;
+ }
+
+ ly_print_(out, "%*c", TRD_INDENT_LINE_BEGIN, ' ');
+ if (ks.argument) {
+ ly_print_(out, "%s ", ks.section_name);
+ } else {
+ ly_print_(out, "%s", ks.section_name);
+ }
+}
+
+/**
+ * @brief Print trt_keyword_stmt.str which is string of name or path.
+ * @param[in] ks is keyword statement structure.
+ * @param[in] mll is max line length.
+ * @param[in,out] out is output handler.
+ */
+static void
+trt_print_keyword_stmt_str(struct trt_keyword_stmt ks, size_t mll, struct ly_out *out)
+{
+ uint32_t ind_initial;
+ uint32_t ind_divided;
+ /* flag if path must be splitted to more lines */
+ ly_bool linebreak_was_set;
+ /* flag if at least one subpath was printed */
+ ly_bool subpath_printed;
+ /* the sum of the sizes of the substrings on the current line */
+ uint32_t how_far;
+ /* pointer to start of the subpath */
+ const char *sub_ptr;
+ /* size of subpath from sub_ptr */
+ size_t sub_len;
+
+ if ((!ks.argument) || (ks.argument[0] == '\0')) {
+ return;
+ }
+
+ /* module name cannot be splitted */
+ if (!strcmp(ks.section_name, TRD_KEYWORD_MODULE) || !strcmp(ks.section_name, TRD_KEYWORD_SUBMODULE)) {
+ ly_print_(out, "%s", ks.argument);
+ return;
+ }
+
+ /* after -> for trd_keyword_stmt_body do */
+
+ /* set begin indentation */
+ ind_initial = TRD_INDENT_LINE_BEGIN + strlen(ks.section_name) + 1;
+ ind_divided = ind_initial + TRD_INDENT_LONG_LINE_BREAK;
+ linebreak_was_set = 0;
+ subpath_printed = 0;
+ how_far = 0;
+ sub_ptr = ks.argument;
+ sub_len = 0;
+
+ while (sub_ptr[0] != '\0') {
+ uint32_t ind;
+ /* skip slash */
+ const char *tmp = sub_ptr[0] == '/' ? sub_ptr + 1 : sub_ptr;
+
+ /* get position of the end of substr */
+ tmp = strchr(tmp, '/');
+ /* set correct size if this is a last substring */
+ sub_len = !tmp ? strlen(sub_ptr) : (size_t)(tmp - sub_ptr);
+ /* actualize sum of the substring's sizes on the current line */
+ how_far += sub_len;
+ /* correction due to colon character if it this is last substring */
+ how_far = *(sub_ptr + sub_len) == '\0' ? how_far + 1 : how_far;
+ /* choose indentation which depends on
+ * whether the string is printed on multiple lines or not
+ */
+ ind = linebreak_was_set ? ind_divided : ind_initial;
+ if (ind + how_far <= mll) {
+ /* printing before max line length */
+ sub_ptr = trg_print_substr(sub_ptr, sub_len, out);
+ subpath_printed = 1;
+ } else {
+ /* printing on new line */
+ if (subpath_printed == 0) {
+ /* first subpath is too long
+ * but print it at first line anyway
+ */
+ sub_ptr = trg_print_substr(sub_ptr, sub_len, out);
+ subpath_printed = 1;
+ continue;
+ }
+ ly_print_(out, "\n");
+ ly_print_(out, "%*c", ind_divided, ' ');
+ linebreak_was_set = 1;
+ sub_ptr = trg_print_substr(sub_ptr, sub_len, out);
+ how_far = sub_len;
+ subpath_printed = 1;
+ }
+ }
+}
+
+/**
+ * @brief Print separator based on trt_keyword_stmt.type
+ * @param[in] ks is keyword statement structure.
+ * @param[in,out] out is output handler.
+ */
+static void
+trt_print_keyword_stmt_end(struct trt_keyword_stmt ks, struct ly_out *out)
+{
+ if (!strcmp(ks.section_name, TRD_KEYWORD_MODULE) || !strcmp(ks.section_name, TRD_KEYWORD_SUBMODULE)) {
+ return;
+ } else if (ks.has_node) {
+ ly_print_(out, ":");
+ }
+}
+
+/**
+ * @brief Print entire struct trt_keyword_stmt structure.
+ * @param[in] ks is item to print.
+ * @param[in] mll is max line length.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_keyword_stmt(struct trt_keyword_stmt ks, size_t mll, struct ly_out *out)
+{
+ assert(ks.section_name);
+ trt_print_keyword_stmt_begin(ks, out);
+ trt_print_keyword_stmt_str(ks, mll, out);
+ trt_print_keyword_stmt_end(ks, out);
+}
+
+/**********************************************************************
+ * Main trp functions
+ *********************************************************************/
+
+/**
+ * @brief Printing one line including wrapper and node
+ * which can be incomplete (divided).
+ * @param[in] node is \<node\> representation.
+ * @param[in] pck contains special printing functions callback.
+ * @param[in] indent contains wrapper and indent in node numbers.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_line(const struct trt_node *node, struct trt_pck_print pck, struct trt_pck_indent indent, struct ly_out *out)
+{
+ trp_print_wrapper(indent.wrapper, out);
+ trp_print_node(node, pck, indent.in_node, out);
+}
+
+/**
+ * @brief Printing one line including wrapper and
+ * \<status\>--\<flags\> \<name\>\<option_mark\>.
+ * @param[in] node is \<node\> representation.
+ * @param[in] wr is wrapper for printing indentation before node.
+ * @param[in] out is output handler.
+ */
+static void
+trp_print_line_up_to_node_name(const struct trt_node *node, struct trt_wrapper wr, struct ly_out *out)
+{
+ trp_print_wrapper(wr, out);
+ trp_print_node_up_to_name(node, out);
+}
+
+/**
+ * @brief Check if leafref target must be change to string 'leafref'
+ * because his target string is too long.
+ * @param[in] node containing leafref target.
+ * @param[in] wr is wrapper for printing indentation before node.
+ * @param[in] mll is max line length.
+ * @param[in] out is output handler.
+ * @return true if leafref must be changed to string 'leafref'.
+ */
+static ly_bool
+trp_leafref_target_is_too_long(const struct trt_node *node, struct trt_wrapper wr, size_t mll, struct ly_out *out)
+{
+ size_t type_len;
+ struct ly_out_clb_arg *data;
+
+ if (node->type.type != TRD_TYPE_TARGET) {
+ return 0;
+ }
+
+ /* set ly_out to counting characters */
+ data = out->method.clb.arg;
+
+ data->counter = 0;
+ data->mode = TRD_CHAR_COUNT;
+ /* count number of printed bytes */
+ trp_print_wrapper(wr, out);
+ ly_print_(out, "%*c", TRD_INDENT_BTW_SIBLINGS, ' ');
+ trp_print_divided_node_up_to_name(node, out);
+ data->mode = TRD_PRINT;
+ type_len = strlen(node->type.str);
+
+ return data->counter + type_len > mll;
+}
+
+/**
+ * @brief Get default indent in node based on node values.
+ * @param[in] node is \<node\> representation.
+ * @return Default indent in node assuming that the node
+ * will not be divided.
+ */
+static struct trt_indent_in_node
+trp_default_indent_in_node(const struct trt_node *node)
+{
+ struct trt_indent_in_node ret;
+ uint32_t opts_len = 0;
+
+ ret.type = TRD_INDENT_IN_NODE_NORMAL;
+
+ /* btw_name_opts */
+ ret.btw_name_opts = node->name.keys ? TRD_INDENT_BEFORE_KEYS : 0;
+
+ /* btw_opts_type */
+ if (!(TRP_TRT_TYPE_IS_EMPTY(node->type))) {
+ if (trp_mark_is_used(node->name)) {
+ opts_len += node->name.add_opts ? strlen(node->name.add_opts) : 0;
+ opts_len += node->name.opts ? strlen(node->name.opts) : 0;
+ ret.btw_opts_type = TRD_INDENT_BEFORE_TYPE > opts_len ? 1 : TRD_INDENT_BEFORE_TYPE - opts_len;
+ } else {
+ ret.btw_opts_type = TRD_INDENT_BEFORE_TYPE;
+ }
+ } else {
+ ret.btw_opts_type = 0;
+ }
+
+ /* btw_type_iffeatures */
+ ret.btw_type_iffeatures = node->iffeatures.type == TRD_IFF_PRESENT ? TRD_INDENT_BEFORE_IFFEATURES : 0;
+
+ return ret;
+}
+
+/**
+ * @brief Setting linebreaks in trt_indent_in_node.
+ *
+ * The order where the linebreak tag can be placed is from the end.
+ *
+ * @param[in] indent containing alignment lengths
+ * or already linebreak marks.
+ * @return indent with a newly placed linebreak tag.
+ * @return .type set to TRD_INDENT_IN_NODE_FAILED if it is not possible
+ * to place a more linebreaks.
+ */
+static struct trt_indent_in_node
+trp_indent_in_node_place_break(struct trt_indent_in_node indent)
+{
+ /* somewhere must be set a line break in node */
+ struct trt_indent_in_node ret = indent;
+
+ /* gradually break the node from the end */
+ if ((indent.btw_type_iffeatures != TRD_LINEBREAK) && (indent.btw_type_iffeatures != 0)) {
+ ret.btw_type_iffeatures = TRD_LINEBREAK;
+ } else if ((indent.btw_opts_type != TRD_LINEBREAK) && (indent.btw_opts_type != 0)) {
+ ret.btw_opts_type = TRD_LINEBREAK;
+ } else if ((indent.btw_name_opts != TRD_LINEBREAK) && (indent.btw_name_opts != 0)) {
+ /* set line break between name and opts */
+ ret.btw_name_opts = TRD_LINEBREAK;
+ } else {
+ /* it is not possible to place a more line breaks,
+ * unfortunately the max_line_length constraint is violated
+ */
+ ret.type = TRD_INDENT_IN_NODE_FAILED;
+ }
+ return ret;
+}
+
+/**
+ * @brief Set the first half of the node based on the linebreak mark.
+ *
+ * Items in the second half of the node will be empty.
+ *
+ * @param[in,out] innod contains information in which part of the \<node\>
+ * the first half ends. Set first half of the node, indent is unchanged.
+ */
+static void
+trp_first_half_node(struct trt_pair_indent_node *innod)
+{
+ if (innod->indent.btw_name_opts == TRD_LINEBREAK) {
+ innod->node.type = TRP_EMPTY_TRT_TYPE;
+ innod->node.iffeatures = TRP_EMPTY_TRT_IFFEATURES;
+ } else if (innod->indent.btw_opts_type == TRD_LINEBREAK) {
+ innod->node.type = TRP_EMPTY_TRT_TYPE;
+ innod->node.iffeatures = TRP_EMPTY_TRT_IFFEATURES;
+ } else if (innod->indent.btw_type_iffeatures == TRD_LINEBREAK) {
+ innod->node.iffeatures = TRP_EMPTY_TRT_IFFEATURES;
+ }
+}
+
+/**
+ * @brief Set the second half of the node based on the linebreak mark.
+ *
+ * Items in the first half of the node will be empty.
+ * Indentations belonging to the first node will be reset to zero.
+ *
+ * @param[in,out] innod contains information in which part of the \<node\>
+ * the second half starts. Set second half of the node, indent is newly set.
+ */
+static void
+trp_second_half_node(struct trt_pair_indent_node *innod)
+{
+ if (innod->indent.btw_name_opts < 0) {
+ /* Logically, the information up to token <opts> should
+ * be deleted, but the the trp_print_node function needs it to
+ * create the correct indent.
+ */
+ innod->indent.btw_name_opts = 0;
+ innod->indent.btw_opts_type = TRP_TRT_TYPE_IS_EMPTY(innod->node.type) ? 0 : TRD_INDENT_BEFORE_TYPE;
+ innod->indent.btw_type_iffeatures = innod->node.iffeatures.type == TRD_IFF_NON_PRESENT ? 0 : TRD_INDENT_BEFORE_IFFEATURES;
+ } else if (innod->indent.btw_opts_type == TRD_LINEBREAK) {
+ innod->indent.btw_name_opts = 0;
+ innod->indent.btw_opts_type = 0;
+ innod->indent.btw_type_iffeatures = innod->node.iffeatures.type == TRD_IFF_NON_PRESENT ? 0 : TRD_INDENT_BEFORE_IFFEATURES;
+ } else if (innod->indent.btw_type_iffeatures == TRD_LINEBREAK) {
+ innod->node.type = TRP_EMPTY_TRT_TYPE;
+ innod->indent.btw_name_opts = 0;
+ innod->indent.btw_opts_type = 0;
+ innod->indent.btw_type_iffeatures = 0;
+ }
+}
+
+/**
+ * @brief Get the correct alignment for the node.
+ *
+ * This function is recursively called itself. It's like a backend
+ * function for a function ::trp_try_normal_indent_in_node().
+ *
+ * @param[in] pck contains speciall callback functions for printing.
+ * @param[in] wrapper contains information about '|' context.
+ * @param[in] mll is max line length.
+ * @param[in,out] cnt counting number of characters to print.
+ * @param[in,out] out is output handler.
+ * @param[in,out] innod pair of node and indentation numbers of that node.
+ */
+static void
+trp_try_normal_indent_in_node_(struct trt_pck_print pck, struct trt_wrapper wrapper, size_t mll, size_t *cnt,
+ struct ly_out *out, struct trt_pair_indent_node *innod)
+{
+ trp_print_line(&innod->node, pck, TRP_INIT_PCK_INDENT(wrapper, innod->indent), out);
+
+ if (*cnt <= mll) {
+ /* success */
+ return;
+ } else {
+ innod->indent = trp_indent_in_node_place_break(innod->indent);
+ if (innod->indent.type != TRD_INDENT_IN_NODE_FAILED) {
+ /* erase information in node due to line break */
+ trp_first_half_node(innod);
+ /* check if line fits, recursive call */
+ *cnt = 0;
+ trp_try_normal_indent_in_node_(pck, wrapper, mll, cnt, out, innod);
+ /* make sure that the result will be with the status divided
+ * or eventually with status failed */
+ innod->indent.type = innod->indent.type == TRD_INDENT_IN_NODE_FAILED ? TRD_INDENT_IN_NODE_FAILED : TRD_INDENT_IN_NODE_DIVIDED;
+ }
+ return;
+ }
+}
+
+/**
+ * @brief Get the correct alignment for the node.
+ *
+ * @param[in] node is \<node\> representation.
+ * @param[in] pck contains speciall callback functions for printing.
+ * @param[in] indent contains wrapper and indent in node numbers.
+ * @param[in] mll is max line length.
+ * @param[in,out] out is output handler.
+ * @param[out] innod If the node does not fit in the line, some indent variable has negative value as a line break sign
+ * and therefore ::TRD_INDENT_IN_NODE_DIVIDED is set.
+ * If the node fits into the line, all indent variables values has non-negative number and therefore
+ * ::TRD_INDENT_IN_NODE_NORMAL is set.
+ * If the node does not fit into the line, all indent variables has negative or zero values, function failed
+ * and therefore ::TRD_INDENT_IN_NODE_FAILED is set.
+ */
+static void
+trp_try_normal_indent_in_node(const struct trt_node *node, struct trt_pck_print pck, struct trt_pck_indent indent,
+ size_t mll, struct ly_out *out, struct trt_pair_indent_node *innod)
+{
+ struct ly_out_clb_arg *data;
+
+ *innod = TRP_INIT_PAIR_INDENT_NODE(indent.in_node, *node);
+
+ /* set ly_out to counting characters */
+ data = out->method.clb.arg;
+
+ data->counter = 0;
+ data->mode = TRD_CHAR_COUNT;
+ trp_try_normal_indent_in_node_(pck, indent.wrapper, mll, &data->counter, out, innod);
+ data->mode = TRD_PRINT;
+}
+
+/**
+ * @brief Auxiliary function for ::trp_print_entire_node()
+ * that prints split nodes.
+ * @param[in] node is node representation.
+ * @param[in] ppck contains speciall callback functions for printing.
+ * @param[in] ipck contains wrapper and indent in node numbers.
+ * @param[in] mll is max line length.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_divided_node(const struct trt_node *node, struct trt_pck_print ppck, struct trt_pck_indent ipck, size_t mll, struct ly_out *out)
+{
+ ly_bool entire_node_was_printed;
+ struct trt_pair_indent_node innod;
+
+ trp_try_normal_indent_in_node(node, ppck, ipck, mll, out, &innod);
+
+ if (innod.indent.type == TRD_INDENT_IN_NODE_FAILED) {
+ /* nothing can be done, continue as usual */
+ innod.indent.type = TRD_INDENT_IN_NODE_DIVIDED;
+ }
+
+ trp_print_line(&innod.node, ppck, TRP_INIT_PCK_INDENT(ipck.wrapper, innod.indent), out);
+ entire_node_was_printed = trp_indent_in_node_are_eq(ipck.in_node, innod.indent);
+
+ if (!entire_node_was_printed) {
+ ly_print_(out, "\n");
+ /* continue with second half node */
+ innod.node = *node;
+ trp_second_half_node(&innod);
+ /* continue with printing node */
+ trp_print_divided_node(&innod.node, ppck, TRP_INIT_PCK_INDENT(ipck.wrapper, innod.indent), mll, out);
+ } else {
+ return;
+ }
+}
+
+/**
+ * @brief Printing of the wrapper and the whole node,
+ * which can be divided into several lines.
+ * @param[in] node_p is node representation.
+ * @param[in] ppck contains speciall callback functions for printing.
+ * @param[in] ipck contains wrapper and indent in node numbers.
+ * @param[in] mll is max line length.
+ * @param[in,out] out is output handler.
+ */
+static void
+trp_print_entire_node(const struct trt_node *node_p, struct trt_pck_print ppck, struct trt_pck_indent ipck, size_t mll,
+ struct ly_out *out)
+{
+ struct trt_pair_indent_node innod;
+ struct trt_pck_indent tmp;
+ struct trt_node node;
+
+ node = *node_p;
+ if (trp_leafref_target_is_too_long(&node, ipck.wrapper, mll, out)) {
+ node.type.type = TRD_TYPE_LEAFREF;
+ }
+
+ /* check if normal indent is possible */
+ trp_try_normal_indent_in_node(&node, ppck, ipck, mll, out, &innod);
+
+ if (innod.indent.type == TRD_INDENT_IN_NODE_NORMAL) {
+ /* node fits to one line */
+ trp_print_line(&node, ppck, ipck, out);
+ } else if (innod.indent.type == TRD_INDENT_IN_NODE_DIVIDED) {
+ /* node will be divided */
+ /* print first half */
+ tmp = TRP_INIT_PCK_INDENT(ipck.wrapper, innod.indent);
+ /* pretend that this is normal node */
+ tmp.in_node.type = TRD_INDENT_IN_NODE_NORMAL;
+
+ trp_print_line(&innod.node, ppck, tmp, out);
+ ly_print_(out, "\n");
+
+ /* continue with second half on new line */
+ innod.node = node;
+ trp_second_half_node(&innod);
+ tmp = TRP_INIT_PCK_INDENT(trp_wrapper_if_last_sibling(ipck.wrapper, node.last_one), innod.indent);
+
+ trp_print_divided_node(&innod.node, ppck, tmp, mll, out);
+ } else if (innod.indent.type == TRD_INDENT_IN_NODE_FAILED) {
+ /* node name is too long */
+ trp_print_line_up_to_node_name(&node, ipck.wrapper, out);
+
+ if (trp_node_body_is_empty(&node)) {
+ return;
+ } else {
+ ly_print_(out, "\n");
+
+ innod.node = node;
+ trp_second_half_node(&innod);
+ innod.indent.type = TRD_INDENT_IN_NODE_DIVIDED;
+ tmp = TRP_INIT_PCK_INDENT(trp_wrapper_if_last_sibling(ipck.wrapper, node.last_one), innod.indent);
+
+ trp_print_divided_node(&innod.node, ppck, tmp, mll, out);
+ }
+ }
+}
+
+/**
+ * @brief Check if parent-stmt is valid for printing extensinon.
+ *
+ * @param[in] lysc_tree flag if ext is from compiled tree.
+ * @param[in] ext Extension to check.
+ * @return 1 if extension is valid.
+ */
+static ly_bool
+trp_ext_parent_is_valid(ly_bool lysc_tree, void *ext)
+{
+ enum ly_stmt parent_stmt;
+
+ if (lysc_tree) {
+ parent_stmt = ((struct lysc_ext_instance *)ext)->parent_stmt;
+ } else {
+ parent_stmt = ((struct lysp_ext_instance *)ext)->parent_stmt;
+ }
+ if ((parent_stmt & LY_STMT_OP_MASK) || (parent_stmt & LY_STMT_DATA_NODE_MASK) ||
+ (parent_stmt & LY_STMT_SUBMODULE) || parent_stmt & LY_STMT_MODULE) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @brief Check if printer_tree can use node extension.
+ *
+ * @param[in] lysc_tree Flag if @p node is compiled.
+ * @param[in] node to check. Its type is lysc_node or lysp_node.
+ * @return Pointer to extension instance which printer_tree can used.
+ */
+static void *
+trp_ext_is_present(ly_bool lysc_tree, const void *node)
+{
+ const struct lysp_node *pn;
+ const struct lysc_node *cn;
+ LY_ARRAY_COUNT_TYPE i;
+ void *ret = NULL;
+
+ if (!node) {
+ return NULL;
+ }
+
+ if (lysc_tree) {
+ cn = (const struct lysc_node *)node;
+ LY_ARRAY_FOR(cn->exts, i) {
+ if (!(cn->exts && cn->exts->def->plugin && cn->exts->def->plugin->printer_ctree)) {
+ continue;
+ }
+ if (!trp_ext_parent_is_valid(1, &cn->exts[i])) {
+ continue;
+ }
+ ret = &cn->exts[i];
+ break;
+ }
+ } else {
+ pn = (const struct lysp_node *)node;
+ LY_ARRAY_FOR(pn->exts, i) {
+ if (!(pn->exts && pn->exts->record->plugin.printer_ptree)) {
+ continue;
+ }
+ if (!trp_ext_parent_is_valid(0, &pn->exts[i])) {
+ continue;
+ }
+ ret = &pn->exts[i];
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Check if printer_tree can use node extension.
+ *
+ * @param[in] tc Context with current node.
+ * @return 1 if some extension for printer_tree is valid.
+ */
+static ly_bool
+trp_ext_is_present_in_node(struct trt_tree_ctx *tc)
+{
+ if (tc->lysc_tree && trp_ext_is_present(tc->lysc_tree, tc->cn)) {
+ return 1;
+ } else if (trp_ext_is_present(tc->lysc_tree, tc->pn)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Release allocated memory and set pointers to NULL.
+ *
+ * @param[in,out] overr is override structure to release.
+ * @param[out] filtered is flag to reset.
+ */
+static void
+trp_ext_free_node_override(struct lyplg_ext_sprinter_tree_node_override *overr, ly_bool *filtered)
+{
+ *filtered = 0;
+ overr->flags = NULL;
+ overr->add_opts = NULL;
+}
+
+/**
+ * @brief Release private plugin data.
+ *
+ * @param[in,out] plug_ctx is plugin context.
+ */
+static void
+trp_ext_free_plugin_ctx(struct lyspr_tree_ctx *plug_ctx)
+{
+ LY_ARRAY_FREE(plug_ctx->schemas);
+ if (plug_ctx->free_plugin_priv) {
+ plug_ctx->free_plugin_priv(plug_ctx->plugin_priv);
+ }
+}
+
+/**********************************************************************
+ * trop and troc getters
+ *********************************************************************/
+
+/**
+ * @brief Get nodetype.
+ * @param[in] node is any lysp_node.
+ */
+static uint16_t
+trop_nodetype(const void *node)
+{
+ return ((const struct lysp_node *)node)->nodetype;
+}
+
+/**
+ * @brief Get sibling.
+ * @param[in] node is any lysp_node.
+ */
+static const void *
+trop_next(const void *node)
+{
+ return ((const struct lysp_node *)node)->next;
+}
+
+/**
+ * @brief Get parent.
+ * @param[in] node is any lysp_node.
+ */
+static const void *
+trop_parent(const void *node)
+{
+ return ((const struct lysp_node *)node)->parent;
+}
+
+/**
+ * @brief Try to get child.
+ * @param[in] node is any lysp_node.
+ */
+static const void *
+trop_child(const void *node)
+{
+ return lysp_node_child(node);
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node is any lysp_node.
+ */
+static const void *
+trop_actions(const void *node)
+{
+ return lysp_node_actions(node);
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node must be of type lysp_node_action.
+ */
+static const void *
+trop_action_input(const void *node)
+{
+ return &((const struct lysp_node_action *)node)->input;
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node must be of type lysp_node_action.
+ */
+static const void *
+trop_action_output(const void *node)
+{
+ return &((const struct lysp_node_action *)node)->output;
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node is any lysp_node.
+ */
+static const void *
+trop_notifs(const void *node)
+{
+ return lysp_node_notifs(node);
+}
+
+/**
+ * @brief Fill struct tro_getters with @ref TRP_trop getters
+ * which are adapted to lysp nodes.
+ */
+static struct tro_getters
+trop_init_getters(void)
+{
+ return (struct tro_getters) {
+ .nodetype = trop_nodetype,
+ .next = trop_next,
+ .parent = trop_parent,
+ .child = trop_child,
+ .actions = trop_actions,
+ .action_input = trop_action_input,
+ .action_output = trop_action_output,
+ .notifs = trop_notifs
+ };
+}
+
+/**
+ * @brief Get nodetype.
+ * @param[in] node is any lysc_node.
+ */
+static uint16_t
+troc_nodetype(const void *node)
+{
+ return ((const struct lysc_node *)node)->nodetype;
+}
+
+/**
+ * @brief Get sibling.
+ * @param[in] node is any lysc_node.
+ */
+static const void *
+troc_next(const void *node)
+{
+ return ((const struct lysc_node *)node)->next;
+}
+
+/**
+ * @brief Get parent.
+ * @param[in] node is any lysc_node.
+ */
+static const void *
+troc_parent(const void *node)
+{
+ return ((const struct lysc_node *)node)->parent;
+}
+
+/**
+ * @brief Try to get child.
+ * @param[in] node is any lysc_node.
+ */
+static const void *
+troc_child(const void *node)
+{
+ return lysc_node_child(node);
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node is any lysc_node.
+ */
+static const void *
+troc_actions(const void *node)
+{
+ return lysc_node_actions(node);
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node must be of type lysc_node_action.
+ */
+static const void *
+troc_action_input(const void *node)
+{
+ return &((const struct lysc_node_action *)node)->input;
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node must be of type lysc_node_action.
+ */
+static const void *
+troc_action_output(const void *node)
+{
+ return &((const struct lysc_node_action *)node)->output;
+}
+
+/**
+ * @brief Try to get action.
+ * @param[in] node is any lysc_node.
+ */
+static const void *
+troc_notifs(const void *node)
+{
+ return lysc_node_notifs(node);
+}
+
+/**
+ * @brief Fill struct tro_getters with @ref TRP_troc getters
+ * which are adapted to lysc nodes.
+ */
+static struct tro_getters
+troc_init_getters(void)
+{
+ return (struct tro_getters) {
+ .nodetype = troc_nodetype,
+ .next = troc_next,
+ .parent = troc_parent,
+ .child = troc_child,
+ .actions = troc_actions,
+ .action_input = troc_action_input,
+ .action_output = troc_action_output,
+ .notifs = troc_notifs
+ };
+}
+
+/**********************************************************************
+ * tro functions
+ *********************************************************************/
+
+/**
+ * @brief Call override function for @p node.
+ *
+ * @param[in] lysc_tree if @p node is compiled.
+ * @param[in] node to create override.
+ * @param[in] erase_node_overr if override structure must be reseted.
+ * @param[in,out] plc current plugin context.
+ * @return pointer to override structure or NULL. Override structure in @p plc is updated too.
+ */
+static struct lyplg_ext_sprinter_tree_node_override *
+tro_set_node_overr(ly_bool lysc_tree, const void *node, ly_bool erase_node_overr, struct trt_plugin_ctx *plc)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyplg_ext_sprinter_tree_node_override *no;
+ struct lyspr_tree_ctx *plug_ctx;
+ struct lysc_ext_instance *ce;
+ struct lysp_ext_instance *pe;
+
+ if (erase_node_overr) {
+ trp_ext_free_node_override(&plc->node_overr, &plc->filtered);
+ }
+ no = &plc->node_overr;
+ if (!plc->ctx && lysc_tree && (ce = trp_ext_is_present(lysc_tree, node))) {
+ rc = ce->def->plugin->printer_ctree(ce, NULL, &no->flags, &no->add_opts);
+ } else if (!plc->ctx && (pe = trp_ext_is_present(lysc_tree, node))) {
+ rc = pe->record->plugin.printer_ptree(pe, NULL, &no->flags, &no->add_opts);
+ } else if (plc->ctx) {
+ if (plc->schema && plc->schema->compiled && plc->schema->cn_overr) {
+ rc = plc->schema->cn_overr(node, plc->ctx->plugin_priv, &plc->filtered, &no->flags, &no->add_opts);
+ } else if (plc->schema && plc->schema->pn_overr) {
+ rc = plc->schema->pn_overr(node, plc->ctx->plugin_priv, &plc->filtered, &no->flags, &no->add_opts);
+ } else {
+ no = NULL;
+ }
+ if (trp_ext_is_present(lysc_tree, node)) {
+ plug_ctx = plc->ctx;
+ plc->ctx = NULL;
+ tro_set_node_overr(lysc_tree, node, 0, plc);
+ plc->ctx = plug_ctx;
+ }
+ } else {
+ no = NULL;
+ }
+
+ if (rc) {
+ plc->last_error = rc;
+ no = NULL;
+ }
+
+ return no;
+}
+
+/**
+ * @brief Get next sibling of the current node.
+ *
+ * This is a general algorithm that is able to
+ * work with lysp_node or lysc_node.
+ *
+ * @param[in] node points to lysp_node or lysc_node.
+ * @param[in] tc current tree context.
+ * @return next sibling node.
+ */
+static const void *
+tro_next_sibling(const void *node, const struct trt_tree_ctx *tc)
+{
+ struct tro_getters get;
+ struct trt_plugin_ctx plugin_ctx;
+ const void *tmp, *parent, *sibl;
+
+ assert(node);
+
+ get = tc->lysc_tree ? troc_init_getters() : trop_init_getters();
+
+ if (get.nodetype(node) & (LYS_RPC | LYS_ACTION)) {
+ if ((tmp = get.next(node))) {
+ /* next action exists */
+ sibl = tmp;
+ } else if ((parent = get.parent(node))) {
+ /* maybe if notif exists as sibling */
+ sibl = get.notifs(parent);
+ } else {
+ sibl = NULL;
+ }
+ } else if (get.nodetype(node) & LYS_INPUT) {
+ if ((parent = get.parent(node))) {
+ /* if output action has data */
+ if (get.child(get.action_output(parent))) {
+ /* then next sibling is output action */
+ sibl = get.action_output(parent);
+ } else {
+ /* input action cannot have siblings other
+ * than output action.
+ */
+ sibl = NULL;
+ }
+ } else {
+ /* there is no way how to get output action */
+ sibl = NULL;
+ }
+ } else if (get.nodetype(node) & LYS_OUTPUT) {
+ /* output action cannot have siblings */
+ sibl = NULL;
+ } else if (get.nodetype(node) & LYS_NOTIF) {
+ /* must have as a sibling only notif */
+ sibl = get.next(node);
+ } else {
+ /* for rest of nodes */
+ if ((tmp = get.next(node))) {
+ /* some sibling exists */
+ sibl = tmp;
+ } else if ((parent = get.parent(node))) {
+ /* Action and notif are siblings too.
+ * They can be reached through parent.
+ */
+ if ((tmp = get.actions(parent))) {
+ /* next sibling is action */
+ sibl = tmp;
+ } else if ((tmp = get.notifs(parent))) {
+ /* next sibling is notif */
+ sibl = tmp;
+ } else {
+ /* sibling not exists */
+ sibl = NULL;
+ }
+ } else {
+ /* sibling not exists */
+ sibl = NULL;
+ }
+ }
+
+ 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);
+ }
+
+ return sibl;
+}
+
+/**
+ * @brief Get child of the current node.
+ *
+ * This is a general algorithm that is able to
+ * work with lysp_node or lysc_node.
+ *
+ * @param[in] node points to lysp_node or lysc_node.
+ * @param[in] tc current tree context.
+ * @return child node.
+ */
+static const void *
+tro_next_child(const void *node, const struct trt_tree_ctx *tc)
+{
+ struct tro_getters get;
+ struct trt_plugin_ctx plugin_ctx;
+ const void *tmp, *child;
+
+ assert(node);
+
+ get = tc->lysc_tree ? troc_init_getters() : trop_init_getters();
+
+ if (get.nodetype(node) & (LYS_ACTION | LYS_RPC)) {
+ if (get.child(get.action_input(node))) {
+ /* go to LYS_INPUT */
+ child = get.action_input(node);
+ } else if (get.child(get.action_output(node))) {
+ /* go to LYS_OUTPUT */
+ child = get.action_output(node);
+ } else {
+ /* input action and output action have no data */
+ child = NULL;
+ }
+ } else {
+ if ((tmp = get.child(node))) {
+ child = tmp;
+ } else {
+ /* current node can't have children or has no children */
+ /* but maybe has some actions or notifs */
+ if ((tmp = get.actions(node))) {
+ child = tmp;
+ } else if ((tmp = get.notifs(node))) {
+ child = tmp;
+ } else {
+ child = NULL;
+ }
+ }
+ }
+
+ 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);
+ }
+
+ return child;
+}
+
+/**
+ * @brief Get new trt_parent_cache if we apply the transfer
+ * to the child node in the tree.
+ * @param[in] ca is parent cache for current node.
+ * @param[in] tc contains current tree node.
+ * @return Cache for the current node.
+ */
+static struct trt_parent_cache
+tro_parent_cache_for_child(struct trt_parent_cache ca, const struct trt_tree_ctx *tc)
+{
+ struct trt_parent_cache ret = TRP_EMPTY_PARENT_CACHE;
+
+ if (!tc->lysc_tree) {
+ const struct lysp_node *pn = tc->pn;
+
+ ret.ancestor =
+ pn->nodetype & (LYS_INPUT) ? TRD_ANCESTOR_RPC_INPUT :
+ pn->nodetype & (LYS_OUTPUT) ? TRD_ANCESTOR_RPC_OUTPUT :
+ pn->nodetype & (LYS_NOTIF) ? TRD_ANCESTOR_NOTIF :
+ ca.ancestor;
+
+ ret.lys_status =
+ pn->flags & (LYS_STATUS_CURR | LYS_STATUS_DEPRC | LYS_STATUS_OBSLT) ? pn->flags :
+ ca.lys_status;
+
+ ret.lys_config =
+ ca.ancestor == TRD_ANCESTOR_RPC_INPUT ? 0 : /* because <flags> will be -w */
+ ca.ancestor == TRD_ANCESTOR_RPC_OUTPUT ? LYS_CONFIG_R :
+ pn->flags & (LYS_CONFIG_R | LYS_CONFIG_W) ? pn->flags :
+ ca.lys_config;
+
+ ret.last_list =
+ pn->nodetype & (LYS_LIST) ? (struct lysp_node_list *)pn :
+ ca.last_list;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Transformation of the Schema nodes flags to
+ * Tree diagram \<status\>.
+ * @param[in] flags is node's flags obtained from the tree.
+ */
+static char *
+tro_flags2status(uint16_t flags)
+{
+ return flags & LYS_STATUS_OBSLT ? "o" :
+ flags & LYS_STATUS_DEPRC ? "x" :
+ "+";
+}
+
+/**
+ * @brief Transformation of the Schema nodes flags to Tree diagram
+ * \<flags\> but more specifically 'ro' or 'rw'.
+ * @param[in] flags is node's flags obtained from the tree.
+ */
+static char *
+tro_flags2config(uint16_t flags)
+{
+ return flags & LYS_CONFIG_R ? TRD_FLAGS_TYPE_RO :
+ flags & LYS_CONFIG_W ? TRD_FLAGS_TYPE_RW :
+ TRD_FLAGS_TYPE_EMPTY;
+}
+
+/**
+ * @brief Print current node's iffeatures.
+ * @param[in] tc is tree context.
+ * @param[in,out] out is output handler.
+ */
+static void
+tro_print_features_names(const struct trt_tree_ctx *tc, struct ly_out *out)
+{
+ const struct lysp_qname *iffs;
+
+ if (tc->lysc_tree) {
+ assert(TRP_TREE_CTX_LYSP_NODE_PRESENT(tc->cn));
+ iffs = TRP_TREE_CTX_GET_LYSP_NODE(tc->cn)->iffeatures;
+ } else {
+ iffs = tc->pn->iffeatures;
+ }
+ LY_ARRAY_COUNT_TYPE i;
+
+ LY_ARRAY_FOR(iffs, i) {
+ if (i == 0) {
+ ly_print_(out, "%s", iffs[i].str);
+ } else {
+ ly_print_(out, ",%s", iffs[i].str);
+ }
+ }
+
+}
+
+/**
+ * @brief Print current list's keys.
+ *
+ * Well, actually printing keys in the lysp_tree is trivial,
+ * because char* points to all keys. However, special functions have
+ * been reserved for this, because in principle the list of elements
+ * can have more implementations.
+ *
+ * @param[in] tc is tree context.
+ * @param[in,out] out is output handler.
+ */
+static void
+tro_print_keys(const struct trt_tree_ctx *tc, struct ly_out *out)
+{
+ const struct lysp_node_list *list;
+
+ if (tc->lysc_tree) {
+ assert(TRP_TREE_CTX_LYSP_NODE_PRESENT(tc->cn));
+ list = (const struct lysp_node_list *)TRP_TREE_CTX_GET_LYSP_NODE(tc->cn);
+ } else {
+ list = (const struct lysp_node_list *)tc->pn;
+ }
+ assert(list->nodetype & LYS_LIST);
+
+ if (trg_charptr_has_data(list->key)) {
+ ly_print_(out, "%s", list->key);
+ }
+}
+
+/**
+ * @brief Get address of the current node.
+ * @param[in] tc contains current node.
+ * @return Address of lysc_node or lysp_node, or NULL.
+ */
+static const void *
+tro_tree_ctx_get_node(const struct trt_tree_ctx *tc)
+{
+ return tc->lysc_tree ?
+ (const void *)tc->cn :
+ (const void *)tc->pn;
+}
+
+/**
+ * @brief Get address of current node's child.
+ * @param[in,out] tc contains current node.
+ */
+static const void *
+tro_tree_ctx_get_child(const struct trt_tree_ctx *tc)
+{
+ if (!tro_tree_ctx_get_node(tc)) {
+ return NULL;
+ }
+
+ if (tc->lysc_tree) {
+ return lysc_node_child(tc->cn);
+ } else {
+ return lysp_node_child(tc->pn);
+ }
+}
+
+/**
+ * @brief Get rpcs section if exists.
+ * @param[in,out] tc is tree context.
+ * @return Section representation if it exists. The @p tc is modified
+ * and his pointer points to the first node in rpcs section.
+ * @return Empty section representation otherwise.
+ */
+static struct trt_keyword_stmt
+tro_modi_get_rpcs(struct trt_tree_ctx *tc)
+{
+ assert(tc);
+ const void *actions;
+ struct trt_keyword_stmt ret = {0};
+
+ if (tc->lysc_tree) {
+ actions = tc->cmod->rpcs;
+ if (actions) {
+ tc->cn = actions;
+ }
+ } else {
+ actions = tc->pmod->rpcs;
+ if (actions) {
+ tc->pn = actions;
+ tc->tpn = tc->pn;
+ }
+ }
+
+ if (actions) {
+ tc->section = TRD_SECT_RPCS;
+ ret.section_name = TRD_KEYWORD_RPC;
+ ret.has_node = tro_tree_ctx_get_node(tc) ? 1 : 0;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Get notification section if exists
+ * @param[in,out] tc is tree context.
+ * @return Section representation if it exists.
+ * The @p tc is modified and his pointer points to the
+ * first node in notification section.
+ * @return Empty section representation otherwise.
+ */
+static struct trt_keyword_stmt
+tro_modi_get_notifications(struct trt_tree_ctx *tc)
+{
+ assert(tc);
+ const void *notifs;
+ struct trt_keyword_stmt ret = {0};
+
+ if (tc->lysc_tree) {
+ notifs = tc->cmod->notifs;
+ if (notifs) {
+ tc->cn = notifs;
+ }
+ } else {
+ notifs = tc->pmod->notifs;
+ if (notifs) {
+ tc->pn = notifs;
+ tc->tpn = tc->pn;
+ }
+ }
+
+ if (notifs) {
+ tc->section = TRD_SECT_NOTIF;
+ ret.section_name = TRD_KEYWORD_NOTIF;
+ ret.has_node = tro_tree_ctx_get_node(tc) ? 1 : 0;
+ }
+
+ return ret;
+}
+
+static struct trt_keyword_stmt
+tro_get_ext_section(struct trt_tree_ctx *tc, void *ext, struct lyspr_tree_ctx *plug_ctx)
+{
+ struct trt_keyword_stmt ret = {0};
+ struct lysc_ext_instance *ce = NULL;
+ struct lysp_ext_instance *pe = NULL;
+
+ if (tc->lysc_tree) {
+ ce = ext;
+ ret.section_name = ce->def->name;
+ ret.argument = ce->argument;
+ ret.has_node = plug_ctx->schemas->ctree ? 1 : 0;
+ } else {
+ pe = ext;
+ ret.section_name = pe->def->name;
+ ret.argument = pe->argument;
+ ret.has_node = plug_ctx->schemas->ptree ? 1 : 0;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Get name of the module.
+ * @param[in] tc is context of the tree.
+ */
+static struct trt_keyword_stmt
+tro_read_module_name(const struct trt_tree_ctx *tc)
+{
+ assert(tc);
+ struct trt_keyword_stmt ret;
+
+ ret.section_name = !tc->lysc_tree && tc->pmod->is_submod ?
+ TRD_KEYWORD_SUBMODULE :
+ TRD_KEYWORD_MODULE;
+
+ ret.argument = !tc->lysc_tree ?
+ LYSP_MODULE_NAME(tc->pmod) :
+ tc->cmod->mod->name;
+
+ ret.has_node = tro_tree_ctx_get_node(tc) ? 1 : 0;
+
+ return ret;
+}
+
+static ly_bool
+tro_read_if_sibling_exists(const struct trt_tree_ctx *tc)
+{
+ const void *parent;
+
+ if (tc->lysc_tree) {
+ parent = troc_parent(tc->cn);
+ } else {
+ parent = trop_parent(tc->pn);
+ }
+
+ return parent ? 1 : 0;
+}
+
+/**
+ * @brief Create implicit "case" node as parent of @p node.
+ * @param[in] node child of implicit case node.
+ * @param[out] case_node created case node.
+ */
+static void
+tro_create_implicit_case_node(const struct trt_node *node, struct trt_node *case_node)
+{
+ case_node->status = node->status;
+ case_node->flags = TRD_FLAGS_TYPE_EMPTY;
+ case_node->name.type = TRD_NODE_CASE;
+ case_node->name.keys = node->name.keys;
+ case_node->name.module_prefix = node->name.module_prefix;
+ case_node->name.str = node->name.str;
+ case_node->name.opts = node->name.opts;
+ case_node->name.add_opts = node->name.add_opts;
+ case_node->type = TRP_EMPTY_TRT_TYPE;
+ case_node->iffeatures = TRP_EMPTY_TRT_IFFEATURES;
+ case_node->last_one = node->last_one;
+}
+
+/**********************************************************************
+ * Definition of trop reading functions
+ *********************************************************************/
+
+/**
+ * @brief Check if list statement has keys.
+ * @param[in] pn is pointer to the list.
+ * @return 1 if has keys, otherwise 0.
+ */
+static ly_bool
+trop_list_has_keys(const struct lysp_node *pn)
+{
+ return trg_charptr_has_data(((const struct lysp_node_list *)pn)->key);
+}
+
+/**
+ * @brief Check if it contains at least one feature.
+ * @param[in] pn is current node.
+ * @return 1 if has if-features, otherwise 0.
+ */
+static ly_bool
+trop_node_has_iffeature(const struct lysp_node *pn)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lysp_qname *iffs;
+
+ ly_bool ret = 0;
+
+ iffs = pn->iffeatures;
+ LY_ARRAY_FOR(iffs, u) {
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+/**
+ * @brief Find out if leaf is also the key in last list.
+ * @param[in] pn is pointer to leaf.
+ * @param[in] ca_last_list is pointer to last visited list.
+ * Obtained from trt_parent_cache.
+ * @return 1 if leaf is also the key, otherwise 0.
+ */
+static ly_bool
+trop_leaf_is_key(const struct lysp_node *pn, const struct lysp_node_list *ca_last_list)
+{
+ const struct lysp_node_leaf *leaf = (const struct lysp_node_leaf *)pn;
+ const struct lysp_node_list *list = ca_last_list;
+
+ if (!list) {
+ return 0;
+ }
+ return trg_charptr_has_data(list->key) ?
+ trg_word_is_present(list->key, leaf->name, ' ') : 0;
+}
+
+/**
+ * @brief Check if container's type is presence.
+ * @param[in] pn is pointer to container.
+ * @return 1 if container has presence statement, otherwise 0.
+ */
+static ly_bool
+trop_container_has_presence(const struct lysp_node *pn)
+{
+ return trg_charptr_has_data(((struct lysp_node_container *)pn)->presence);
+}
+
+/**
+ * @brief Get leaflist's path without lysp_node type control.
+ * @param[in] pn is pointer to the leaflist.
+ */
+static const char *
+trop_leaflist_refpath(const struct lysp_node *pn)
+{
+ const struct lysp_node_leaflist *list = (const struct lysp_node_leaflist *)pn;
+
+ return list->type.path ? list->type.path->expr : NULL;
+}
+
+/**
+ * @brief Get leaflist's type name without lysp_node type control.
+ * @param[in] pn is pointer to the leaflist.
+ */
+static const char *
+trop_leaflist_type_name(const struct lysp_node *pn)
+{
+ const struct lysp_node_leaflist *list = (const struct lysp_node_leaflist *)pn;
+
+ return list->type.name;
+}
+
+/**
+ * @brief Get leaf's path without lysp_node type control.
+ * @param[in] pn is pointer to the leaf node.
+ */
+static const char *
+trop_leaf_refpath(const struct lysp_node *pn)
+{
+ const struct lysp_node_leaf *leaf = (const struct lysp_node_leaf *)pn;
+
+ return leaf->type.path ? leaf->type.path->expr : NULL;
+}
+
+/**
+ * @brief Get leaf's type name without lysp_node type control.
+ * @param[in] pn is pointer to the leaf's type name.
+ */
+static const char *
+trop_leaf_type_name(const struct lysp_node *pn)
+{
+ const struct lysp_node_leaf *leaf = (const struct lysp_node_leaf *)pn;
+
+ return leaf->type.name;
+}
+
+/**
+ * @brief Get pointer to data using node type specification
+ * and getter function.
+ *
+ * @param[in] flags is node type specification.
+ * If it is the correct node, the getter function is called.
+ * @param[in] f is getter function which provides the desired
+ * char pointer from the structure.
+ * @param[in] pn pointer to node.
+ * @return NULL if node has wrong type or getter function return
+ * pointer to NULL.
+ * @return Pointer to desired char pointer obtained from the node.
+ */
+static const char *
+trop_node_charptr(uint16_t flags, trt_get_charptr_func f, const struct lysp_node *pn)
+{
+ if (pn->nodetype & flags) {
+ const char *ret = f(pn);
+
+ return trg_charptr_has_data(ret) ? ret : NULL;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * @brief Resolve \<status\> of the current node.
+ * @param[in] nodetype is node's type obtained from the tree.
+ * @param[in] flags is node's flags obtained from the tree.
+ * @param[in] ca_lys_status is inherited status obtained from trt_parent_cache.
+ * @return The status type.
+ */
+static char *
+trop_resolve_status(uint16_t nodetype, uint16_t flags, uint16_t ca_lys_status)
+{
+ if (nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ /* LYS_INPUT and LYS_OUTPUT is special case */
+ return tro_flags2status(ca_lys_status);
+ /* if ancestor's status is deprc or obslt
+ * and also node's status is not set
+ */
+ } else if ((ca_lys_status & (LYS_STATUS_DEPRC | LYS_STATUS_OBSLT)) && !(flags & (LYS_STATUS_CURR | LYS_STATUS_DEPRC | LYS_STATUS_OBSLT))) {
+ /* get ancestor's status */
+ return tro_flags2status(ca_lys_status);
+ } else {
+ /* else get node's status */
+ return tro_flags2status(flags);
+ }
+}
+
+/**
+ * @brief Resolve \<flags\> of the current node.
+ * @param[in] nodetype is node's type obtained from the tree.
+ * @param[in] flags is node's flags obtained from the tree.
+ * @param[in] ca_ancestor is ancestor type obtained from trt_parent_cache.
+ * @param[in] ca_lys_config is inherited config item obtained from trt_parent_cache.
+ * @param[in] no Override structure for flags.
+ * @return The flags type.
+ */
+static const char *
+trop_resolve_flags(uint16_t nodetype, uint16_t flags, trt_ancestor_type ca_ancestor, uint16_t ca_lys_config,
+ struct lyplg_ext_sprinter_tree_node_override *no)
+{
+ if (no && no->flags) {
+ return no->flags;
+ } else if ((nodetype & LYS_INPUT) || (ca_ancestor == TRD_ANCESTOR_RPC_INPUT)) {
+ return TRD_FLAGS_TYPE_RPC_INPUT_PARAMS;
+ } else if ((nodetype & LYS_OUTPUT) || (ca_ancestor == TRD_ANCESTOR_RPC_OUTPUT)) {
+ return TRD_FLAGS_TYPE_RO;
+ } else if (ca_ancestor == TRD_ANCESTOR_NOTIF) {
+ return TRD_FLAGS_TYPE_RO;
+ } else if (nodetype & LYS_NOTIF) {
+ return TRD_FLAGS_TYPE_NOTIF;
+ } else if (nodetype & LYS_USES) {
+ return TRD_FLAGS_TYPE_USES_OF_GROUPING;
+ } else if (nodetype & (LYS_RPC | LYS_ACTION)) {
+ return TRD_FLAGS_TYPE_RPC;
+ } else if (!(flags & (LYS_CONFIG_R | LYS_CONFIG_W))) {
+ /* config is not set. Look at ancestor's config */
+ return tro_flags2config(ca_lys_config);
+ } else {
+ return tro_flags2config(flags);
+ }
+}
+
+/**
+ * @brief Resolve node type of the current node.
+ * @param[in] pn is pointer to the current node in the tree.
+ * @param[in] ca_last_list is pointer to the last visited list. Obtained from the trt_parent_cache.
+ * @param[out] type Resolved type of node.
+ * @param[out] opts Resolved opts of node.
+ */
+static void
+trop_resolve_node_opts(const struct lysp_node *pn, const struct lysp_node_list *ca_last_list, trt_node_type *type,
+ const char **opts)
+{
+ if (pn->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ *type = TRD_NODE_ELSE;
+ } else if (pn->nodetype & LYS_CASE) {
+ *type = TRD_NODE_CASE;
+ } else if ((pn->nodetype & LYS_CHOICE) && !(pn->flags & LYS_MAND_TRUE)) {
+ *type = TRD_NODE_CHOICE;
+ *opts = TRD_NODE_OPTIONAL;
+ } else if (pn->nodetype & LYS_CHOICE) {
+ *type = TRD_NODE_CHOICE;
+ } else if ((pn->nodetype & LYS_CONTAINER) && (trop_container_has_presence(pn))) {
+ *opts = TRD_NODE_CONTAINER;
+ } else if (pn->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ *opts = TRD_NODE_LISTLEAFLIST;
+ } else if ((pn->nodetype & (LYS_ANYDATA | LYS_ANYXML)) && !(pn->flags & LYS_MAND_TRUE)) {
+ *opts = TRD_NODE_OPTIONAL;
+ } else if ((pn->nodetype & LYS_LEAF) && !(pn->flags & LYS_MAND_TRUE) && (!trop_leaf_is_key(pn, ca_last_list))) {
+ *opts = TRD_NODE_OPTIONAL;
+ } else {
+ *type = TRD_NODE_ELSE;
+ }
+}
+
+/**
+ * @brief Resolve \<type\> of the current node.
+ * @param[in] pn is current node.
+ * @return Resolved type.
+ */
+static struct trt_type
+trop_resolve_type(const struct lysp_node *pn)
+{
+ const char *tmp = NULL;
+
+ if (!pn) {
+ return TRP_EMPTY_TRT_TYPE;
+ } else if ((tmp = trop_node_charptr(LYS_LEAFLIST, trop_leaflist_refpath, pn))) {
+ return TRP_INIT_TRT_TYPE(TRD_TYPE_TARGET, tmp);
+ } else if ((tmp = trop_node_charptr(LYS_LEAFLIST, trop_leaflist_type_name, pn))) {
+ return TRP_INIT_TRT_TYPE(TRD_TYPE_NAME, tmp);
+ } else if ((tmp = trop_node_charptr(LYS_LEAF, trop_leaf_refpath, pn))) {
+ return TRP_INIT_TRT_TYPE(TRD_TYPE_TARGET, tmp);
+ } else if ((tmp = trop_node_charptr(LYS_LEAF, trop_leaf_type_name, pn))) {
+ return TRP_INIT_TRT_TYPE(TRD_TYPE_NAME, tmp);
+ } else if (pn->nodetype == LYS_ANYDATA) {
+ return TRP_INIT_TRT_TYPE(TRD_TYPE_NAME, "anydata");
+ } else if (pn->nodetype & LYS_ANYXML) {
+ return TRP_INIT_TRT_TYPE(TRD_TYPE_NAME, "anyxml");
+ } else {
+ return TRP_EMPTY_TRT_TYPE;
+ }
+}
+
+/**
+ * @brief Resolve iffeatures.
+ *
+ * @param[in] pn is current parsed node.
+ * @return Resolved iffeatures.
+ */
+static struct trt_iffeatures
+trop_resolve_iffeatures(const struct lysp_node *pn)
+{
+ struct trt_iffeatures iff;
+
+ if (pn && trop_node_has_iffeature(pn)) {
+ iff.type = TRD_IFF_PRESENT;
+ iff.str = NULL;
+ } else {
+ iff.type = TRD_IFF_NON_PRESENT;
+ iff.str = NULL;
+ }
+
+ return iff;
+}
+
+/**
+ * @brief Transformation of current lysp_node to struct trt_node.
+ * @param[in] ca contains stored important data
+ * when browsing the tree downwards.
+ * @param[in] tc is context of the tree.
+ */
+static struct trt_node
+trop_read_node(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ const struct lysp_node *pn;
+ struct trt_node ret;
+ struct lyplg_ext_sprinter_tree_node_override *no;
+
+ assert(tc && tc->pn && tc->pn->nodetype != LYS_UNKNOWN);
+
+ no = tro_set_node_overr(tc->lysc_tree, tc->pn, 1, &tc->plugin_ctx);
+
+ pn = tc->pn;
+ ret = TRP_EMPTY_NODE;
+
+ /* <status> */
+ ret.status = trop_resolve_status(pn->nodetype, pn->flags, ca.lys_status);
+
+ /* <flags> */
+ ret.flags = trop_resolve_flags(pn->nodetype, pn->flags, ca.ancestor, ca.lys_config, no);
+
+ /* set type of the node */
+ trop_resolve_node_opts(pn, ca.last_list, &ret.name.type, &ret.name.opts);
+ ret.name.add_opts = no && no->add_opts ? no->add_opts : NULL;
+ ret.name.keys = (tc->pn->nodetype & LYS_LIST) && trop_list_has_keys(tc->pn);
+
+ /* The parsed tree is not compiled, so no node can be augmented
+ * from another module. This means that nodes from the parsed tree
+ * will never have the prefix.
+ */
+ ret.name.module_prefix = NULL;
+
+ /* set node's name */
+ ret.name.str = pn->name;
+
+ /* <type> */
+ ret.type = trop_resolve_type(pn);
+
+ /* <iffeature> */
+ ret.iffeatures = trop_resolve_iffeatures(pn);
+
+ ret.last_one = !tro_next_sibling(pn, tc);
+
+ return ret;
+}
+
+/**
+ * @brief Find out if the current node has siblings.
+ * @param[in] tc is context of the tree.
+ * @return 1 if sibling exists otherwise 0.
+ */
+static ly_bool
+trop_read_if_sibling_exists(const struct trt_tree_ctx *tc)
+{
+ return tro_next_sibling(tc->pn, tc) != NULL;
+}
+
+/**********************************************************************
+ * Modify trop getters
+ *********************************************************************/
+
+/**
+ * @brief Change current node pointer to its parent
+ * but only if parent exists.
+ * @param[in,out] tc is tree context.
+ * Contains pointer to the current node.
+ * @return 1 if the node had parents and the change was successful.
+ * @return 0 if the node did not have parents.
+ * The pointer to the current node did not change.
+ */
+static ly_bool
+trop_modi_parent(struct trt_tree_ctx *tc)
+{
+ assert(tc && tc->pn);
+ /* If no parent exists, stay in actual node. */
+ if ((tc->pn != tc->tpn) && (tc->pn->parent)) {
+ tc->pn = tc->pn->parent;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @brief Change the current node pointer to its child
+ * but only if exists.
+ * @param[in] ca contains inherited data from ancestors.
+ * @param[in,out] tc is context of the tree.
+ * Contains pointer to the current node.
+ * @return Non-empty \<node\> representation of the current
+ * node's child. The @p tc is modified.
+ * @return Empty \<node\> representation if child don't exists.
+ * The @p tc is not modified.
+ */
+static struct trt_node
+trop_modi_next_child(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ const struct lysp_node *tmp;
+
+ assert(tc && tc->pn);
+
+ if ((tmp = tro_next_child(tc->pn, tc))) {
+ tc->pn = tmp;
+ return trop_read_node(ca, tc);
+ } else {
+ return TRP_EMPTY_NODE;
+ }
+}
+
+/**
+ * @brief Change the pointer to the current node to its next sibling
+ * only if exists.
+ * @param[in] ca contains inherited data from ancestors.
+ * @param[in,out] tc is tree context.
+ * Contains pointer to the current node.
+ * @return Non-empty \<node\> representation if sibling exists.
+ * The @p tc is modified.
+ * @return Empty \<node\> representation otherwise.
+ * The @p tc is not modified.
+ */
+static struct trt_node
+trop_modi_next_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ const struct lysp_node *pn;
+
+ assert(tc && tc->pn);
+
+ pn = tro_next_sibling(tc->pn, tc);
+
+ if (pn) {
+ if ((tc->tpn == tc->pn) && (tc->section != TRD_SECT_PLUG_DATA)) {
+ tc->tpn = pn;
+ }
+ tc->pn = pn;
+ return trop_read_node(ca, tc);
+ } else {
+ return TRP_EMPTY_NODE;
+ }
+}
+
+/**
+ * @brief Change the current node pointer to the first child of node's
+ * parent. If current node is already first sibling/child then nothing
+ * will change.
+ * @param[in] ca Settings of parent.
+ * @param[in,out] tc is tree context.
+ * @return node for printing.
+ */
+static struct trt_node
+trop_modi_first_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ struct trt_node node;
+
+ assert(tc && tc->pn);
+
+ if (trop_modi_parent(tc)) {
+ node = trop_modi_next_child(ca, tc);
+ } else if (tc->plugin_ctx.schema) {
+ tc->pn = tc->plugin_ctx.schema->ptree;
+ tc->tpn = tc->pn;
+ node = trop_read_node(ca, tc);
+ } else {
+ /* current node is top-node */
+ switch (tc->section) {
+ case TRD_SECT_MODULE:
+ tc->pn = tc->pmod->data;
+ tc->tpn = tc->pn;
+ break;
+ case TRD_SECT_AUGMENT:
+ tc->pn = (const struct lysp_node *)tc->pmod->augments;
+ tc->tpn = tc->pn;
+ break;
+ case TRD_SECT_RPCS:
+ tc->pn = (const struct lysp_node *)tc->pmod->rpcs;
+ tc->tpn = tc->pn;
+ break;
+ case TRD_SECT_NOTIF:
+ tc->pn = (const struct lysp_node *)tc->pmod->notifs;
+ tc->tpn = tc->pn;
+ break;
+ case TRD_SECT_GROUPING:
+ tc->pn = (const struct lysp_node *)tc->pmod->groupings;
+ tc->tpn = tc->pn;
+ break;
+ case TRD_SECT_PLUG_DATA:
+ /* Nothing to do. */
+ break;
+ default:
+ assert(0);
+ }
+ node = trop_read_node(ca, tc);
+ }
+
+ if (tc->plugin_ctx.filtered) {
+ node = trop_modi_next_sibling(ca, tc);
+ }
+
+ return node;
+}
+
+/**
+ * @brief Get next (or first) augment section if exists.
+ * @param[in,out] tc is tree context. It is modified and his current
+ * node is set to the lysp_node_augment.
+ * @return Section's representation if (next augment) section exists.
+ * @return Empty section structure otherwise.
+ */
+static struct trt_keyword_stmt
+trop_modi_next_augment(struct trt_tree_ctx *tc)
+{
+ assert(tc);
+ const struct lysp_node_augment *augs;
+ struct trt_keyword_stmt ret = {0};
+
+ /* if next_augment func was called for the first time */
+ if (tc->section != TRD_SECT_AUGMENT) {
+ tc->section = TRD_SECT_AUGMENT;
+ augs = tc->pmod->augments;
+ } else {
+ /* get augment sibling from top-node pointer */
+ augs = (const struct lysp_node_augment *)tc->tpn->next;
+ }
+
+ if (augs) {
+ tc->pn = &augs->node;
+ tc->tpn = tc->pn;
+ ret.section_name = TRD_KEYWORD_AUGMENT;
+ ret.argument = augs->nodeid;
+ ret.has_node = tro_tree_ctx_get_node(tc) ? 1 : 0;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Get next (or first) grouping section if exists
+ * @param[in,out] tc is tree context. It is modified and his current
+ * node is set to the lysp_node_grp.
+ * @return The next (or first) section representation if it exists.
+ * @return Empty section representation otherwise.
+ */
+static struct trt_keyword_stmt
+trop_modi_next_grouping(struct trt_tree_ctx *tc)
+{
+ assert(tc);
+ const struct lysp_node_grp *grps;
+ struct trt_keyword_stmt ret = {0};
+
+ if (tc->section != TRD_SECT_GROUPING) {
+ tc->section = TRD_SECT_GROUPING;
+ grps = tc->pmod->groupings;
+ } else {
+ grps = (const struct lysp_node_grp *)tc->tpn->next;
+ }
+
+ if (grps) {
+ tc->pn = &grps->node;
+ tc->tpn = tc->pn;
+ ret.section_name = TRD_KEYWORD_GROUPING;
+ ret.argument = grps->name;
+ ret.has_node = tro_tree_ctx_get_child(tc) ? 1 : 0;
+ }
+
+ return ret;
+}
+
+/**********************************************************************
+ * Definition of troc reading functions
+ *********************************************************************/
+
+/**
+ * @copydoc trop_read_if_sibling_exists
+ */
+static ly_bool
+troc_read_if_sibling_exists(const struct trt_tree_ctx *tc)
+{
+ return tro_next_sibling(tc->cn, tc) != NULL;
+}
+
+/**
+ * @brief Resolve \<flags\> of the current node.
+ *
+ * Use this function only if trt_tree_ctx.lysc_tree is true.
+ *
+ * @param[in] nodetype is current lysc_node.nodetype.
+ * @param[in] flags is current lysc_node.flags.
+ * @param[in] no Override structure for flags.
+ * @return The flags type.
+ */
+static const char *
+troc_resolve_flags(uint16_t nodetype, uint16_t flags, struct lyplg_ext_sprinter_tree_node_override *no)
+{
+ if (no && no->flags) {
+ return no->flags;
+ } else if ((nodetype & LYS_INPUT) || (flags & LYS_IS_INPUT)) {
+ return TRD_FLAGS_TYPE_RPC_INPUT_PARAMS;
+ } else if ((nodetype & LYS_OUTPUT) || (flags & LYS_IS_OUTPUT)) {
+ return TRD_FLAGS_TYPE_RO;
+ } else if (nodetype & LYS_IS_NOTIF) {
+ return TRD_FLAGS_TYPE_RO;
+ } else if (nodetype & LYS_NOTIF) {
+ return TRD_FLAGS_TYPE_NOTIF;
+ } else if (nodetype & LYS_USES) {
+ return TRD_FLAGS_TYPE_USES_OF_GROUPING;
+ } else if (nodetype & (LYS_RPC | LYS_ACTION)) {
+ return TRD_FLAGS_TYPE_RPC;
+ } else {
+ return tro_flags2config(flags);
+ }
+}
+
+/**
+ * @brief Resolve node type of the current node.
+ *
+ * Use this function only if trt_tree_ctx.lysc_tree is true.
+ *
+ * @param[in] nodetype is current lysc_node.nodetype.
+ * @param[in] flags is current lysc_node.flags.
+ * @param[out] type Resolved type of node.
+ * @param[out] opts Resolved opts.
+ */
+static void
+troc_resolve_node_opts(uint16_t nodetype, uint16_t flags, trt_node_type *type, const char **opts)
+{
+ if (nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ *type = TRD_NODE_ELSE;
+ } else if (nodetype & LYS_CASE) {
+ *type = TRD_NODE_CASE;
+ } else if ((nodetype & LYS_CHOICE) && !(flags & LYS_MAND_TRUE)) {
+ *type = TRD_NODE_CHOICE;
+ *opts = TRD_NODE_OPTIONAL;
+ } else if (nodetype & LYS_CHOICE) {
+ *type = TRD_NODE_CHOICE;
+ } else if ((nodetype & LYS_CONTAINER) && (flags & LYS_PRESENCE)) {
+ *opts = TRD_NODE_CONTAINER;
+ } else if (nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ *opts = TRD_NODE_LISTLEAFLIST;
+ } else if ((nodetype & (LYS_ANYDATA | LYS_ANYXML)) && !(flags & LYS_MAND_TRUE)) {
+ *opts = TRD_NODE_OPTIONAL;
+ } else if ((nodetype & LYS_LEAF) && !(flags & (LYS_MAND_TRUE | LYS_KEY))) {
+ *opts = TRD_NODE_OPTIONAL;
+ } else {
+ *type = TRD_NODE_ELSE;
+ }
+}
+
+/**
+ * @brief Resolve prefix (\<prefix\>:\<name\>) of node that has been
+ * placed from another module via an augment statement.
+ *
+ * @param[in] cn is current compiled node.
+ * @param[in] current_compiled_module is module whose nodes are
+ * currently being printed.
+ * @return Prefix of foreign module or NULL.
+ */
+static const char *
+troc_resolve_node_prefix(const struct lysc_node *cn, const struct lysc_module *current_compiled_module)
+{
+ const struct lys_module *node_module;
+ const char *ret = NULL;
+
+ node_module = cn->module;
+ if (!node_module || !current_compiled_module) {
+ return NULL;
+ } else if (node_module->compiled != current_compiled_module) {
+ ret = node_module->prefix;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Transformation of current lysc_node to struct trt_node.
+ * @param[in] ca is not used.
+ * @param[in] tc is context of the tree.
+ */
+static struct trt_node
+troc_read_node(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ (void) ca;
+ const struct lysc_node *cn;
+ struct trt_node ret;
+ struct lyplg_ext_sprinter_tree_node_override *no;
+
+ assert(tc && tc->cn);
+
+ no = tro_set_node_overr(tc->lysc_tree, tc->cn, 1, &tc->plugin_ctx);
+
+ cn = tc->cn;
+ ret = TRP_EMPTY_NODE;
+
+ /* <status> */
+ ret.status = tro_flags2status(cn->flags);
+
+ /* <flags> */
+ ret.flags = troc_resolve_flags(cn->nodetype, cn->flags, no);
+
+ /* set type of the node */
+ troc_resolve_node_opts(cn->nodetype, cn->flags, &ret.name.type, &ret.name.opts);
+ ret.name.add_opts = no && no->add_opts ? no->add_opts : NULL;
+ ret.name.keys = (cn->nodetype & LYS_LIST) && !(cn->flags & LYS_KEYLESS);
+
+ /* <prefix> */
+ ret.name.module_prefix = troc_resolve_node_prefix(cn, tc->cmod);
+
+ /* set node's name */
+ ret.name.str = cn->name;
+
+ /* <type> */
+ ret.type = trop_resolve_type(TRP_TREE_CTX_GET_LYSP_NODE(cn));
+
+ /* <iffeature> */
+ ret.iffeatures = trop_resolve_iffeatures(TRP_TREE_CTX_GET_LYSP_NODE(cn));
+
+ ret.last_one = !tro_next_sibling(cn, tc);
+
+ return ret;
+}
+
+/**********************************************************************
+ * Modify troc getters
+ *********************************************************************/
+
+/**
+ * @copydoc ::trop_modi_parent()
+ */
+static ly_bool
+troc_modi_parent(struct trt_tree_ctx *tc)
+{
+ assert(tc && tc->cn);
+ /* If no parent exists, stay in actual node. */
+ if (tc->cn->parent) {
+ tc->cn = tc->cn->parent;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @copydoc ::trop_modi_next_sibling()
+ */
+static struct trt_node
+troc_modi_next_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ const struct lysc_node *cn;
+
+ assert(tc && tc->cn);
+
+ cn = tro_next_sibling(tc->cn, tc);
+
+ /* if next sibling exists */
+ if (cn) {
+ /* update trt_tree_ctx */
+ tc->cn = cn;
+ return troc_read_node(ca, tc);
+ } else {
+ return TRP_EMPTY_NODE;
+ }
+}
+
+/**
+ * @copydoc trop_modi_next_child()
+ */
+static struct trt_node
+troc_modi_next_child(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ const struct lysc_node *tmp;
+
+ assert(tc && tc->cn);
+
+ if ((tmp = tro_next_child(tc->cn, tc))) {
+ tc->cn = tmp;
+ return troc_read_node(ca, tc);
+ } else {
+ return TRP_EMPTY_NODE;
+ }
+}
+
+/**
+ * @copydoc ::trop_modi_first_sibling()
+ */
+static struct trt_node
+troc_modi_first_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ struct trt_node node;
+
+ assert(tc && tc->cn);
+
+ if (troc_modi_parent(tc)) {
+ node = troc_modi_next_child(ca, tc);
+ } else if (tc->plugin_ctx.schema) {
+ tc->cn = tc->plugin_ctx.schema->ctree;
+ node = troc_read_node(ca, tc);
+ } else {
+ /* current node is top-node */
+ switch (tc->section) {
+ case TRD_SECT_MODULE:
+ tc->cn = tc->cmod->data;
+ break;
+ case TRD_SECT_RPCS:
+ tc->cn = (const struct lysc_node *)tc->cmod->rpcs;
+ break;
+ case TRD_SECT_NOTIF:
+ tc->cn = (const struct lysc_node *)tc->cmod->notifs;
+ break;
+ case TRD_SECT_PLUG_DATA:
+ /* nothing to do */
+ break;
+ default:
+ assert(0);
+ }
+ node = troc_read_node(ca, tc);
+ }
+
+ if (tc->plugin_ctx.filtered) {
+ node = troc_modi_next_sibling(ca, tc);
+ }
+
+ return node;
+}
+
+/**********************************************************************
+ * Definition of tree browsing functions
+ *********************************************************************/
+
+static uint32_t
+trb_gap_to_opts(const struct trt_node *node)
+{
+ uint32_t len = 0;
+
+ if (node->name.keys) {
+ return 0;
+ }
+
+ if (node->flags) {
+ len += strlen(node->flags);
+ /* space between flags and name */
+ len += 1;
+ } else {
+ /* space between -- and name */
+ len += 1;
+ }
+
+ switch (node->name.type) {
+ case TRD_NODE_CASE:
+ /* ':' is already counted. Plus parentheses. */
+ len += 2;
+ break;
+ case TRD_NODE_CHOICE:
+ /* Plus parentheses. */
+ len += 2;
+ break;
+ default:
+ break;
+ }
+
+ if (node->name.module_prefix) {
+ len += strlen(node->name.module_prefix);
+ }
+ if (node->name.str) {
+ len += strlen(node->name.str);
+ }
+ if (node->name.add_opts) {
+ len += strlen(node->name.add_opts);
+ }
+ if (node->name.opts) {
+ len += strlen(node->name.opts);
+ }
+
+ return len;
+}
+
+static uint32_t
+trb_gap_to_type(const struct trt_node *node)
+{
+ uint32_t len, opts_len;
+
+ if (node->name.keys) {
+ return 0;
+ }
+
+ len = trb_gap_to_opts(node);
+ /* Gap between opts and type. */
+ opts_len = 0;
+ opts_len += node->name.add_opts ? strlen(node->name.add_opts) : 0;
+ opts_len += node->name.opts ? strlen(node->name.opts) : 0;
+ if (opts_len >= TRD_INDENT_BEFORE_TYPE) {
+ /* At least one space should be there. */
+ len += 1;
+ } else if (node->name.add_opts || node->name.opts) {
+ len += TRD_INDENT_BEFORE_TYPE - opts_len;
+ } else {
+ len += TRD_INDENT_BEFORE_TYPE;
+ }
+
+ return len;
+}
+
+/**
+ * @brief Calculate the trt_indent_in_node.btw_opts_type indent size
+ * for a particular node.
+ * @param[in] node for which we get btw_opts_type.
+ * @param[in] max_gap_before_type is the maximum value of btw_opts_type
+ * that it can have.
+ * @return Indent between \<opts\> and \<type\> for node.
+ */
+static int16_t
+trb_calc_btw_opts_type(const struct trt_node *node, int16_t max_gap_before_type)
+{
+ uint32_t to_opts_len;
+
+ to_opts_len = trb_gap_to_opts(node);
+ if (to_opts_len == 0) {
+ return 1;
+ } else {
+ return max_gap_before_type - to_opts_len;
+ }
+}
+
+/**
+ * @brief Print node.
+ *
+ * This function is wrapper for ::trp_print_entire_node().
+ * But difference is that take @p max_gap_before_type which will be
+ * used to set the unified alignment.
+ *
+ * @param[in] node to print.
+ * @param[in] max_gap_before_type is number of indent before \<type\>.
+ * @param[in] wr is wrapper for printing indentation before node.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in] tc is tree context.
+ */
+static void
+trb_print_entire_node(const struct trt_node *node, uint32_t max_gap_before_type, struct trt_wrapper wr,
+ struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ struct trt_indent_in_node ind = trp_default_indent_in_node(node);
+
+ if ((max_gap_before_type > 0) && (node->type.type != TRD_TYPE_EMPTY)) {
+ /* print actual node with unified indent */
+ ind.btw_opts_type = trb_calc_btw_opts_type(node, max_gap_before_type);
+ }
+ /* after -> print actual node with default indent */
+ trp_print_entire_node(node, TRP_INIT_PCK_PRINT(tc, pc->fp.print),
+ TRP_INIT_PCK_INDENT(wr, ind), pc->max_line_length, pc->out);
+}
+
+/**
+ * @brief Check if parent of the current node is the last
+ * of his siblings.
+ *
+ * To mantain stability use this function only if the current node is
+ * the first of the siblings.
+ * Side-effect -> current node is set to the first sibling
+ * if node has a parent otherwise no side-effect.
+ *
+ * @param[in] fp contains all @ref TRP_tro callback functions.
+ * @param[in,out] tc is tree context.
+ * @return 1 if parent is last sibling otherwise 0.
+ */
+static ly_bool
+trb_node_is_last_sibling(const struct trt_fp_all *fp, struct trt_tree_ctx *tc)
+{
+ if (fp->read.if_parent_exists(tc)) {
+ return !fp->read.if_sibling_exists(tc);
+ } else {
+ return !fp->read.if_sibling_exists(tc) && tc->plugin_ctx.last_schema;
+ }
+}
+
+/**
+ * @brief For all siblings find maximal space from '--' to \<type\>.
+ *
+ * Side-effect -> Current node is set to the first sibling.
+ *
+ * @param[in] ca contains inherited data from ancestors.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is tree context.
+ * @return max space.
+ */
+static uint32_t
+trb_max_gap_to_type(struct trt_parent_cache ca, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ struct trt_node node;
+ int32_t maxlen, len;
+
+ maxlen = 0;
+ for (node = pc->fp.modify.first_sibling(ca, tc);
+ !trp_node_is_empty(&node);
+ node = pc->fp.modify.next_sibling(ca, tc)) {
+ len = trb_gap_to_type(&node);
+ maxlen = maxlen < len ? len : maxlen;
+ }
+ pc->fp.modify.first_sibling(ca, tc);
+
+ return maxlen;
+}
+
+/**
+ * @brief Find out if it is possible to unify
+ * the alignment before \<type\>.
+ *
+ * The goal is for all node siblings to have the same alignment
+ * for \<type\> as if they were in a column. All siblings who cannot
+ * adapt because they do not fit on the line at all are ignored.
+ * Side-effect -> Current node is set to the first sibling.
+ *
+ * @param[in] ca contains inherited data from ancestors.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is tree context.
+ * @return positive number indicating the maximum number of spaces
+ * before \<type\> if the length of the flags, node name and opts is 0. To calculate
+ * the trt_indent_in_node.btw_opts_type indent size for a particular
+ * node, use the ::trb_calc_btw_opts_type().
+*/
+static uint32_t
+trb_try_unified_indent(struct trt_parent_cache ca, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ return trb_max_gap_to_type(ca, pc, tc);
+}
+
+/**
+ * @brief Check if there is no case statement
+ * under the choice statement.
+ *
+ * It can return true only if the Parsed schema tree
+ * is used for browsing.
+ *
+ * @param[in] tc is tree context.
+ * @return 1 if implicit case statement is present otherwise 0.
+ */
+static ly_bool
+trb_need_implicit_node_case(struct trt_tree_ctx *tc)
+{
+ return !tc->lysc_tree && tc->pn->parent &&
+ (tc->pn->parent->nodetype & LYS_CHOICE) &&
+ (tc->pn->nodetype & (LYS_ANYDATA | LYS_CHOICE | LYS_CONTAINER |
+ LYS_LEAF | LYS_LEAFLIST));
+}
+
+static void trb_print_subtree_nodes(struct trt_node *node, uint32_t max_gap_before_type,
+ struct trt_wrapper wr, struct trt_parent_cache ca, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc);
+
+/**
+ * @brief Print implicit case node and his subtree.
+ *
+ * @param[in] node is child of implicit case.
+ * @param[in] wr is wrapper for printing identation before node.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in] tc is tree context. Its settings should be the same as
+ * before the function call.
+ * @return new indentation wrapper for @p node.
+ */
+static struct trt_wrapper
+trb_print_implicit_node(const struct trt_node *node, struct trt_wrapper wr, struct trt_printer_ctx *pc,
+ struct trt_tree_ctx *tc)
+{
+ struct trt_node case_node;
+ struct trt_wrapper wr_case_child;
+
+ tro_create_implicit_case_node(node, &case_node);
+ ly_print_(pc->out, "\n");
+ trb_print_entire_node(&case_node, 0, wr, pc, tc);
+ ly_print_(pc->out, "\n");
+ wr_case_child = pc->fp.read.if_sibling_exists(tc) ?
+ trp_wrapper_set_mark(wr) : trp_wrapper_set_shift(wr);
+ return wr_case_child;
+}
+
+/**
+ * @brief Calculate the wrapper about how deep in the tree the node is.
+ * @param[in] wr_in A wrapper to use as a starting point
+ * @param[in] node from which to count.
+ * @return wrapper for @p node.
+ */
+static struct trt_wrapper
+trb_count_depth(const struct trt_wrapper *wr_in, const struct lysc_node *node)
+{
+ struct trt_wrapper wr = wr_in ? *wr_in : TRP_INIT_WRAPPER_TOP;
+ const struct lysc_node *parent;
+
+ if (!node) {
+ return wr;
+ }
+
+ for (parent = node->parent; parent; parent = parent->parent) {
+ wr = trp_wrapper_set_shift(wr);
+ }
+
+ return wr;
+}
+
+/**
+ * @brief Print all parent nodes of @p node and the @p node itself.
+ *
+ * Side-effect -> trt_tree_ctx.cn will be set to @p node.
+ *
+ * @param[in] node on which the function is focused.
+ * @param[in] wr_in for printing identation before node.
+ * @param[in] pc is @ref TRP_trp settings.
+ * @param[in,out] tc is context of tree printer.
+ */
+static void
+trb_print_parents(const struct lysc_node *node, struct trt_wrapper *wr_in, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ uint32_t max_gap_before_type;
+ struct trt_wrapper wr;
+ struct trt_node print_node;
+
+ assert(pc && tc && tc->section == TRD_SECT_MODULE);
+
+ /* stop recursion */
+ if (!node) {
+ return;
+ }
+ trb_print_parents(node->parent, wr_in, pc, tc);
+
+ /* setup for printing */
+ tc->cn = node;
+ wr = trb_count_depth(wr_in, node);
+
+ /* print node */
+ ly_print_(pc->out, "\n");
+ print_node = pc->fp.read.node(TRP_EMPTY_PARENT_CACHE, tc);
+ max_gap_before_type = trb_max_gap_to_type(TRP_EMPTY_PARENT_CACHE, pc, tc);
+ trb_print_entire_node(&print_node, max_gap_before_type, wr, pc, tc);
+}
+
+/**
+ * @brief Set current node on its child.
+ * @param[in,out] tc contains current node.
+ */
+static void
+trb_tree_ctx_set_child(struct trt_tree_ctx *tc)
+{
+ const void *node = tro_tree_ctx_get_child(tc);
+
+ if (tc->lysc_tree) {
+ tc->cn = node;
+ } else {
+ tc->pn = node;
+ }
+}
+
+/**
+ * @brief Move extension iterator to the next position.
+ *
+ * @param[in] lysc_tree flag if exts is from compiled tree.
+ * @param[in] exts is current array of extensions.
+ * @param[in,out] i is state of iterator.
+ * @return Pointer to the first/next extension.
+ */
+static void *
+trb_ext_iter_next(ly_bool lysc_tree, void *exts, uint64_t *i)
+{
+ void *ext = NULL;
+ struct lysc_ext_instance *ce;
+ struct lysp_ext_instance *pe;
+
+ if (!exts) {
+ return NULL;
+ }
+
+ if (lysc_tree) {
+ ce = exts;
+ while (*i < LY_ARRAY_COUNT(ce)) {
+ if (ce->def->plugin && trp_ext_parent_is_valid(1, &ce[*i])) {
+ ext = &ce[*i];
+ break;
+ }
+ ++(*i);
+ }
+ } else {
+ pe = exts;
+ while (*i < LY_ARRAY_COUNT(pe)) {
+ if (trp_ext_parent_is_valid(0, &pe[*i])) {
+ ext = &pe[*i];
+ break;
+ }
+ ++(*i);
+ }
+ }
+ ++(*i);
+
+ return ext;
+}
+
+/**
+ * @brief Iterate over extensions in module.
+ *
+ * @param[in] tc contains current node.
+ * @param[in,out] i is state of iterator.
+ * @return First/next extension or NULL.
+ */
+static void *
+trb_mod_ext_iter(const struct trt_tree_ctx *tc, uint64_t *i)
+{
+ if (tc->lysc_tree) {
+ return trb_ext_iter_next(1, tc->cmod->exts, i);
+ } else {
+ return trb_ext_iter_next(0, tc->pmod->exts, i);
+ }
+}
+
+/**
+ * @brief Iterate over extensions in node.
+ *
+ * @param[in] tc contains current node.
+ * @param[in,out] i is state of iterator.
+ * @return First/next extension or NULL.
+ */
+static void *
+trb_ext_iter(const struct trt_tree_ctx *tc, uint64_t *i)
+{
+ if (tc->lysc_tree) {
+ return trb_ext_iter_next(1, tc->cn->exts, i);
+ } else {
+ return trb_ext_iter_next(0, tc->pn->exts, i);
+ }
+}
+
+/**
+ * @brief Initialize plugin context.
+ *
+ * @param[in] compiled if @p ext is lysc structure.
+ * @param[in] ext current processed extension.
+ * @param[out] plug_ctx is plugin context which will be initialized.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+tro_ext_printer_tree(ly_bool compiled, void *ext, const struct lyspr_tree_ctx *plug_ctx)
+{
+ struct lysc_ext_instance *ext_comp;
+ struct lysp_ext_instance *ext_pars;
+ const char *flags = NULL, *add_opts = NULL;
+
+ if (compiled) {
+ ext_comp = ext;
+ return ext_comp->def->plugin->printer_ctree(ext, plug_ctx, &flags, &add_opts);
+ } else {
+ ext_pars = ext;
+ return ext_pars->record->plugin.printer_ptree(ext, plug_ctx, &flags, &add_opts);
+ }
+}
+
+/**
+ * @brief Reset tree context by plugin context.
+ *
+ * @param[in] plug_ctx is plugin context.
+ * @param[in] i which index in schemas should be used.
+ * @param[in] pc are printing functions.
+ * @param[out] tc tree context which will be updated.
+ */
+static void
+trm_reset_tree_ctx_by_plugin(struct lyspr_tree_ctx *plug_ctx, LY_ARRAY_COUNT_TYPE i, struct trt_printer_ctx *pc,
+ struct trt_tree_ctx *tc)
+{
+ tc->plugin_ctx.ctx = plug_ctx;
+ tc->pmod = NULL;
+ tc->cmod = NULL;
+ if (plug_ctx->schemas[i].compiled) {
+ tc->lysc_tree = 1;
+ tc->cn = plug_ctx->schemas[i].ctree;
+ tc->plugin_ctx.schema = &plug_ctx->schemas[i];
+ pc->fp.modify = TRP_TRT_FP_MODIFY_COMPILED;
+ pc->fp.read = TRP_TRT_FP_READ_COMPILED;
+ } else {
+ tc->lysc_tree = 0;
+ tc->pn = plug_ctx->schemas[i].ptree;
+ tc->tpn = tc->pn;
+ tc->plugin_ctx.schema = &plug_ctx->schemas[i];
+ pc->fp.modify = TRP_TRT_FP_MODIFY_PARSED;
+ pc->fp.read = TRP_TRT_FP_READ_PARSED;
+ }
+}
+
+/**
+ * @brief Print schemas from plugin context.
+ *
+ * @param[in] plug_ctx is plugin context.
+ * @param[in] last_nodes if this schemas will be the last.
+ * @param[in] max_gap_before_type is gap before type.
+ * @param[in] wr is indentation wrapper.
+ * @param[in] ca containing information from parent.
+ * @param[in] pc functions for tree traversing.
+ * @param[in] tc current tree context.
+ */
+static void
+trb_ext_print_schemas(struct lyspr_tree_ctx *plug_ctx, ly_bool last_nodes, uint32_t max_gap_before_type,
+ struct trt_wrapper wr, struct trt_parent_cache ca, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ LY_ARRAY_COUNT_TYPE i;
+ struct trt_printer_ctx pc_dupl;
+ struct trt_tree_ctx tc_dupl;
+ struct trt_node node;
+
+ tc_dupl = *tc;
+ pc_dupl = *pc;
+
+ LY_ARRAY_FOR(plug_ctx->schemas, i) {
+ trm_reset_tree_ctx_by_plugin(plug_ctx, i, pc, tc);
+ tc->plugin_ctx.last_schema = last_nodes && ((i + 1) == LY_ARRAY_COUNT(plug_ctx->schemas));
+ node = TRP_EMPTY_NODE;
+ trb_print_subtree_nodes(&node, max_gap_before_type, wr, ca, pc, tc);
+ *tc = tc_dupl;
+ }
+
+ *pc = pc_dupl;
+}
+
+/**
+ * @brief Count unified indentation across schemas from extension instance.
+ *
+ * @param[in] plug_ctx is plugin context.
+ * @param[in] ca containing parent settings.
+ * @param[out] max_gap_before_type is result of unified indent.
+ * @param[in] pc functions for tree traversing.
+ * @param[in] tc is tree context.
+ */
+static void
+trb_ext_try_unified_indent(struct lyspr_tree_ctx *plug_ctx, struct trt_parent_cache ca, uint32_t *max_gap_before_type,
+ struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ LY_ARRAY_COUNT_TYPE i;
+ struct trt_printer_ctx pc_dupl;
+ struct trt_tree_ctx tc_dupl;
+ uint32_t max;
+
+ tc_dupl = *tc;
+ pc_dupl = *pc;
+
+ LY_ARRAY_FOR(plug_ctx->schemas, i) {
+ trm_reset_tree_ctx_by_plugin(plug_ctx, i, pc, tc);
+ max = trb_try_unified_indent(ca, pc, tc);
+ *max_gap_before_type = max > *max_gap_before_type ? max : *max_gap_before_type;
+ *tc = tc_dupl;
+ }
+
+ *pc = pc_dupl;
+}
+
+/**
+ * @brief For every extension instance print all schemas.
+ *
+ * @param[in] wr indentation wrapper for node.
+ * @param[in] ca parent settings.
+ * @param[in] pc function used for tree traversing.
+ * @param[in] tc tree context.
+ */
+static void
+trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struct trt_printer_ctx *pc,
+ struct trt_tree_ctx *tc)
+{
+ LY_ERR rc;
+ LY_ARRAY_COUNT_TYPE i;
+ uint64_t last_instance = UINT64_MAX;
+ void *ext;
+ ly_bool child_exists;
+ uint32_t max, max_gap_before_type = 0;
+
+ ca = tro_parent_cache_for_child(ca, tc);
+ /* if node is last sibling, then do not add '|' to wrapper */
+ wr = trb_node_is_last_sibling(&pc->fp, tc) ?
+ trp_wrapper_set_shift(wr) : trp_wrapper_set_mark(wr);
+
+ if (tc->lysc_tree) {
+ child_exists = tro_next_child(tc->cn, tc) ? 1 : 0;
+ } else {
+ child_exists = tro_next_child(tc->pn, tc) ? 1 : 0;
+ }
+
+ i = 0;
+ while ((ext = trb_ext_iter(tc, &i))) {
+ struct lyspr_tree_ctx plug_ctx = {0};
+
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ trb_ext_try_unified_indent(&plug_ctx, ca, &max_gap_before_type, pc, tc);
+ if (plug_ctx.schemas) {
+ last_instance = i;
+ }
+ trp_ext_free_plugin_ctx(&plug_ctx);
+ }
+
+ if (child_exists) {
+ pc->fp.modify.next_child(ca, tc);
+ max = trb_try_unified_indent(ca, pc, tc);
+ max_gap_before_type = max > max_gap_before_type ? max : max_gap_before_type;
+ pc->fp.modify.parent(tc);
+ }
+
+ i = 0;
+ while ((ext = trb_ext_iter(tc, &i))) {
+ struct lyspr_tree_ctx plug_ctx = {0};
+
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ if (!child_exists && (last_instance == i)) {
+ trb_ext_print_schemas(&plug_ctx, 1, max_gap_before_type, wr, ca, pc, tc);
+ } else {
+ trb_ext_print_schemas(&plug_ctx, 0, max_gap_before_type, wr, ca, pc, tc);
+ }
+ trp_ext_free_plugin_ctx(&plug_ctx);
+ }
+
+end:
+ return;
+}
+
+/**
+ * @brief Print subtree of nodes.
+ *
+ * The current node is expected to be the root of the subtree.
+ * Before root node is no linebreak printing. This must be addressed by
+ * the caller. Root node will also be printed. Behind last printed node
+ * is no linebreak.
+ *
+ * @param[in,out] node current processed node used as iterator.
+ * @param[in] max_gap_before_type is result from
+ * ::trb_try_unified_indent() function for root node.
+ * Set parameter to 0 if distance does not matter.
+ * @param[in] wr is wrapper saying how deep in the whole tree
+ * is the root of the subtree.
+ * @param[in] ca is parent_cache from root's parent.
+ * If root is top-level node, insert ::TRP_EMPTY_PARENT_CACHE.
+ * @param[in] pc is @ref TRP_trp settings.
+ * @param[in,out] tc is context of tree printer.
+ */
+static void
+trb_print_subtree_nodes(struct trt_node *node, uint32_t max_gap_before_type, struct trt_wrapper wr,
+ struct trt_parent_cache ca, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ if (!trp_node_is_empty(node)) {
+ /* Print root node. */
+ trb_print_entire_node(node, max_gap_before_type, wr, pc, tc);
+ if (trp_ext_is_present_in_node(tc)) {
+ trb_ext_print_instances(wr, ca, pc, tc);
+ }
+ /* if node is last sibling, then do not add '|' to wrapper */
+ wr = trb_node_is_last_sibling(&pc->fp, tc) ?
+ trp_wrapper_set_shift(wr) : trp_wrapper_set_mark(wr);
+ /* go to the child */
+ ca = tro_parent_cache_for_child(ca, tc);
+ *node = pc->fp.modify.next_child(ca, tc);
+ if (trp_node_is_empty(node)) {
+ return;
+ }
+ /* TODO comment browse through instances + filtered. try unified indentation for children */
+ max_gap_before_type = trb_try_unified_indent(ca, pc, tc);
+ } else {
+ /* Root node is ignored, continue with child. */
+ *node = pc->fp.modify.first_sibling(ca, tc);
+ }
+
+ do {
+ if (!tc->plugin_ctx.filtered && !trb_need_implicit_node_case(tc)) {
+ /* normal behavior */
+ ly_print_(pc->out, "\n");
+ trb_print_subtree_nodes(node, max_gap_before_type, wr, ca, pc, tc);
+ } else if (!tc->plugin_ctx.filtered) {
+ struct trt_wrapper wr_case_child;
+
+ wr_case_child = trb_print_implicit_node(node, wr, pc, tc);
+ trb_print_subtree_nodes(node, max_gap_before_type, wr_case_child, ca, pc, tc);
+ }
+ /* go to the actual node's sibling */
+ *node = pc->fp.modify.next_sibling(ca, tc);
+ } while (!trp_node_is_empty(node));
+
+ /* get back from child node to root node */
+ pc->fp.modify.parent(tc);
+}
+
+/**
+ * @brief Print all parents and their children.
+ *
+ * This function is suitable for printing top-level nodes that
+ * do not have ancestors. Function call ::trb_print_subtree_nodes()
+ * for all top-level siblings. Use this function after 'module' keyword
+ * or 'augment' and so. The nodes may not be exactly top-level in the
+ * tree, but the function considers them that way.
+ *
+ * @param[in] wr is wrapper saying how deeply the top-level nodes are
+ * immersed in the tree.
+ * @param[pc] pc contains mainly functions for printing.
+ * @param[in,out] tc is tree context.
+ */
+static void
+trb_print_family_tree(struct trt_wrapper wr, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ struct trt_parent_cache ca;
+ struct trt_node node;
+ uint32_t max_gap_before_type;
+
+ if (!tro_tree_ctx_get_node(tc)) {
+ return;
+ }
+
+ ca = TRP_EMPTY_PARENT_CACHE;
+ max_gap_before_type = trb_try_unified_indent(ca, pc, tc);
+
+ if (!tc->lysc_tree) {
+ if ((tc->section == TRD_SECT_GROUPING) && (tc->tpn == tc->pn->parent)) {
+ ca.lys_config = 0x0;
+ }
+ }
+
+ for (node = pc->fp.modify.first_sibling(ca, tc);
+ !trp_node_is_empty(&node);
+ node = pc->fp.modify.next_sibling(ca, tc)) {
+ ly_print_(pc->out, "\n");
+ trb_print_subtree_nodes(&node, max_gap_before_type, wr, ca, pc, tc);
+ }
+}
+
+/**********************************************************************
+ * Definition of trm main functions
+ *********************************************************************/
+
+/**
+ * @brief Settings if lysp_node are used for browsing through the tree.
+ *
+ * @param[in] module YANG schema tree structure representing
+ * YANG module.
+ * @param[in] out is output handler.
+ * @param[in] max_line_length is the maximum line length limit
+ * that should not be exceeded.
+ * @param[in,out] pc will be adapted to lysp_tree.
+ * @param[in,out] tc will be adapted to lysp_tree.
+ */
+static void
+trm_lysp_tree_ctx(const struct lys_module *module, struct ly_out *out, size_t max_line_length,
+ struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ *tc = (struct trt_tree_ctx) {
+ .lysc_tree = 0,
+ .section = TRD_SECT_MODULE,
+ .pmod = module->parsed,
+ .cmod = NULL,
+ .pn = module->parsed ? module->parsed->data : NULL,
+ .tpn = module->parsed ? module->parsed->data : NULL,
+ .cn = NULL,
+ .last_error = 0,
+ .plugin_ctx.ctx = NULL,
+ .plugin_ctx.schema = NULL,
+ .plugin_ctx.filtered = 0,
+ .plugin_ctx.node_overr = TRP_TREE_CTX_EMPTY_NODE_OVERR,
+ .plugin_ctx.last_schema = 1,
+ .plugin_ctx.last_error = 0
+ };
+
+ pc->out = out;
+
+ pc->fp.modify = TRP_TRT_FP_MODIFY_PARSED;
+ pc->fp.read = TRP_TRT_FP_READ_PARSED;
+
+ pc->fp.print = (struct trt_fp_print) {
+ .print_features_names = tro_print_features_names,
+ .print_keys = tro_print_keys
+ };
+
+ pc->max_line_length = max_line_length;
+}
+
+/**
+ * @brief Settings if lysc_node are used for browsing through the tree.
+ *
+ * Pointers to current nodes will be set to module data.
+ *
+ * @param[in] module YANG schema tree structure representing
+ * YANG module.
+ * @param[in] out is output handler.
+ * @param[in] max_line_length is the maximum line length limit
+ * that should not be exceeded.
+ * @param[in,out] pc will be adapted to lysc_tree.
+ * @param[in,out] tc will be adapted to lysc_tree.
+ */
+static void
+trm_lysc_tree_ctx(const struct lys_module *module, struct ly_out *out, size_t max_line_length,
+ struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ *tc = (struct trt_tree_ctx) {
+ .lysc_tree = 1,
+ .section = TRD_SECT_MODULE,
+ .pmod = module->parsed,
+ .cmod = module->compiled,
+ .tpn = NULL,
+ .pn = NULL,
+ .cn = module->compiled->data,
+ .last_error = 0,
+ .plugin_ctx.ctx = NULL,
+ .plugin_ctx.schema = NULL,
+ .plugin_ctx.filtered = 0,
+ .plugin_ctx.node_overr = TRP_TREE_CTX_EMPTY_NODE_OVERR,
+ .plugin_ctx.last_schema = 1,
+ .plugin_ctx.last_error = 0
+ };
+
+ pc->out = out;
+
+ pc->fp.modify = TRP_TRT_FP_MODIFY_COMPILED;
+ pc->fp.read = TRP_TRT_FP_READ_COMPILED;
+
+ pc->fp.print = (struct trt_fp_print) {
+ .print_features_names = tro_print_features_names,
+ .print_keys = tro_print_keys
+ };
+
+ pc->max_line_length = max_line_length;
+}
+
+/**
+ * @brief Reset settings to browsing through the lysc tree.
+ * @param[in,out] pc resets to @ref TRP_troc functions.
+ * @param[in,out] tc resets to lysc browsing.
+ */
+static void
+trm_reset_to_lysc_tree_ctx(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ LY_ERR erc;
+
+ erc = tc->last_error;
+ trp_ext_free_node_override(&tc->plugin_ctx.node_overr, &tc->plugin_ctx.filtered);
+ trm_lysc_tree_ctx(tc->pmod->mod, pc->out, pc->max_line_length, pc, tc);
+ tc->last_error = erc;
+}
+
+/**
+ * @brief Reset settings to browsing through the lysp tree.
+ * @param[in,out] pc resets to @ref TRP_trop functions.
+ * @param[in,out] tc resets to lysp browsing.
+ */
+static void
+trm_reset_to_lysp_tree_ctx(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ LY_ERR erc;
+
+ erc = tc->last_error;
+ trp_ext_free_node_override(&tc->plugin_ctx.node_overr, &tc->plugin_ctx.filtered);
+ trm_lysp_tree_ctx(tc->pmod->mod, pc->out, pc->max_line_length, pc, tc);
+ tc->last_error = erc;
+}
+
+/**
+ * @brief If augment's target node is located on the current module.
+ * @param[in] pn is examined augment.
+ * @param[in] pmod is current module.
+ * @return 1 if nodeid refers to the local node, otherwise 0.
+ */
+static ly_bool
+trm_nodeid_target_is_local(const struct lysp_node_augment *pn, const struct lysp_module *pmod)
+{
+ const char *id, *prefix, *name;
+ size_t prefix_len, name_len;
+ const struct lys_module *mod;
+ ly_bool ret = 0;
+
+ if (pn == NULL) {
+ return ret;
+ }
+
+ id = pn->nodeid;
+ if (!id) {
+ return ret;
+ }
+ /* only absolute-schema-nodeid is taken into account */
+ assert(id[0] == '/');
+ ++id;
+
+ ly_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len);
+ if (prefix) {
+ mod = ly_resolve_prefix(pmod->mod->ctx, prefix, prefix_len, LY_VALUE_SCHEMA, pmod);
+ ret = mod ? (mod->parsed == pmod) : 0;
+ } else {
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Printing section module, rpcs, notifications or yang-data.
+ *
+ * First node must be the first child of 'module',
+ * 'rpcs', 'notifications' or 'yang-data'.
+ *
+ * @param[in] ks is section representation.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_section_as_family_tree(struct trt_keyword_stmt ks, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ assert(ks.section_name);
+
+ trp_print_keyword_stmt(ks, pc->max_line_length, pc->out);
+ if (!strcmp(ks.section_name, TRD_KEYWORD_MODULE) || !strcmp(ks.section_name, TRD_KEYWORD_SUBMODULE)) {
+ trb_print_family_tree(TRP_INIT_WRAPPER_TOP, pc, tc);
+ } else {
+ trb_print_family_tree(TRP_INIT_WRAPPER_BODY, pc, tc);
+ }
+}
+
+/**
+ * @brief Printing section augment or grouping.
+ *
+ * First node is 'augment' or 'grouping' itself.
+ *
+ * @param[in] ks is section representation.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_section_as_subtree(struct trt_keyword_stmt ks, struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ assert(ks.section_name);
+ trp_print_keyword_stmt(ks, pc->max_line_length, pc->out);
+ trb_tree_ctx_set_child(tc);
+ trb_print_family_tree(TRP_INIT_WRAPPER_BODY, pc, tc);
+}
+
+/**
+ * @brief Print 'module' keyword, its name and all nodes.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_module_section(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ trm_print_section_as_family_tree(pc->fp.read.module_name(tc), pc, tc);
+}
+
+/**
+ * @brief For all augment sections: print 'augment' keyword,
+ * its target node and all nodes.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_augmentations(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ ly_bool once;
+ ly_bool origin_was_lysc_tree = 0;
+ struct trt_keyword_stmt ks;
+
+ if (tc->lysc_tree) {
+ origin_was_lysc_tree = 1;
+ trm_reset_to_lysp_tree_ctx(pc, tc);
+ }
+
+ once = 1;
+ for (ks = trop_modi_next_augment(tc); ks.section_name; ks = trop_modi_next_augment(tc)) {
+
+ if (origin_was_lysc_tree) {
+ /* if lysc tree is used, then only augments targeting
+ * another module are printed
+ */
+ if (trm_nodeid_target_is_local((const struct lysp_node_augment *)tc->tpn, tc->pmod)) {
+ continue;
+ }
+ }
+
+ if (once) {
+ ly_print_(pc->out, "\n");
+ ly_print_(pc->out, "\n");
+ once = 0;
+ } else {
+ ly_print_(pc->out, "\n");
+ }
+
+ trm_print_section_as_subtree(ks, pc, tc);
+ }
+
+ if (origin_was_lysc_tree) {
+ trm_reset_to_lysc_tree_ctx(pc, tc);
+ }
+}
+
+/**
+ * @brief For rpcs section: print 'rpcs' keyword and all its nodes.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_rpcs(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ struct trt_keyword_stmt rpc;
+
+ rpc = tro_modi_get_rpcs(tc);
+
+ if (rpc.section_name) {
+ ly_print_(pc->out, "\n");
+ ly_print_(pc->out, "\n");
+ trm_print_section_as_family_tree(rpc, pc, tc);
+ }
+}
+
+/**
+ * @brief For notifications section: print 'notifications' keyword
+ * and all its nodes.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_notifications(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ struct trt_keyword_stmt notifs;
+
+ notifs = tro_modi_get_notifications(tc);
+
+ if (notifs.section_name) {
+ ly_print_(pc->out, "\n");
+ ly_print_(pc->out, "\n");
+ trm_print_section_as_family_tree(notifs, pc, tc);
+ }
+}
+
+/**
+ * @brief For all grouping sections: print 'grouping' keyword, its name
+ * and all nodes.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_groupings(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ ly_bool once;
+ struct trt_keyword_stmt ks;
+
+ if (tc->lysc_tree) {
+ return;
+ }
+
+ once = 1;
+ for (ks = trop_modi_next_grouping(tc); ks.section_name; ks = trop_modi_next_grouping(tc)) {
+ if (once) {
+ ly_print_(pc->out, "\n");
+ ly_print_(pc->out, "\n");
+ once = 0;
+ } else {
+ ly_print_(pc->out, "\n");
+ }
+ trm_print_section_as_subtree(ks, pc, tc);
+ }
+}
+
+/**
+ * @brief Print all sections defined in plugins.
+ *
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_plugin_ext(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ LY_ERR rc;
+ ly_bool once;
+ LY_ARRAY_COUNT_TYPE i = 0, j;
+ struct trt_keyword_stmt ks, prev_ks = {0};
+ struct trt_printer_ctx pc_dupl;
+ struct trt_tree_ctx tc_dupl;
+ struct trt_node node;
+ uint32_t max_gap_before_type;
+ void *ext;
+
+ tc->section = TRD_SECT_PLUG_DATA;
+
+ tc_dupl = *tc;
+ pc_dupl = *pc;
+
+ once = 1;
+
+ while ((ext = trb_mod_ext_iter(tc, &i))) {
+ struct lyspr_tree_ctx plug_ctx = {0};
+
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ if (!plug_ctx.schemas) {
+ continue;
+ }
+
+ ks = tro_get_ext_section(tc, ext, &plug_ctx);
+ if (once || (prev_ks.section_name && strcmp(prev_ks.section_name, ks.section_name))) {
+ ly_print_(pc->out, "\n");
+ ly_print_(pc->out, "\n");
+ once = 0;
+ } else {
+ ly_print_(pc->out, "\n");
+ }
+ trp_print_keyword_stmt(ks, pc->max_line_length, pc->out);
+
+ max_gap_before_type = 0;
+ trb_ext_try_unified_indent(&plug_ctx, TRP_EMPTY_PARENT_CACHE, &max_gap_before_type, pc, tc);
+ LY_ARRAY_FOR(plug_ctx.schemas, j) {
+ trm_reset_tree_ctx_by_plugin(&plug_ctx, j, pc, tc);
+ node = TRP_EMPTY_NODE;
+ trb_print_subtree_nodes(&node, max_gap_before_type, TRP_INIT_WRAPPER_BODY, TRP_EMPTY_PARENT_CACHE, pc, tc);
+ }
+
+ *tc = tc_dupl;
+ trp_ext_free_plugin_ctx(&plug_ctx);
+ prev_ks = ks;
+ }
+
+end:
+ *pc = pc_dupl;
+ return;
+}
+
+/**
+ * @brief Print sections module, augment, rpcs, notifications,
+ * grouping, yang-data.
+ * @param[in] pc contains mainly functions for printing.
+ * @param[in,out] tc is the tree context.
+ */
+static void
+trm_print_sections(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
+{
+ trm_print_module_section(pc, tc);
+ trm_print_augmentations(pc, tc);
+ trm_print_rpcs(pc, tc);
+ trm_print_notifications(pc, tc);
+ trm_print_groupings(pc, tc);
+ trm_print_plugin_ext(pc, tc);
+ ly_print_(pc->out, "\n");
+}
+
+static LY_ERR
+tree_print_check_error(struct ly_out_clb_arg *out, struct trt_tree_ctx *tc)
+{
+ if (out->last_error) {
+ return out->last_error;
+ } else if (tc->last_error) {
+ return tc->last_error;
+ } else {
+ return LY_SUCCESS;
+ }
+}
+
+/**********************************************************************
+ * Definition of module interface
+ *********************************************************************/
+
+LY_ERR
+tree_print_module(struct ly_out *out, const struct lys_module *module, uint32_t UNUSED(options), size_t line_length)
+{
+ struct trt_printer_ctx pc;
+ struct trt_tree_ctx tc;
+ struct ly_out *new_out;
+ LY_ERR erc;
+ struct ly_out_clb_arg clb_arg = TRP_INIT_LY_OUT_CLB_ARG(TRD_PRINT, out, 0, LY_SUCCESS);
+
+ LY_CHECK_ARG_RET3(module->ctx, out, module, module->parsed, LY_EINVAL);
+
+ if ((erc = ly_out_new_clb(&trp_ly_out_clb_func, &clb_arg, &new_out))) {
+ return erc;
+ }
+
+ line_length = line_length == 0 ? SIZE_MAX : line_length;
+ if ((module->ctx->flags & LY_CTX_SET_PRIV_PARSED) && module->compiled) {
+ trm_lysc_tree_ctx(module, new_out, line_length, &pc, &tc);
+ } else {
+ trm_lysp_tree_ctx(module, new_out, line_length, &pc, &tc);
+ }
+
+ trm_print_sections(&pc, &tc);
+ erc = tree_print_check_error(&clb_arg, &tc);
+
+ ly_out_free(new_out, NULL, 1);
+
+ return erc;
+}
+
+LY_ERR
+tree_print_compiled_node(struct ly_out *out, const struct lysc_node *node, uint32_t options, size_t line_length)
+{
+ struct trt_printer_ctx pc;
+ struct trt_tree_ctx tc;
+ struct ly_out *new_out;
+ struct trt_wrapper wr;
+ LY_ERR erc;
+ struct ly_out_clb_arg clb_arg = TRP_INIT_LY_OUT_CLB_ARG(TRD_PRINT, out, 0, LY_SUCCESS);
+
+ assert(out && node);
+
+ if (!(node->module->ctx->flags & LY_CTX_SET_PRIV_PARSED)) {
+ return LY_EINVAL;
+ }
+
+ if ((erc = ly_out_new_clb(&trp_ly_out_clb_func, &clb_arg, &new_out))) {
+ return erc;
+ }
+
+ line_length = line_length == 0 ? SIZE_MAX : line_length;
+ trm_lysc_tree_ctx(node->module, new_out, line_length, &pc, &tc);
+
+ trp_print_keyword_stmt(pc.fp.read.module_name(&tc), pc.max_line_length, pc.out);
+ trb_print_parents(node, NULL, &pc, &tc);
+
+ if (!(options & LYS_PRINT_NO_SUBSTMT)) {
+ tc.cn = lysc_node_child(node);
+ wr = trb_count_depth(NULL, tc.cn);
+ trb_print_family_tree(wr, &pc, &tc);
+ }
+ ly_print_(out, "\n");
+
+ erc = tree_print_check_error(&clb_arg, &tc);
+ ly_out_free(new_out, NULL, 1);
+
+ return erc;
+}
+
+LY_ERR
+tree_print_parsed_submodule(struct ly_out *out, const struct lysp_submodule *submodp, uint32_t UNUSED(options),
+ size_t line_length)
+{
+ struct trt_printer_ctx pc;
+ struct trt_tree_ctx tc;
+ struct ly_out *new_out;
+ LY_ERR erc;
+ struct ly_out_clb_arg clb_arg = TRP_INIT_LY_OUT_CLB_ARG(TRD_PRINT, out, 0, LY_SUCCESS);
+
+ assert(submodp);
+ LY_CHECK_ARG_RET(submodp->mod->ctx, out, LY_EINVAL);
+
+ if ((erc = ly_out_new_clb(&trp_ly_out_clb_func, &clb_arg, &new_out))) {
+ return erc;
+ }
+
+ line_length = line_length == 0 ? SIZE_MAX : line_length;
+ trm_lysp_tree_ctx(submodp->mod, new_out, line_length, &pc, &tc);
+ tc.pmod = (struct lysp_module *)submodp;
+ tc.tpn = submodp->data;
+ tc.pn = tc.tpn;
+
+ trm_print_sections(&pc, &tc);
+ erc = tree_print_check_error(&clb_arg, &tc);
+
+ ly_out_free(new_out, NULL, 1);
+
+ return erc;
+}
diff --git a/src/printer_xml.c b/src/printer_xml.c
new file mode 100644
index 0000000..a7f4c73
--- /dev/null
+++ b/src/printer_xml.c
@@ -0,0 +1,607 @@
+/**
+ * @file printer_xml.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief XML printer for libyang data structure
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "context.h"
+#include "dict.h"
+#include "log.h"
+#include "out.h"
+#include "out_internal.h"
+#include "parser_data.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_types.h"
+#include "printer_data.h"
+#include "printer_internal.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+#include "xml.h"
+
+/**
+ * @brief XML printer context.
+ */
+struct xmlpr_ctx {
+ struct ly_out *out; /**< output specification */
+ uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+ uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
+ const struct ly_ctx *ctx; /**< libyang context */
+ struct ly_set prefix; /**< printed namespace prefixes */
+ struct ly_set ns; /**< printed namespaces */
+};
+
+#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
+#define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */
+
+/**
+ * @brief Print a namespace if not already printed.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] ns Namespace to print, expected to be in dictionary.
+ * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
+ * @param[in] prefix_opts Prefix options changing the meaning of parameters.
+ * @return Printed prefix of the namespace to use.
+ */
+static const char *
+xml_print_ns(struct xmlpr_ctx *pctx, const char *ns, const char *new_prefix, uint32_t prefix_opts)
+{
+ uint32_t i;
+
+ for (i = pctx->ns.count; i > 0; --i) {
+ if (!new_prefix) {
+ /* find default namespace */
+ if (!pctx->prefix.objs[i - 1]) {
+ if (!strcmp(pctx->ns.objs[i - 1], ns)) {
+ /* matching default namespace */
+ return pctx->prefix.objs[i - 1];
+ }
+ /* not matching default namespace */
+ break;
+ }
+ } else {
+ /* find prefixed namespace */
+ if (!strcmp(pctx->ns.objs[i - 1], ns)) {
+ if (!pctx->prefix.objs[i - 1]) {
+ /* default namespace is not interesting */
+ continue;
+ }
+
+ if (!strcmp(pctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
+ /* the same prefix or can be any */
+ return pctx->prefix.objs[i - 1];
+ }
+ }
+ }
+ }
+
+ /* suitable namespace not found, must be printed */
+ ly_print_(pctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
+
+ /* and added into namespaces */
+ if (new_prefix) {
+ LY_CHECK_RET(lydict_insert(pctx->ctx, new_prefix, 0, &new_prefix), NULL);
+ }
+ LY_CHECK_RET(ly_set_add(&pctx->prefix, (void *)new_prefix, 1, NULL), NULL);
+ LY_CHECK_RET(ly_set_add(&pctx->ns, (void *)ns, 1, &i), NULL);
+
+ /* return it */
+ return pctx->prefix.objs[i];
+}
+
+static const char *
+xml_print_ns_opaq(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, const struct ly_opaq_name *name, uint32_t prefix_opts)
+{
+ switch (format) {
+ case LY_VALUE_XML:
+ return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
+ break;
+ case LY_VALUE_JSON:
+ if (name->module_name) {
+ const struct lys_module *mod = ly_ctx_get_module_latest(pctx->ctx, name->module_name);
+
+ if (mod) {
+ return xml_print_ns(pctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
+ }
+ }
+ break;
+ default:
+ /* cannot be created */
+ LOGINT(pctx->ctx);
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Print prefix data.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] format Value prefix format, only ::LY_VALUE_XML supported.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] prefix_opts Prefix options changing the meaning of parameters.
+ * @return LY_ERR value.
+ */
+static void
+xml_print_ns_prefix_data(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, void *prefix_data, uint32_t prefix_opts)
+{
+ const struct ly_set *set;
+ const struct lyxml_ns *ns;
+ uint32_t i;
+
+ switch (format) {
+ case LY_VALUE_XML:
+ set = prefix_data;
+ for (i = 0; i < set->count; ++i) {
+ ns = set->objs[i];
+ if (!ns->prefix) {
+ /* default namespace is not for the element */
+ continue;
+ }
+
+ xml_print_ns(pctx, ns->uri, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : ns->prefix, prefix_opts);
+ }
+ break;
+ default:
+ /* cannot be created */
+ LOGINT(pctx->ctx);
+ }
+}
+
+/**
+ * @brief Print metadata of a node.
+ *
+ * @param[in] pctx XML printer context.
+ * @param[in] node Data node with metadata.
+ */
+static void
+xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
+{
+ struct lyd_meta *meta;
+ const struct lys_module *mod;
+ struct ly_set ns_list = {0};
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool dynamic, filter_attrs = 0;
+
+ /* with-defaults */
+ if (node->schema->nodetype & LYD_NODE_TERM) {
+ if (((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
+ ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
+ /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
+ mod = ly_ctx_get_module_latest(LYD_CTX(node), "ietf-netconf-with-defaults");
+ if (mod) {
+ ly_print_(pctx->out, " %s:default=\"true\"", xml_print_ns(pctx, mod->ns, mod->prefix, 0));
+ }
+ }
+ }
+
+ /* check for NETCONF filter unqualified attributes */
+ if (!strcmp(node->schema->module->name, "notifications")) {
+ filter_attrs = 1;
+ } else {
+ LY_ARRAY_FOR(node->schema->exts, u) {
+ if (!strcmp(node->schema->exts[u].def->name, "get-filter-element-attributes") &&
+ !strcmp(node->schema->exts[u].def->module->name, "ietf-netconf")) {
+ filter_attrs = 1;
+ break;
+ }
+ }
+ }
+
+ for (meta = node->meta; meta; meta = meta->next) {
+ const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
+ &dynamic, NULL);
+
+ /* print namespaces connected with the value's prefixes */
+ for (uint32_t i = 0; i < ns_list.count; ++i) {
+ mod = ns_list.objs[i];
+ xml_print_ns(pctx, mod->ns, mod->prefix, 1);
+ }
+ ly_set_erase(&ns_list, NULL);
+
+ mod = meta->annotation->module;
+ if (filter_attrs && !strcmp(mod->name, "ietf-netconf") && (!strcmp(meta->name, "type") ||
+ !strcmp(meta->name, "select"))) {
+ /* print special NETCONF filter unqualified attributes */
+ ly_print_(pctx->out, " %s=\"", meta->name);
+ } else {
+ /* print the metadata with its namespace */
+ ly_print_(pctx->out, " %s:%s=\"", xml_print_ns(pctx, mod->ns, mod->prefix, 1), meta->name);
+ }
+
+ /* print metadata value */
+ if (value && value[0]) {
+ lyxml_dump_text(pctx->out, value, 1);
+ }
+ ly_print_(pctx->out, "\"");
+ if (dynamic) {
+ free((void *)value);
+ }
+ }
+}
+
+/**
+ * @brief Print generic XML element despite of the data node type.
+ *
+ * Prints the element name, attributes and necessary namespaces.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ */
+static void
+xml_print_node_open(struct xmlpr_ctx *pctx, const struct lyd_node *node)
+{
+ /* print node name */
+ ly_print_(pctx->out, "%*s<%s", INDENT, node->schema->name);
+
+ /* print default namespace */
+ xml_print_ns(pctx, node->schema->module->ns, NULL, 0);
+
+ /* print metadata */
+ xml_print_meta(pctx, node);
+}
+
+static LY_ERR
+xml_print_attr(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
+{
+ const struct lyd_attr *attr;
+ const char *pref;
+
+ LY_LIST_FOR(node->attr, attr) {
+ pref = NULL;
+ if (attr->name.prefix) {
+ /* print attribute namespace */
+ pref = xml_print_ns_opaq(pctx, attr->format, &attr->name, 0);
+ }
+
+ /* print namespaces connected with the value's prefixes */
+ if (attr->val_prefix_data) {
+ xml_print_ns_prefix_data(pctx, attr->format, attr->val_prefix_data, LYXML_PREFIX_REQUIRED);
+ }
+
+ /* print the attribute with its prefix and value */
+ ly_print_(pctx->out, " %s%s%s=\"", pref ? pref : "", pref ? ":" : "", attr->name.name);
+ lyxml_dump_text(pctx->out, attr->value, 1);
+ ly_print_(pctx->out, "\""); /* print attribute value terminator */
+
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+xml_print_opaq_open(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
+{
+ /* print node name */
+ ly_print_(pctx->out, "%*s<%s", INDENT, node->name.name);
+
+ if (node->name.prefix || node->name.module_ns) {
+ /* print default namespace */
+ xml_print_ns_opaq(pctx, node->format, &node->name, LYXML_PREFIX_DEFAULT);
+ }
+
+ /* print attributes */
+ LY_CHECK_RET(xml_print_attr(pctx, node));
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node);
+
+/**
+ * @brief Print XML element representing lyd_node_term.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
+{
+ struct ly_set ns_list = {0};
+ ly_bool dynamic;
+ const char *value;
+
+ xml_print_node_open(pctx, &node->node);
+ value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
+ &ns_list, &dynamic, NULL);
+ LY_CHECK_RET(!value, LY_EINVAL);
+
+ /* print namespaces connected with the values's prefixes */
+ for (uint32_t u = 0; u < ns_list.count; ++u) {
+ const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
+
+ ly_print_(pctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
+ }
+ ly_set_erase(&ns_list, NULL);
+
+ if (!value[0]) {
+ ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
+ } else {
+ ly_print_(pctx->out, ">");
+ lyxml_dump_text(pctx->out, value, 0);
+ ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
+ }
+ if (dynamic) {
+ free((void *)value);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print XML element representing lyd_node_inner.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_inner(struct xmlpr_ctx *pctx, const struct lyd_node_inner *node)
+{
+ LY_ERR ret;
+ struct lyd_node *child;
+
+ xml_print_node_open(pctx, &node->node);
+
+ LY_LIST_FOR(node->child, child) {
+ if (lyd_node_should_print(child, pctx->options)) {
+ break;
+ }
+ }
+ if (!child) {
+ /* there are no children that will be printed */
+ ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
+ return LY_SUCCESS;
+ }
+
+ /* children */
+ ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
+
+ LEVEL_INC;
+ LY_LIST_FOR(node->child, child) {
+ ret = xml_print_node(pctx, child);
+ LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
+ }
+ LEVEL_DEC;
+
+ ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+xml_print_anydata(struct xmlpr_ctx *pctx, const struct lyd_node_any *node)
+{
+ struct lyd_node_any *any = (struct lyd_node_any *)node;
+ struct lyd_node *iter;
+ uint32_t prev_opts, temp_lo = 0;
+ LY_ERR ret;
+
+ if ((node->schema->nodetype == LYS_ANYDATA) && (node->value_type != LYD_ANYDATA_DATATREE)) {
+ LOGINT_RET(pctx->ctx);
+ }
+
+ xml_print_node_open(pctx, &node->node);
+
+ if (!any->value.tree) {
+ /* no content */
+no_content:
+ ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
+ return LY_SUCCESS;
+ } else {
+ if (any->value_type == LYD_ANYDATA_LYB) {
+ /* turn logging off */
+ ly_temp_log_options(&temp_lo);
+
+ /* try to parse it into a data tree */
+ if (lyd_parse_data_mem((struct ly_ctx *)LYD_CTX(node), any->value.mem, LYD_LYB,
+ LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &iter) == LY_SUCCESS) {
+ /* successfully parsed */
+ free(any->value.mem);
+ any->value.tree = iter;
+ any->value_type = LYD_ANYDATA_DATATREE;
+ }
+
+ /* turn logging on again */
+ ly_temp_log_options(NULL);
+ }
+
+ switch (any->value_type) {
+ case LYD_ANYDATA_DATATREE:
+ /* close opening tag and print data */
+ prev_opts = pctx->options;
+ pctx->options &= ~LYD_PRINT_WITHSIBLINGS;
+ LEVEL_INC;
+
+ ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
+ LY_LIST_FOR(any->value.tree, iter) {
+ ret = xml_print_node(pctx, iter);
+ LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
+ }
+
+ LEVEL_DEC;
+ pctx->options = prev_opts;
+ break;
+ case LYD_ANYDATA_STRING:
+ /* escape XML-sensitive characters */
+ if (!any->value.str[0]) {
+ goto no_content;
+ }
+ /* close opening tag and print data */
+ ly_print_(pctx->out, ">");
+ lyxml_dump_text(pctx->out, any->value.str, 0);
+ break;
+ case LYD_ANYDATA_XML:
+ /* print without escaping special characters */
+ if (!any->value.str[0]) {
+ goto 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 */
+ LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
+ goto no_content;
+ }
+
+ /* closing tag */
+ if (any->value_type == LYD_ANYDATA_DATATREE) {
+ ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
+ } else {
+ ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+xml_print_opaq(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
+{
+ LY_ERR ret;
+ struct lyd_node *child;
+
+ LY_CHECK_RET(xml_print_opaq_open(pctx, node));
+
+ if (node->value[0]) {
+ /* print namespaces connected with the value's prefixes */
+ if (node->val_prefix_data) {
+ xml_print_ns_prefix_data(pctx, node->format, node->val_prefix_data, LYXML_PREFIX_REQUIRED);
+ }
+
+ ly_print_(pctx->out, ">");
+ lyxml_dump_text(pctx->out, node->value, 0);
+ }
+
+ if (node->child) {
+ /* children */
+ if (!node->value[0]) {
+ ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
+ }
+
+ LEVEL_INC;
+ LY_LIST_FOR(node->child, child) {
+ ret = xml_print_node(pctx, child);
+ LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
+ }
+ LEVEL_DEC;
+
+ ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->name.name, DO_FORMAT ? "\n" : "");
+ } else if (node->value[0]) {
+ ly_print_(pctx->out, "</%s>%s", node->name.name, DO_FORMAT ? "\n" : "");
+ } else {
+ /* no value or children */
+ ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Print XML element representing lyd_node.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t ns_count;
+
+ if (!lyd_node_should_print(node, pctx->options)) {
+ /* do not print at all */
+ return LY_SUCCESS;
+ }
+
+ /* remember namespace definition count on this level */
+ ns_count = pctx->ns.count;
+
+ if (!node->schema) {
+ ret = xml_print_opaq(pctx, (const struct lyd_node_opaq *)node);
+ } else {
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_NOTIF:
+ case LYS_RPC:
+ case LYS_ACTION:
+ ret = xml_print_inner(pctx, (const struct lyd_node_inner *)node);
+ break;
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ ret = xml_print_term(pctx, (const struct lyd_node_term *)node);
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ ret = xml_print_anydata(pctx, (const struct lyd_node_any *)node);
+ break;
+ default:
+ LOGINT(pctx->ctx);
+ ret = LY_EINT;
+ break;
+ }
+ }
+
+ /* remove all added namespaces */
+ while (ns_count < pctx->ns.count) {
+ lydict_remove(pctx->ctx, pctx->prefix.objs[pctx->prefix.count - 1]);
+ ly_set_rm_index(&pctx->prefix, pctx->prefix.count - 1, NULL);
+ ly_set_rm_index(&pctx->ns, pctx->ns.count - 1, NULL);
+ }
+
+ return ret;
+}
+
+LY_ERR
+xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
+{
+ const struct lyd_node *node;
+ struct xmlpr_ctx pctx = {0};
+
+ if (!root) {
+ if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
+ ly_print_(out, "");
+ }
+ goto finish;
+ }
+
+ pctx.out = out;
+ pctx.level = 0;
+ pctx.options = options;
+ pctx.ctx = LYD_CTX(root);
+
+ /* content */
+ LY_LIST_FOR(root, node) {
+ LY_CHECK_RET(xml_print_node(&pctx, node));
+ if (!(options & LYD_PRINT_WITHSIBLINGS)) {
+ break;
+ }
+ }
+
+finish:
+ assert(!pctx.prefix.count && !pctx.ns.count);
+ ly_set_erase(&pctx.prefix, NULL);
+ ly_set_erase(&pctx.ns, NULL);
+ ly_print_flush(out);
+ return LY_SUCCESS;
+}
diff --git a/src/printer_yang.c b/src/printer_yang.c
new file mode 100644
index 0000000..ea643ac
--- /dev/null
+++ b/src/printer_yang.c
@@ -0,0 +1,2657 @@
+/**
+ * @file printer_yang.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG printer
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+#include "out.h"
+#include "out_internal.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+#include "printer_internal.h"
+#include "printer_schema.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+/**
+ * @brief Types of the YANG printers
+ */
+enum lys_ypr_schema_type {
+ LYS_YPR_PARSED, /**< YANG printer of the parsed schema */
+ LYS_YPR_COMPILED /**< YANG printer of the compiled schema */
+};
+
+#define YPR_CTX_FLAG_EXTRA_LINE 0x01 /**< Flag for ::ypr_ctx::flags to print extra line in schema */
+
+#define YPR_EXTRA_LINE(COND, PCTX) if (COND) { (PCTX)->flags |= YPR_CTX_FLAG_EXTRA_LINE; }
+#define YPR_EXTRA_LINE_PRINT(PCTX) \
+ if ((PCTX)->flags & YPR_CTX_FLAG_EXTRA_LINE) { \
+ (PCTX)->flags &= ~YPR_CTX_FLAG_EXTRA_LINE; \
+ if (DO_FORMAT) { \
+ ly_print_((PCTX)->out, "\n"); \
+ } \
+ }
+
+/**
+ * @brief Compiled YANG printer context
+ *
+ * Note that the YANG extensions API provides getter to the members for the extension plugins.
+ */
+struct lys_ypr_ctx {
+ union {
+ struct {
+ struct ly_out *out; /**< output specification */
+ uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+ uint16_t flags; /**< internal flags for use by printer */
+ uint32_t options; /**< Schema output options (see @ref schemaprinterflags). */
+ const struct lys_module *module; /**< schema to print */
+ };
+ struct lyspr_ctx printer_ctx;
+ };
+
+ /* YANG printer specific members */
+ enum lys_ypr_schema_type schema; /**< type of the schema to print */
+};
+
+/**
+ * @brief Print the given text as content of a double quoted YANG string,
+ * including encoding characters that have special meanings. The quotation marks
+ * are not printed.
+ *
+ * Follows RFC 7950, section 6.1.3.
+ *
+ * @param[in] out Output specification.
+ * @param[in] text String to be printed.
+ * @param[in] len Length of the string from @p text to be printed. In case of -1,
+ * the @p text is printed completely as a NULL-terminated string.
+ */
+static void
+ypr_encode(struct ly_out *out, const char *text, ssize_t len)
+{
+ size_t i, start_len;
+ const char *start;
+ char special = 0;
+
+ if (!len) {
+ return;
+ }
+
+ if (len < 0) {
+ len = strlen(text);
+ }
+
+ start = text;
+ start_len = 0;
+ for (i = 0; i < (size_t)len; ++i) {
+ switch (text[i]) {
+ case '\n':
+ case '\t':
+ case '\"':
+ case '\\':
+ special = text[i];
+ break;
+ default:
+ ++start_len;
+ break;
+ }
+
+ if (special) {
+ ly_write_(out, start, start_len);
+ switch (special) {
+ case '\n':
+ ly_write_(out, "\\n", 2);
+ break;
+ case '\t':
+ ly_write_(out, "\\t", 2);
+ break;
+ case '\"':
+ ly_write_(out, "\\\"", 2);
+ break;
+ case '\\':
+ ly_write_(out, "\\\\", 2);
+ break;
+ }
+
+ start += start_len + 1;
+ start_len = 0;
+
+ special = 0;
+ }
+ }
+
+ ly_write_(out, start, start_len);
+}
+
+static void
+ypr_open(struct ly_out *out, ly_bool *flag)
+{
+ if (flag && !*flag) {
+ *flag = 1;
+ ly_print_(out, " {\n");
+ }
+}
+
+static void
+ypr_close(struct lys_ypr_ctx *pctx, ly_bool flag)
+{
+ if (flag) {
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ } else {
+ ly_print_(pctx->out, ";\n");
+ }
+}
+
+static void
+ypr_text(struct lys_ypr_ctx *pctx, const char *name, const char *text, ly_bool singleline, ly_bool closed)
+{
+ const char *s, *t;
+
+ if (singleline) {
+ ly_print_(pctx->out, "%*s%s \"", INDENT, name);
+ } else {
+ ly_print_(pctx->out, "%*s%s\n", INDENT, name);
+ LEVEL++;
+
+ ly_print_(pctx->out, "%*s\"", INDENT);
+ }
+ t = text;
+ while ((s = strchr(t, '\n'))) {
+ ypr_encode(pctx->out, t, s - t);
+ ly_print_(pctx->out, "\n");
+ t = s + 1;
+ if (*t != '\n') {
+ ly_print_(pctx->out, "%*s ", INDENT);
+ }
+ }
+
+ ypr_encode(pctx->out, t, strlen(t));
+ if (closed) {
+ ly_print_(pctx->out, "\";\n");
+ } else {
+ ly_print_(pctx->out, "\"");
+ }
+ if (!singleline) {
+ LEVEL--;
+ }
+}
+
+static void
+yprp_stmt(struct lys_ypr_ctx *pctx, struct lysp_stmt *stmt)
+{
+ struct lysp_stmt *childstmt;
+ const char *s, *t;
+
+ if (stmt->arg) {
+ if (stmt->flags) {
+ ly_print_(pctx->out, "%*s%s\n", INDENT, stmt->stmt);
+ LEVEL++;
+ ly_print_(pctx->out, "%*s%c", INDENT, (stmt->flags & LYS_DOUBLEQUOTED) ? '\"' : '\'');
+ t = stmt->arg;
+ while ((s = strchr(t, '\n'))) {
+ ypr_encode(pctx->out, t, s - t);
+ ly_print_(pctx->out, "\n");
+ t = s + 1;
+ if (*t != '\n') {
+ ly_print_(pctx->out, "%*s ", INDENT);
+ }
+ }
+ LEVEL--;
+ ypr_encode(pctx->out, t, strlen(t));
+ ly_print_(pctx->out, "%c%s", (stmt->flags & LYS_DOUBLEQUOTED) ? '\"' : '\'', stmt->child ? " {\n" : ";\n");
+ } else {
+ ly_print_(pctx->out, "%*s%s %s%s", INDENT, stmt->stmt, stmt->arg, stmt->child ? " {\n" : ";\n");
+ }
+ } else {
+ ly_print_(pctx->out, "%*s%s%s", INDENT, stmt->stmt, stmt->child ? " {\n" : ";\n");
+ }
+
+ if (stmt->child) {
+ LEVEL++;
+ LY_LIST_FOR(stmt->child, childstmt) {
+ yprp_stmt(pctx, childstmt);
+ }
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ }
+}
+
+static void
+yprp_extension_instance(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index,
+ struct lysp_ext_instance *ext, ly_bool *flag)
+{
+ struct lysp_stmt *stmt;
+ ly_bool child_presence;
+
+ if ((ext->flags & LYS_INTERNAL) || (ext->parent_stmt != substmt) || (ext->parent_stmt_index != substmt_index)) {
+ return;
+ }
+
+ ypr_open(pctx->out, flag);
+
+ if (ext->def->argname) {
+ ly_print_(pctx->out, "%*s%s \"", INDENT, ext->name);
+ ypr_encode(pctx->out, ext->argument, -1);
+ ly_print_(pctx->out, "\"");
+ } else {
+ ly_print_(pctx->out, "%*s%s", INDENT, ext->name);
+ }
+
+ child_presence = 0;
+ LEVEL++;
+ LY_LIST_FOR(ext->child, stmt) {
+ if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) {
+ continue;
+ }
+ if (!child_presence) {
+ ly_print_(pctx->out, " {\n");
+ child_presence = 1;
+ }
+ yprp_stmt(pctx, stmt);
+ }
+ LEVEL--;
+ if (child_presence) {
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ } else {
+ ly_print_(pctx->out, ";\n");
+ }
+}
+
+static void
+yprp_extension_instances(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index,
+ struct lysp_ext_instance *exts, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(exts, u) {
+ yprp_extension_instance(pctx, substmt, substmt_index, &exts[u], flag);
+ }
+}
+
+static void
+yprc_extension_instances(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index,
+ struct lysc_ext_instance *exts, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool inner_flag;
+
+ LY_ARRAY_FOR(exts, u) {
+ if ((exts[u].parent_stmt != substmt) || (exts[u].parent_stmt_index != substmt_index)) {
+ return;
+ }
+
+ ypr_open(pctx->out, flag);
+ if (exts[u].argument) {
+ ly_print_(pctx->out, "%*s%s:%s \"", INDENT, exts[u].def->module->name, exts[u].def->name);
+ ypr_encode(pctx->out, exts[u].argument, -1);
+ ly_print_(pctx->out, "\"");
+ } else {
+ ly_print_(pctx->out, "%*s%s:%s", INDENT, exts[u].def->module->name, exts[u].def->name);
+ }
+
+ LEVEL++;
+ inner_flag = 0;
+ yprc_extension_instances(pctx, LY_STMT_EXTENSION_INSTANCE, 0, exts[u].exts, &inner_flag);
+
+ if (exts[u].def->plugin && exts[u].def->plugin->printer_info) {
+ exts[u].def->plugin->printer_info(&pctx->printer_ctx, &exts[u], &inner_flag);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+ }
+}
+
+static void
+ypr_substmt(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index, const char *text, void *exts)
+{
+ ly_bool extflag = 0;
+
+ if (!text) {
+ /* nothing to print */
+ return;
+ }
+
+ if (lys_stmt_flags(substmt) & LY_STMT_FLAG_ID) {
+ ly_print_(pctx->out, "%*s%s %s", INDENT, lys_stmt_str(substmt), text);
+ } else {
+ ypr_text(pctx, lys_stmt_str(substmt), text, (lys_stmt_flags(substmt) & LY_STMT_FLAG_YIN) ? 0 : 1, 0);
+ }
+
+ LEVEL++;
+ if (pctx->schema == LYS_YPR_PARSED) {
+ yprp_extension_instances(pctx, substmt, substmt_index, exts, &extflag);
+ } else {
+ yprc_extension_instances(pctx, substmt, substmt_index, exts, &extflag);
+ }
+ LEVEL--;
+ ypr_close(pctx, extflag);
+}
+
+static void
+ypr_unsigned(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index, void *exts,
+ unsigned long attr_value, ly_bool *flag)
+{
+ char *str;
+
+ if (asprintf(&str, "%lu", attr_value) == -1) {
+ LOGMEM(pctx->module->ctx);
+ return;
+ }
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, substmt, substmt_index, str, exts);
+ free(str);
+}
+
+static void
+ypr_signed(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index, void *exts, long attr_value,
+ ly_bool *flag)
+{
+ char *str;
+
+ if (asprintf(&str, "%ld", attr_value) == -1) {
+ LOGMEM(pctx->module->ctx);
+ return;
+ }
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, substmt, substmt_index, str, exts);
+ free(str);
+}
+
+static void
+yprp_revision(struct lys_ypr_ctx *pctx, const struct lysp_revision *rev)
+{
+ if (rev->dsc || rev->ref || rev->exts) {
+ ly_print_(pctx->out, "%*srevision %s {\n", INDENT, rev->date);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_REVISION, 0, rev->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, rev->dsc, rev->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, rev->ref, rev->exts);
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ } else {
+ ly_print_(pctx->out, "%*srevision %s;\n", INDENT, rev->date);
+ }
+}
+
+static void
+ypr_mandatory(struct lys_ypr_ctx *pctx, uint16_t flags, void *exts, ly_bool *flag)
+{
+ if (flags & LYS_MAND_MASK) {
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_MANDATORY, 0, (flags & LYS_MAND_TRUE) ? "true" : "false", exts);
+ }
+}
+
+static void
+ypr_config(struct lys_ypr_ctx *pctx, uint16_t flags, void *exts, ly_bool *flag)
+{
+ if (flags & LYS_CONFIG_MASK) {
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_CONFIG, 0, (flags & LYS_CONFIG_W) ? "true" : "false", exts);
+ }
+}
+
+static void
+ypr_status(struct lys_ypr_ctx *pctx, uint16_t flags, void *exts, ly_bool *flag)
+{
+ const char *status = NULL;
+
+ if (flags & LYS_STATUS_CURR) {
+ ypr_open(pctx->out, flag);
+ status = "current";
+ } else if (flags & LYS_STATUS_DEPRC) {
+ ypr_open(pctx->out, flag);
+ status = "deprecated";
+ } else if (flags & LYS_STATUS_OBSLT) {
+ ypr_open(pctx->out, flag);
+ status = "obsolete";
+ }
+
+ ypr_substmt(pctx, LY_STMT_STATUS, 0, status, exts);
+}
+
+static void
+ypr_description(struct lys_ypr_ctx *pctx, const char *dsc, void *exts, ly_bool *flag)
+{
+ if (dsc) {
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, dsc, exts);
+ }
+}
+
+static void
+ypr_reference(struct lys_ypr_ctx *pctx, const char *ref, void *exts, ly_bool *flag)
+{
+ if (ref) {
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, ref, exts);
+ }
+}
+
+static void
+yprp_iffeatures(struct lys_ypr_ctx *pctx, struct lysp_qname *iffs, struct lysp_ext_instance *exts, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool extflag;
+
+ LY_ARRAY_FOR(iffs, u) {
+ ypr_open(pctx->out, flag);
+ extflag = 0;
+
+ ly_print_(pctx->out, "%*sif-feature \"%s\"", INDENT, iffs[u].str);
+
+ /* extensions */
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_IF_FEATURE, u, exts, &extflag);
+ LEVEL--;
+ ypr_close(pctx, extflag);
+ }
+}
+
+static void
+yprp_extension(struct lys_ypr_ctx *pctx, const struct lysp_ext *ext)
+{
+ ly_bool flag = 0, flag2 = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ ly_print_(pctx->out, "%*sextension %s", INDENT, ext->name);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_EXTENSION, 0, ext->exts, &flag);
+
+ if (ext->argname) {
+ ypr_open(pctx->out, &flag);
+ ly_print_(pctx->out, "%*sargument %s", INDENT, ext->argname);
+ LEVEL++;
+ if (ext->exts) {
+ u = -1;
+ while ((u = lysp_ext_instance_iter(ext->exts, u + 1, LY_STMT_ARGUMENT)) != LY_ARRAY_COUNT(ext->exts)) {
+ yprp_extension_instance(pctx, LY_STMT_ARGUMENT, 0, &ext->exts[u], &flag2);
+ }
+ }
+ if ((ext->flags & LYS_YINELEM_MASK) ||
+ (ext->exts && (lysp_ext_instance_iter(ext->exts, 0, LY_STMT_YIN_ELEMENT) != LY_ARRAY_COUNT(ext->exts)))) {
+ ypr_open(pctx->out, &flag2);
+ ypr_substmt(pctx, LY_STMT_YIN_ELEMENT, 0, (ext->flags & LYS_YINELEM_TRUE) ? "true" : "false", ext->exts);
+ }
+ LEVEL--;
+ ypr_close(pctx, flag2);
+ }
+
+ ypr_status(pctx, ext->flags, ext->exts, &flag);
+ ypr_description(pctx, ext->dsc, ext->exts, &flag);
+ ypr_reference(pctx, ext->ref, ext->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_feature(struct lys_ypr_ctx *pctx, const struct lysp_feature *feat)
+{
+ ly_bool flag = 0;
+
+ ly_print_(pctx->out, "%*sfeature %s", INDENT, feat->name);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_FEATURE, 0, feat->exts, &flag);
+ yprp_iffeatures(pctx, feat->iffeatures, feat->exts, &flag);
+ ypr_status(pctx, feat->flags, feat->exts, &flag);
+ ypr_description(pctx, feat->dsc, feat->exts, &flag);
+ ypr_reference(pctx, feat->ref, feat->exts, &flag);
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_identity(struct lys_ypr_ctx *pctx, const struct lysp_ident *ident)
+{
+ ly_bool flag = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ ly_print_(pctx->out, "%*sidentity %s", INDENT, ident->name);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_IDENTITY, 0, ident->exts, &flag);
+ yprp_iffeatures(pctx, ident->iffeatures, ident->exts, &flag);
+
+ LY_ARRAY_FOR(ident->bases, u) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_BASE, u, ident->bases[u], ident->exts);
+ }
+
+ ypr_status(pctx, ident->flags, ident->exts, &flag);
+ ypr_description(pctx, ident->dsc, ident->exts, &flag);
+ ypr_reference(pctx, ident->ref, ident->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_identity(struct lys_ypr_ctx *pctx, const struct lysc_ident *ident)
+{
+ ly_bool flag = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ ly_print_(pctx->out, "%*sidentity %s", INDENT, ident->name);
+ LEVEL++;
+
+ yprc_extension_instances(pctx, LY_STMT_IDENTITY, 0, ident->exts, &flag);
+
+ ypr_open(pctx->out, &flag);
+ if (lys_identity_iffeature_value(ident) == LY_ENOT) {
+ ly_print_(pctx->out, "%*s/* identity \"%s\" is disabled by if-feature(s) */\n", INDENT, ident->name);
+ }
+
+ LY_ARRAY_FOR(ident->derived, u) {
+ if (pctx->module != ident->derived[u]->module) {
+ ly_print_(pctx->out, "%*sderived %s:%s;\n", INDENT, ident->derived[u]->module->prefix, ident->derived[u]->name);
+ } else {
+ ly_print_(pctx->out, "%*sderived %s;\n", INDENT, ident->derived[u]->name);
+ }
+ }
+
+ ypr_status(pctx, ident->flags, ident->exts, &flag);
+ ypr_description(pctx, ident->dsc, ident->exts, &flag);
+ ypr_reference(pctx, ident->ref, ident->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_restr(struct lys_ypr_ctx *pctx, const struct lysp_restr *restr, enum ly_stmt stmt, ly_bool *flag)
+{
+ ly_bool inner_flag = 0, singleline;
+ const char *text;
+
+ if (!restr) {
+ return;
+ }
+
+ ypr_open(pctx->out, flag);
+ text = ((restr->arg.str[0] != LYSP_RESTR_PATTERN_NACK) && (restr->arg.str[0] != LYSP_RESTR_PATTERN_ACK)) ?
+ restr->arg.str : restr->arg.str + 1;
+ singleline = strchr(text, '\n') ? 0 : 1;
+ ypr_text(pctx, lyplg_ext_stmt2str(stmt), text, singleline, 0);
+
+ LEVEL++;
+ yprp_extension_instances(pctx, stmt, 0, restr->exts, &inner_flag);
+ if (restr->arg.str[0] == LYSP_RESTR_PATTERN_NACK) {
+ /* special byte value in pattern's expression: 0x15 - invert-match, 0x06 - match */
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_MODIFIER, 0, "invert-match", restr->exts);
+ }
+ if (restr->emsg) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_MESSAGE, 0, restr->emsg, restr->exts);
+ }
+ if (restr->eapptag) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_APP_TAG, 0, restr->eapptag, restr->exts);
+ }
+ ypr_description(pctx, restr->dsc, restr->exts, &inner_flag);
+ ypr_reference(pctx, restr->ref, restr->exts, &inner_flag);
+
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+}
+
+static void
+yprc_must(struct lys_ypr_ctx *pctx, const struct lysc_must *must, ly_bool *flag)
+{
+ ly_bool inner_flag = 0;
+
+ ypr_open(pctx->out, flag);
+ ly_print_(pctx->out, "%*smust \"", INDENT);
+ ypr_encode(pctx->out, must->cond->expr, -1);
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprc_extension_instances(pctx, LY_STMT_MUST, 0, must->exts, &inner_flag);
+ if (must->emsg) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_MESSAGE, 0, must->emsg, must->exts);
+ }
+ if (must->eapptag) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_APP_TAG, 0, must->eapptag, must->exts);
+ }
+ ypr_description(pctx, must->dsc, must->exts, &inner_flag);
+ ypr_reference(pctx, must->ref, must->exts, &inner_flag);
+
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+}
+
+static void
+yprc_range(struct lys_ypr_ctx *pctx, const struct lysc_range *range, LY_DATA_TYPE basetype, ly_bool *flag)
+{
+ ly_bool inner_flag = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!range) {
+ return;
+ }
+
+ ypr_open(pctx->out, flag);
+ ly_print_(pctx->out, "%*s%s \"", INDENT, (basetype == LY_TYPE_STRING || basetype == LY_TYPE_BINARY) ? "length" : "range");
+ LY_ARRAY_FOR(range->parts, u) {
+ if (u > 0) {
+ ly_print_(pctx->out, " | ");
+ }
+ if (range->parts[u].max_64 == range->parts[u].min_64) {
+ if (basetype <= LY_TYPE_STRING) { /* unsigned values */
+ ly_print_(pctx->out, "%" PRIu64, range->parts[u].max_u64);
+ } else { /* signed values */
+ ly_print_(pctx->out, "%" PRId64, range->parts[u].max_64);
+ }
+ } else {
+ if (basetype <= LY_TYPE_STRING) { /* unsigned values */
+ ly_print_(pctx->out, "%" PRIu64 "..%" PRIu64, range->parts[u].min_u64, range->parts[u].max_u64);
+ } else { /* signed values */
+ ly_print_(pctx->out, "%" PRId64 "..%" PRId64, range->parts[u].min_64, range->parts[u].max_64);
+ }
+ }
+ }
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprc_extension_instances(pctx, LY_STMT_RANGE, 0, range->exts, &inner_flag);
+ if (range->emsg) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_MESSAGE, 0, range->emsg, range->exts);
+ }
+ if (range->eapptag) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_APP_TAG, 0, range->eapptag, range->exts);
+ }
+ ypr_description(pctx, range->dsc, range->exts, &inner_flag);
+ ypr_reference(pctx, range->ref, range->exts, &inner_flag);
+
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+}
+
+static void
+yprc_pattern(struct lys_ypr_ctx *pctx, const struct lysc_pattern *pattern, ly_bool *flag)
+{
+ ly_bool inner_flag = 0;
+
+ ypr_open(pctx->out, flag);
+ ly_print_(pctx->out, "%*spattern \"", INDENT);
+ ypr_encode(pctx->out, pattern->expr, -1);
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprc_extension_instances(pctx, LY_STMT_PATTERN, 0, pattern->exts, &inner_flag);
+ if (pattern->inverted) {
+ /* special byte value in pattern's expression: 0x15 - invert-match, 0x06 - match */
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_MODIFIER, 0, "invert-match", pattern->exts);
+ }
+ if (pattern->emsg) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_MESSAGE, 0, pattern->emsg, pattern->exts);
+ }
+ if (pattern->eapptag) {
+ ypr_open(pctx->out, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_APP_TAG, 0, pattern->eapptag, pattern->exts);
+ }
+ ypr_description(pctx, pattern->dsc, pattern->exts, &inner_flag);
+ ypr_reference(pctx, pattern->ref, pattern->exts, &inner_flag);
+
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+}
+
+static void
+yprc_bits_enum(struct lys_ypr_ctx *pctx, const struct lysc_type_bitenum_item *items, LY_DATA_TYPE basetype, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lysc_type_bitenum_item *item;
+ ly_bool inner_flag;
+
+ assert((basetype == LY_TYPE_BITS) || (basetype == LY_TYPE_ENUM));
+
+ LY_ARRAY_FOR(items, u) {
+ item = &items[u];
+ inner_flag = 0;
+
+ ypr_open(pctx->out, flag);
+ ly_print_(pctx->out, "%*s%s \"", INDENT, basetype == LY_TYPE_BITS ? "bit" : "enum");
+ ypr_encode(pctx->out, item->name, -1);
+ ly_print_(pctx->out, "\"");
+ LEVEL++;
+ if (basetype == LY_TYPE_BITS) {
+ yprc_extension_instances(pctx, LY_STMT_BIT, 0, item->exts, &inner_flag);
+ ypr_unsigned(pctx, LY_STMT_POSITION, 0, item->exts, item->position, &inner_flag);
+ } else { /* LY_TYPE_ENUM */
+ yprc_extension_instances(pctx, LY_STMT_ENUM, 0, item->exts, &inner_flag);
+ ypr_signed(pctx, LY_STMT_VALUE, 0, item->exts, item->value, &inner_flag);
+ }
+ ypr_status(pctx, item->flags, item->exts, &inner_flag);
+ ypr_description(pctx, item->dsc, item->exts, &inner_flag);
+ ypr_reference(pctx, item->ref, item->exts, &inner_flag);
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+ }
+}
+
+static void
+yprp_when(struct lys_ypr_ctx *pctx, const struct lysp_when *when, ly_bool *flag)
+{
+ ly_bool inner_flag = 0;
+
+ if (!when) {
+ return;
+ }
+ ypr_open(pctx->out, flag);
+
+ ly_print_(pctx->out, "%*swhen \"", INDENT);
+ ypr_encode(pctx->out, when->cond, -1);
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
+ ypr_description(pctx, when->dsc, when->exts, &inner_flag);
+ ypr_reference(pctx, when->ref, when->exts, &inner_flag);
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+}
+
+static void
+yprc_when(struct lys_ypr_ctx *pctx, const struct lysc_when *when, ly_bool *flag)
+{
+ ly_bool inner_flag = 0;
+
+ if (!when) {
+ return;
+ }
+ ypr_open(pctx->out, flag);
+
+ ly_print_(pctx->out, "%*swhen \"", INDENT);
+ ypr_encode(pctx->out, when->cond->expr, -1);
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprc_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
+ ypr_description(pctx, when->dsc, when->exts, &inner_flag);
+ ypr_reference(pctx, when->ref, when->exts, &inner_flag);
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+}
+
+static void
+yprp_bits_enum(struct lys_ypr_ctx *pctx, const struct lysp_type_enum *items, LY_DATA_TYPE type, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool inner_flag;
+
+ LY_ARRAY_FOR(items, u) {
+ ypr_open(pctx->out, flag);
+ if (type == LY_TYPE_BITS) {
+ ly_print_(pctx->out, "%*sbit %s", INDENT, items[u].name);
+ } else { /* LY_TYPE_ENUM */
+ ly_print_(pctx->out, "%*senum \"", INDENT);
+ ypr_encode(pctx->out, items[u].name, -1);
+ ly_print_(pctx->out, "\"");
+ }
+ inner_flag = 0;
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_ENUM, 0, items[u].exts, &inner_flag);
+ yprp_iffeatures(pctx, items[u].iffeatures, items[u].exts, &inner_flag);
+ if (items[u].flags & LYS_SET_VALUE) {
+ if (type == LY_TYPE_BITS) {
+ ypr_unsigned(pctx, LY_STMT_POSITION, 0, items[u].exts, items[u].value, &inner_flag);
+ } else { /* LY_TYPE_ENUM */
+ ypr_signed(pctx, LY_STMT_VALUE, 0, items[u].exts, items[u].value, &inner_flag);
+ }
+ }
+ ypr_status(pctx, items[u].flags, items[u].exts, &inner_flag);
+ ypr_description(pctx, items[u].dsc, items[u].exts, &inner_flag);
+ ypr_reference(pctx, items[u].ref, items[u].exts, &inner_flag);
+ LEVEL--;
+ ypr_close(pctx, inner_flag);
+ }
+}
+
+static void
+yprp_type(struct lys_ypr_ctx *pctx, const struct lysp_type *type)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+
+ ly_print_(pctx->out, "%*stype %s", INDENT, type->name);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_TYPE, 0, type->exts, &flag);
+
+ yprp_restr(pctx, type->range, LY_STMT_RANGE, &flag);
+ yprp_restr(pctx, type->length, LY_STMT_LENGTH, &flag);
+ LY_ARRAY_FOR(type->patterns, u) {
+ yprp_restr(pctx, &type->patterns[u], LY_STMT_PATTERN, &flag);
+ }
+ yprp_bits_enum(pctx, type->bits, LY_TYPE_BITS, &flag);
+ yprp_bits_enum(pctx, type->enums, LY_TYPE_ENUM, &flag);
+
+ if (type->path) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_PATH, 0, type->path->expr, type->exts);
+ }
+ if (type->flags & LYS_SET_REQINST) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_REQUIRE_INSTANCE, 0, type->require_instance ? "true" : "false", type->exts);
+ }
+ if (type->flags & LYS_SET_FRDIGITS) {
+ ypr_unsigned(pctx, LY_STMT_FRACTION_DIGITS, 0, type->exts, type->fraction_digits, &flag);
+ }
+ LY_ARRAY_FOR(type->bases, u) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_BASE, u, type->bases[u], type->exts);
+ }
+ LY_ARRAY_FOR(type->types, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_type(pctx, &type->types[u]);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_dflt_value(struct lys_ypr_ctx *pctx, const struct ly_ctx *ly_pctx, const struct lyd_value *value,
+ struct lysc_ext_instance *exts)
+{
+ ly_bool dynamic;
+ const char *str;
+
+ str = value->realtype->plugin->print(ly_pctx, value, LY_VALUE_JSON, NULL, &dynamic, NULL);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, str, exts);
+ if (dynamic) {
+ free((void *)str);
+ }
+}
+
+static void
+yprc_type(struct lys_ypr_ctx *pctx, const struct lysc_type *type)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+
+ ly_print_(pctx->out, "%*stype %s", INDENT, lys_datatype2str(type->basetype));
+ LEVEL++;
+
+ yprc_extension_instances(pctx, LY_STMT_TYPE, 0, type->exts, &flag);
+
+ switch (type->basetype) {
+ case LY_TYPE_BINARY: {
+ struct lysc_type_bin *bin = (struct lysc_type_bin *)type;
+
+ yprc_range(pctx, bin->length, type->basetype, &flag);
+ break;
+ }
+ case LY_TYPE_UINT8:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_UINT64:
+ case LY_TYPE_INT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_INT64: {
+ struct lysc_type_num *num = (struct lysc_type_num *)type;
+
+ yprc_range(pctx, num->range, type->basetype, &flag);
+ break;
+ }
+ case LY_TYPE_STRING: {
+ struct lysc_type_str *str = (struct lysc_type_str *)type;
+
+ yprc_range(pctx, str->length, type->basetype, &flag);
+ LY_ARRAY_FOR(str->patterns, u) {
+ yprc_pattern(pctx, str->patterns[u], &flag);
+ }
+ break;
+ }
+ case LY_TYPE_BITS:
+ case LY_TYPE_ENUM: {
+ /* bits and enums structures are compatible */
+ struct lysc_type_bits *bits = (struct lysc_type_bits *)type;
+
+ yprc_bits_enum(pctx, bits->bits, type->basetype, &flag);
+ break;
+ }
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ /* nothing to do */
+ break;
+ case LY_TYPE_DEC64: {
+ struct lysc_type_dec *dec = (struct lysc_type_dec *)type;
+
+ ypr_open(pctx->out, &flag);
+ ypr_unsigned(pctx, LY_STMT_FRACTION_DIGITS, 0, type->exts, dec->fraction_digits, &flag);
+ yprc_range(pctx, dec->range, dec->basetype, &flag);
+ break;
+ }
+ case LY_TYPE_IDENT: {
+ struct lysc_type_identityref *ident = (struct lysc_type_identityref *)type;
+
+ LY_ARRAY_FOR(ident->bases, u) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_BASE, u, ident->bases[u]->name, type->exts);
+ }
+ break;
+ }
+ case LY_TYPE_INST: {
+ struct lysc_type_instanceid *inst = (struct lysc_type_instanceid *)type;
+
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_REQUIRE_INSTANCE, 0, inst->require_instance ? "true" : "false", inst->exts);
+ break;
+ }
+ case LY_TYPE_LEAFREF: {
+ struct lysc_type_leafref *lr = (struct lysc_type_leafref *)type;
+
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_PATH, 0, lr->path->expr, lr->exts);
+ ypr_substmt(pctx, LY_STMT_REQUIRE_INSTANCE, 0, lr->require_instance ? "true" : "false", lr->exts);
+ yprc_type(pctx, lr->realtype);
+ break;
+ }
+ case LY_TYPE_UNION: {
+ struct lysc_type_union *un = (struct lysc_type_union *)type;
+
+ LY_ARRAY_FOR(un->types, u) {
+ ypr_open(pctx->out, &flag);
+ yprc_type(pctx, un->types[u]);
+ }
+ break;
+ }
+ default:
+ LOGINT(pctx->module->ctx);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_typedef(struct lys_ypr_ctx *pctx, const struct lysp_tpdf *tpdf)
+{
+ ly_print_(pctx->out, "%*stypedef %s {\n", INDENT, tpdf->name);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_TYPEDEF, 0, tpdf->exts, NULL);
+
+ yprp_type(pctx, &tpdf->type);
+
+ if (tpdf->units) {
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, tpdf->units, tpdf->exts);
+ }
+ if (tpdf->dflt.str) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, tpdf->dflt.str, tpdf->exts);
+ }
+
+ ypr_status(pctx, tpdf->flags, tpdf->exts, NULL);
+ ypr_description(pctx, tpdf->dsc, tpdf->exts, NULL);
+ ypr_reference(pctx, tpdf->ref, tpdf->exts, NULL);
+
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+}
+
+static void yprp_node(struct lys_ypr_ctx *pctx, const struct lysp_node *node);
+static void yprc_node(struct lys_ypr_ctx *pctx, const struct lysc_node *node);
+static void yprp_action(struct lys_ypr_ctx *pctx, const struct lysp_node_action *action);
+static void yprp_notification(struct lys_ypr_ctx *pctx, const struct lysp_node_notif *notif);
+
+static void
+yprp_grouping(struct lys_ypr_ctx *pctx, const struct lysp_node_grp *grp)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node *data;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *subgrp;
+
+ ly_print_(pctx->out, "%*sgrouping %s", INDENT, grp->name);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_GROUPING, 0, grp->exts, &flag);
+ ypr_status(pctx, grp->flags, grp->exts, &flag);
+ ypr_description(pctx, grp->dsc, grp->exts, &flag);
+ ypr_reference(pctx, grp->ref, grp->exts, &flag);
+
+ LY_ARRAY_FOR(grp->typedefs, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_typedef(pctx, &grp->typedefs[u]);
+ }
+
+ LY_LIST_FOR(grp->groupings, subgrp) {
+ ypr_open(pctx->out, &flag);
+ yprp_grouping(pctx, subgrp);
+ }
+
+ LY_LIST_FOR(grp->child, data) {
+ ypr_open(pctx->out, &flag);
+ yprp_node(pctx, data);
+ }
+
+ LY_LIST_FOR(grp->actions, action) {
+ ypr_open(pctx->out, &flag);
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(grp->notifs, notif) {
+ ypr_open(pctx->out, &flag);
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_inout(struct lys_ypr_ctx *pctx, const struct lysp_node_action_inout *inout, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node *data;
+ struct lysp_node_grp *grp;
+
+ if (!inout->child) {
+ /* no children */
+ return;
+ }
+ ypr_open(pctx->out, flag);
+ YPR_EXTRA_LINE_PRINT(pctx);
+
+ ly_print_(pctx->out, "%*s%s {\n", INDENT, inout->name);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_MUST, 0, inout->exts, NULL);
+ LY_ARRAY_FOR(inout->musts, u) {
+ yprp_restr(pctx, &inout->musts[u], LY_STMT_MUST, NULL);
+ }
+ LY_ARRAY_FOR(inout->typedefs, u) {
+ yprp_typedef(pctx, &inout->typedefs[u]);
+ }
+ LY_LIST_FOR(inout->groupings, grp) {
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(inout->child, data) {
+ yprp_node(pctx, data);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, 1);
+}
+
+static void
+yprc_inout(struct lys_ypr_ctx *pctx, const struct lysc_node_action_inout *inout, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_node *data;
+
+ if (!inout->child) {
+ /* input/output is empty */
+ return;
+ }
+ ypr_open(pctx->out, flag);
+
+ ly_print_(pctx->out, "\n%*s%s {\n", INDENT, inout->name);
+ LEVEL++;
+
+ yprc_extension_instances(pctx, lyplg_ext_nodetype2stmt(inout->nodetype), 0, inout->exts, NULL);
+ LY_ARRAY_FOR(inout->musts, u) {
+ yprc_must(pctx, &inout->musts[u], NULL);
+ }
+
+ if (!(pctx->options & LYS_PRINT_NO_SUBSTMT)) {
+ LY_LIST_FOR(inout->child, data) {
+ yprc_node(pctx, data);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, 1);
+}
+
+static void
+yprp_notification(struct lys_ypr_ctx *pctx, const struct lysp_node_notif *notif)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node *data;
+ struct lysp_node_grp *grp;
+
+ ly_print_(pctx->out, "%*snotification %s", INDENT, notif->name);
+
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_NOTIFICATION, 0, notif->exts, &flag);
+ yprp_iffeatures(pctx, notif->iffeatures, notif->exts, &flag);
+
+ LY_ARRAY_FOR(notif->musts, u) {
+ yprp_restr(pctx, &notif->musts[u], LY_STMT_MUST, &flag);
+ }
+ ypr_status(pctx, notif->flags, notif->exts, &flag);
+ ypr_description(pctx, notif->dsc, notif->exts, &flag);
+ ypr_reference(pctx, notif->ref, notif->exts, &flag);
+
+ LY_ARRAY_FOR(notif->typedefs, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_typedef(pctx, &notif->typedefs[u]);
+ }
+
+ LY_LIST_FOR(notif->groupings, grp) {
+ ypr_open(pctx->out, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(notif->child, data) {
+ ypr_open(pctx->out, &flag);
+ yprp_node(pctx, data);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_notification(struct lys_ypr_ctx *pctx, const struct lysc_node_notif *notif)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysc_node *data;
+
+ ly_print_(pctx->out, "%*snotification %s", INDENT, notif->name);
+
+ LEVEL++;
+ yprc_extension_instances(pctx, LY_STMT_NOTIFICATION, 0, notif->exts, &flag);
+
+ LY_ARRAY_FOR(notif->musts, u) {
+ yprc_must(pctx, &notif->musts[u], &flag);
+ }
+ ypr_status(pctx, notif->flags, notif->exts, &flag);
+ ypr_description(pctx, notif->dsc, notif->exts, &flag);
+ ypr_reference(pctx, notif->ref, notif->exts, &flag);
+
+ if (!(pctx->options & LYS_PRINT_NO_SUBSTMT)) {
+ LY_LIST_FOR(notif->child, data) {
+ ypr_open(pctx->out, &flag);
+ yprc_node(pctx, data);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_action(struct lys_ypr_ctx *pctx, const struct lysp_node_action *action)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node_grp *grp;
+
+ ly_print_(pctx->out, "%*s%s %s", INDENT, action->parent ? "action" : "rpc", action->name);
+
+ LEVEL++;
+ yprp_extension_instances(pctx, lyplg_ext_nodetype2stmt(action->nodetype), 0, action->exts, &flag);
+ yprp_iffeatures(pctx, action->iffeatures, action->exts, &flag);
+ ypr_status(pctx, action->flags, action->exts, &flag);
+ ypr_description(pctx, action->dsc, action->exts, &flag);
+ ypr_reference(pctx, action->ref, action->exts, &flag);
+
+ YPR_EXTRA_LINE(flag, pctx);
+
+ LY_ARRAY_FOR(action->typedefs, u) {
+ ypr_open(pctx->out, &flag);
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_typedef(pctx, &action->typedefs[u]);
+ }
+
+ YPR_EXTRA_LINE(action->typedefs, pctx);
+
+ LY_LIST_FOR(action->groupings, grp) {
+ ypr_open(pctx->out, &flag);
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_grouping(pctx, grp);
+ }
+
+ YPR_EXTRA_LINE(action->groupings, pctx);
+
+ yprp_inout(pctx, &action->input, &flag);
+ yprp_inout(pctx, &action->output, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_action(struct lys_ypr_ctx *pctx, const struct lysc_node_action *action)
+{
+ ly_bool flag = 0;
+
+ ly_print_(pctx->out, "%*s%s %s", INDENT, action->parent ? "action" : "rpc", action->name);
+
+ LEVEL++;
+ yprc_extension_instances(pctx, lyplg_ext_nodetype2stmt(action->nodetype), 0, action->exts, &flag);
+ ypr_status(pctx, action->flags, action->exts, &flag);
+ ypr_description(pctx, action->dsc, action->exts, &flag);
+ ypr_reference(pctx, action->ref, action->exts, &flag);
+
+ yprc_inout(pctx, &action->input, &flag);
+ yprc_inout(pctx, &action->output, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_node_common1(struct lys_ypr_ctx *pctx, const struct lysp_node *node, ly_bool *flag)
+{
+ ly_print_(pctx->out, "%*s%s %s%s", INDENT, lys_nodetype2str(node->nodetype), node->name, flag ? "" : " {\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, lyplg_ext_nodetype2stmt(node->nodetype), 0, node->exts, flag);
+ yprp_when(pctx, lysp_node_when(node), flag);
+ yprp_iffeatures(pctx, node->iffeatures, node->exts, flag);
+}
+
+static void
+yprc_node_common1(struct lys_ypr_ctx *pctx, const struct lysc_node *node, ly_bool *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_when **when;
+
+ ly_print_(pctx->out, "%*s%s %s%s", INDENT, lys_nodetype2str(node->nodetype), node->name, flag ? "" : " {\n");
+ LEVEL++;
+
+ yprc_extension_instances(pctx, lyplg_ext_nodetype2stmt(node->nodetype), 0, node->exts, flag);
+
+ when = lysc_node_when(node);
+ LY_ARRAY_FOR(when, u) {
+ yprc_when(pctx, when[u], flag);
+ }
+}
+
+/* macro to unify the code */
+#define YPR_NODE_COMMON2 \
+ ypr_config(pctx, node->flags, node->exts, flag); \
+ if (node->nodetype & (LYS_CHOICE | LYS_LEAF | LYS_ANYDATA)) { \
+ ypr_mandatory(pctx, node->flags, node->exts, flag); \
+ } \
+ ypr_status(pctx, node->flags, node->exts, flag); \
+ ypr_description(pctx, node->dsc, node->exts, flag); \
+ ypr_reference(pctx, node->ref, node->exts, flag)
+
+static void
+yprp_node_common2(struct lys_ypr_ctx *pctx, const struct lysp_node *node, ly_bool *flag)
+{
+ YPR_NODE_COMMON2;
+}
+
+static void
+yprc_node_common2(struct lys_ypr_ctx *pctx, const struct lysc_node *node, ly_bool *flag)
+{
+ YPR_NODE_COMMON2;
+}
+
+#undef YPR_NODE_COMMON2
+
+static void
+yprp_container(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *grp;
+ struct lysp_node_container *cont = (struct lysp_node_container *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(cont->musts, u) {
+ yprp_restr(pctx, &cont->musts[u], LY_STMT_MUST, &flag);
+ }
+ if (cont->presence) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_PRESENCE, 0, cont->presence, cont->exts);
+ }
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_ARRAY_FOR(cont->typedefs, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_typedef(pctx, &cont->typedefs[u]);
+ }
+
+ LY_LIST_FOR(cont->groupings, grp) {
+ ypr_open(pctx->out, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(cont->child, child) {
+ ypr_open(pctx->out, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LY_LIST_FOR(cont->actions, action) {
+ ypr_open(pctx->out, &flag);
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(cont->notifs, notif) {
+ ypr_open(pctx->out, &flag);
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_container(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysc_node *child;
+ struct lysc_node_action *action;
+ struct lysc_node_notif *notif;
+ struct lysc_node_container *cont = (struct lysc_node_container *)node;
+
+ yprc_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(cont->musts, u) {
+ yprc_must(pctx, &cont->musts[u], &flag);
+ }
+ if (cont->flags & LYS_PRESENCE) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_PRESENCE, 0, "true", cont->exts);
+ }
+
+ yprc_node_common2(pctx, node, &flag);
+
+ if (!(pctx->options & LYS_PRINT_NO_SUBSTMT)) {
+ LY_LIST_FOR(cont->child, child) {
+ ypr_open(pctx->out, &flag);
+ yprc_node(pctx, child);
+ }
+
+ LY_LIST_FOR(cont->actions, action) {
+ ypr_open(pctx->out, &flag);
+ yprc_action(pctx, action);
+ }
+
+ LY_LIST_FOR(cont->notifs, notif) {
+ ypr_open(pctx->out, &flag);
+ yprc_notification(pctx, notif);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_case(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ ly_bool flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_case *cas = (struct lysp_node_case *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_LIST_FOR(cas->child, child) {
+ ypr_open(pctx->out, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_case(struct lys_ypr_ctx *pctx, const struct lysc_node_case *cs)
+{
+ ly_bool flag = 0;
+ struct lysc_node *child;
+
+ yprc_node_common1(pctx, &cs->node, &flag);
+ yprc_node_common2(pctx, &cs->node, &flag);
+
+ if (!(pctx->options & LYS_PRINT_NO_SUBSTMT)) {
+ for (child = cs->child; child && child->parent == (struct lysc_node *)cs; child = child->next) {
+ ypr_open(pctx->out, &flag);
+ yprc_node(pctx, child);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_choice(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ ly_bool flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_choice *choice = (struct lysp_node_choice *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ if (choice->dflt.str) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, choice->dflt.str, choice->exts);
+ }
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_LIST_FOR(choice->child, child) {
+ ypr_open(pctx->out, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_choice(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ ly_bool flag = 0;
+ struct lysc_node_case *cs;
+ struct lysc_node_choice *choice = (struct lysc_node_choice *)node;
+
+ yprc_node_common1(pctx, node, &flag);
+
+ if (choice->dflt) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, choice->dflt->name, choice->exts);
+ }
+
+ yprc_node_common2(pctx, node, &flag);
+
+ for (cs = choice->cases; cs; cs = (struct lysc_node_case *)cs->next) {
+ ypr_open(pctx->out, &flag);
+ yprc_case(pctx, cs);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_leaf(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node_leaf *leaf = (struct lysp_node_leaf *)node;
+
+ yprp_node_common1(pctx, node, NULL);
+
+ yprp_type(pctx, &leaf->type);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, leaf->units, leaf->exts);
+ LY_ARRAY_FOR(leaf->musts, u) {
+ yprp_restr(pctx, &leaf->musts[u], LY_STMT_MUST, NULL);
+ }
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, leaf->dflt.str, leaf->exts);
+
+ yprp_node_common2(pctx, node, NULL);
+
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprc_leaf(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_node_leaf *leaf = (struct lysc_node_leaf *)node;
+
+ yprc_node_common1(pctx, node, NULL);
+
+ yprc_type(pctx, leaf->type);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, leaf->units, leaf->exts);
+ LY_ARRAY_FOR(leaf->musts, u) {
+ yprc_must(pctx, &leaf->musts[u], NULL);
+ }
+
+ if (leaf->dflt) {
+ yprc_dflt_value(pctx, node->module->ctx, leaf->dflt, leaf->exts);
+ }
+
+ yprc_node_common2(pctx, node, NULL);
+
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprp_leaflist(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node_leaflist *llist = (struct lysp_node_leaflist *)node;
+
+ yprp_node_common1(pctx, node, NULL);
+
+ yprp_type(pctx, &llist->type);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, llist->units, llist->exts);
+ LY_ARRAY_FOR(llist->musts, u) {
+ yprp_restr(pctx, &llist->musts[u], LY_STMT_MUST, NULL);
+ }
+ LY_ARRAY_FOR(llist->dflts, u) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, llist->dflts[u].str, llist->exts);
+ }
+
+ ypr_config(pctx, node->flags, node->exts, NULL);
+
+ if (llist->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, llist->exts, llist->min, NULL);
+ }
+ if (llist->flags & LYS_SET_MAX) {
+ if (llist->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, llist->exts, llist->max, NULL);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", llist->exts);
+ }
+ }
+
+ if (llist->flags & LYS_ORDBY_MASK) {
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0, (llist->flags & LYS_ORDBY_USER) ? "user" : "system", llist->exts);
+ }
+
+ ypr_status(pctx, node->flags, node->exts, NULL);
+ ypr_description(pctx, node->dsc, node->exts, NULL);
+ ypr_reference(pctx, node->ref, node->exts, NULL);
+
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprc_leaflist(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_node_leaflist *llist = (struct lysc_node_leaflist *)node;
+
+ yprc_node_common1(pctx, node, NULL);
+
+ yprc_type(pctx, llist->type);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, llist->units, llist->exts);
+ LY_ARRAY_FOR(llist->musts, u) {
+ yprc_must(pctx, &llist->musts[u], NULL);
+ }
+ LY_ARRAY_FOR(llist->dflts, u) {
+ yprc_dflt_value(pctx, node->module->ctx, llist->dflts[u], llist->exts);
+ }
+
+ ypr_config(pctx, node->flags, node->exts, NULL);
+
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, llist->exts, llist->min, NULL);
+ if (llist->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, llist->exts, llist->max, NULL);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", llist->exts);
+ }
+
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0, (llist->flags & LYS_ORDBY_USER) ? "user" : "system", llist->exts);
+
+ ypr_status(pctx, node->flags, node->exts, NULL);
+ ypr_description(pctx, node->dsc, node->exts, NULL);
+ ypr_reference(pctx, node->ref, node->exts, NULL);
+
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprp_list(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *grp;
+ struct lysp_node_list *list = (struct lysp_node_list *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(list->musts, u) {
+ yprp_restr(pctx, &list->musts[u], LY_STMT_MUST, &flag);
+ }
+ if (list->key) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_KEY, 0, list->key, list->exts);
+ }
+ LY_ARRAY_FOR(list->uniques, u) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_UNIQUE, u, list->uniques[u].str, list->exts);
+ }
+
+ ypr_config(pctx, node->flags, node->exts, &flag);
+
+ if (list->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, list->exts, list->min, &flag);
+ }
+ if (list->flags & LYS_SET_MAX) {
+ if (list->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, list->exts, list->max, &flag);
+ } else {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", list->exts);
+ }
+ }
+
+ if (list->flags & LYS_ORDBY_MASK) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0, (list->flags & LYS_ORDBY_USER) ? "user" : "system", list->exts);
+ }
+
+ ypr_status(pctx, node->flags, node->exts, &flag);
+ ypr_description(pctx, node->dsc, node->exts, &flag);
+ ypr_reference(pctx, node->ref, node->exts, &flag);
+
+ LY_ARRAY_FOR(list->typedefs, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_typedef(pctx, &list->typedefs[u]);
+ }
+
+ LY_LIST_FOR(list->groupings, grp) {
+ ypr_open(pctx->out, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(list->child, child) {
+ ypr_open(pctx->out, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LY_LIST_FOR(list->actions, action) {
+ ypr_open(pctx->out, &flag);
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(list->notifs, notif) {
+ ypr_open(pctx->out, &flag);
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_list(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysc_node_list *list = (struct lysc_node_list *)node;
+
+ yprc_node_common1(pctx, node, NULL);
+
+ LY_ARRAY_FOR(list->musts, u) {
+ yprc_must(pctx, &list->musts[u], NULL);
+ }
+ if (!(list->flags & LYS_KEYLESS)) {
+ ly_print_(pctx->out, "%*skey \"", INDENT);
+ for (struct lysc_node *key = list->child; key && key->nodetype == LYS_LEAF && (key->flags & LYS_KEY); key = key->next) {
+ ly_print_(pctx->out, "%s%s", u > 0 ? ", " : "", key->name);
+ }
+ ly_print_(pctx->out, "\";\n");
+ }
+ LY_ARRAY_FOR(list->uniques, u) {
+ ly_print_(pctx->out, "%*sunique \"", INDENT);
+ LY_ARRAY_FOR(list->uniques[u], v) {
+ ly_print_(pctx->out, "%s%s", v > 0 ? ", " : "", list->uniques[u][v]->name);
+ }
+ ypr_close(pctx, 0);
+ }
+
+ ypr_config(pctx, node->flags, node->exts, NULL);
+
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, list->exts, list->min, NULL);
+ if (list->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, list->exts, list->max, NULL);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", list->exts);
+ }
+
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0, (list->flags & LYS_ORDBY_USER) ? "user" : "system", list->exts);
+
+ ypr_status(pctx, node->flags, node->exts, NULL);
+ ypr_description(pctx, node->dsc, node->exts, NULL);
+ ypr_reference(pctx, node->ref, node->exts, NULL);
+
+ if (!(pctx->options & LYS_PRINT_NO_SUBSTMT)) {
+ struct lysc_node *child;
+ struct lysc_node_action *action;
+ struct lysc_node_notif *notif;
+
+ LY_LIST_FOR(list->child, child) {
+ yprc_node(pctx, child);
+ }
+
+ LY_LIST_FOR(list->actions, action) {
+ yprc_action(pctx, action);
+ }
+
+ LY_LIST_FOR(list->notifs, notif) {
+ yprc_notification(pctx, notif);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, 1);
+}
+
+static void
+yprp_refine(struct lys_ypr_ctx *pctx, struct lysp_refine *refine)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+
+ ly_print_(pctx->out, "%*srefine \"%s\"", INDENT, refine->nodeid);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_REFINE, 0, refine->exts, &flag);
+ yprp_iffeatures(pctx, refine->iffeatures, refine->exts, &flag);
+
+ LY_ARRAY_FOR(refine->musts, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_restr(pctx, &refine->musts[u], LY_STMT_MUST, NULL);
+ }
+
+ if (refine->presence) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_PRESENCE, 0, refine->presence, refine->exts);
+ }
+
+ LY_ARRAY_FOR(refine->dflts, u) {
+ ypr_open(pctx->out, &flag);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, refine->dflts[u].str, refine->exts);
+ }
+
+ ypr_config(pctx, refine->flags, refine->exts, &flag);
+ ypr_mandatory(pctx, refine->flags, refine->exts, &flag);
+
+ if (refine->flags & LYS_SET_MIN) {
+ ypr_open(pctx->out, &flag);
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, refine->exts, refine->min, NULL);
+ }
+ if (refine->flags & LYS_SET_MAX) {
+ ypr_open(pctx->out, &flag);
+ if (refine->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, refine->exts, refine->max, NULL);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", refine->exts);
+ }
+ }
+
+ ypr_description(pctx, refine->dsc, refine->exts, &flag);
+ ypr_reference(pctx, refine->ref, refine->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_augment(struct lys_ypr_ctx *pctx, const struct lysp_node_augment *aug)
+{
+ struct lysp_node *child;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+
+ ly_print_(pctx->out, "%*saugment \"%s\" {\n", INDENT, aug->nodeid);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_AUGMENT, 0, aug->exts, NULL);
+ yprp_when(pctx, aug->when, NULL);
+ yprp_iffeatures(pctx, aug->iffeatures, aug->exts, NULL);
+ ypr_status(pctx, aug->flags, aug->exts, NULL);
+ ypr_description(pctx, aug->dsc, aug->exts, NULL);
+ ypr_reference(pctx, aug->ref, aug->exts, NULL);
+
+ LY_LIST_FOR(aug->child, child) {
+ yprp_node(pctx, child);
+ }
+
+ LY_LIST_FOR(aug->actions, action) {
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(aug->notifs, notif) {
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, 1);
+}
+
+static void
+yprp_uses(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node_uses *uses = (struct lysp_node_uses *)node;
+ struct lysp_node_augment *aug;
+
+ yprp_node_common1(pctx, node, &flag);
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_ARRAY_FOR(uses->refines, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_refine(pctx, &uses->refines[u]);
+ }
+
+ LY_LIST_FOR(uses->augments, aug) {
+ ypr_open(pctx->out, &flag);
+ yprp_augment(pctx, aug);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_anydata(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysp_node_anydata *any = (struct lysp_node_anydata *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(any->musts, u) {
+ ypr_open(pctx->out, &flag);
+ yprp_restr(pctx, &any->musts[u], LY_STMT_MUST, NULL);
+ }
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprc_anydata(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool flag = 0;
+ struct lysc_node_anydata *any = (struct lysc_node_anydata *)node;
+
+ yprc_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(any->musts, u) {
+ ypr_open(pctx->out, &flag);
+ yprc_must(pctx, &any->musts[u], NULL);
+ }
+
+ yprc_node_common2(pctx, node, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, flag);
+}
+
+static void
+yprp_node(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ yprp_container(pctx, node);
+ break;
+ case LYS_CHOICE:
+ yprp_choice(pctx, node);
+ break;
+ case LYS_LEAF:
+ yprp_leaf(pctx, node);
+ break;
+ case LYS_LEAFLIST:
+ yprp_leaflist(pctx, node);
+ break;
+ case LYS_LIST:
+ yprp_list(pctx, node);
+ break;
+ case LYS_USES:
+ yprp_uses(pctx, node);
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ yprp_anydata(pctx, node);
+ break;
+ case LYS_CASE:
+ yprp_case(pctx, node);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+yprc_node(struct lys_ypr_ctx *pctx, const struct lysc_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ yprc_container(pctx, node);
+ break;
+ case LYS_CHOICE:
+ yprc_choice(pctx, node);
+ break;
+ case LYS_LEAF:
+ yprc_leaf(pctx, node);
+ break;
+ case LYS_LEAFLIST:
+ yprc_leaflist(pctx, node);
+ break;
+ case LYS_LIST:
+ yprc_list(pctx, node);
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ yprc_anydata(pctx, node);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+yprp_deviation(struct lys_ypr_ctx *pctx, const struct lysp_deviation *deviation)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_deviate_add *add;
+ struct lysp_deviate_rpl *rpl;
+ struct lysp_deviate_del *del;
+ struct lysp_deviate *elem;
+
+ ly_print_(pctx->out, "%*sdeviation \"%s\" {\n", INDENT, deviation->nodeid);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATION, 0, deviation->exts, NULL);
+ ypr_description(pctx, deviation->dsc, deviation->exts, NULL);
+ ypr_reference(pctx, deviation->ref, deviation->exts, NULL);
+
+ LY_LIST_FOR(deviation->deviates, elem) {
+ ly_print_(pctx->out, "%*sdeviate ", INDENT);
+ if (elem->mod == LYS_DEV_NOT_SUPPORTED) {
+ if (elem->exts) {
+ ly_print_(pctx->out, "not-supported {\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, elem->exts, NULL);
+ } else {
+ ly_print_(pctx->out, "not-supported;\n");
+ continue;
+ }
+ } else if (elem->mod == LYS_DEV_ADD) {
+ add = (struct lysp_deviate_add *)elem;
+ ly_print_(pctx->out, "add {\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, add->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, add->units, add->exts);
+ LY_ARRAY_FOR(add->musts, u) {
+ yprp_restr(pctx, &add->musts[u], LY_STMT_MUST, NULL);
+ }
+ LY_ARRAY_FOR(add->uniques, u) {
+ ypr_substmt(pctx, LY_STMT_UNIQUE, u, add->uniques[u].str, add->exts);
+ }
+ LY_ARRAY_FOR(add->dflts, u) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, add->dflts[u].str, add->exts);
+ }
+ ypr_config(pctx, add->flags, add->exts, NULL);
+ ypr_mandatory(pctx, add->flags, add->exts, NULL);
+ if (add->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, add->exts, add->min, NULL);
+ }
+ if (add->flags & LYS_SET_MAX) {
+ if (add->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, add->exts, add->max, NULL);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", add->exts);
+ }
+ }
+ } else if (elem->mod == LYS_DEV_REPLACE) {
+ rpl = (struct lysp_deviate_rpl *)elem;
+ ly_print_(pctx->out, "replace {\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, rpl->exts, NULL);
+ if (rpl->type) {
+ yprp_type(pctx, rpl->type);
+ }
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, rpl->units, rpl->exts);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, rpl->dflt.str, rpl->exts);
+ ypr_config(pctx, rpl->flags, rpl->exts, NULL);
+ ypr_mandatory(pctx, rpl->flags, rpl->exts, NULL);
+ if (rpl->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, rpl->exts, rpl->min, NULL);
+ }
+ if (rpl->flags & LYS_SET_MAX) {
+ if (rpl->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, rpl->exts, rpl->max, NULL);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", rpl->exts);
+ }
+ }
+ } else if (elem->mod == LYS_DEV_DELETE) {
+ del = (struct lysp_deviate_del *)elem;
+ ly_print_(pctx->out, "delete {\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, del->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, del->units, del->exts);
+ LY_ARRAY_FOR(del->musts, u) {
+ yprp_restr(pctx, &del->musts[u], LY_STMT_MUST, NULL);
+ }
+ LY_ARRAY_FOR(del->uniques, u) {
+ ypr_substmt(pctx, LY_STMT_UNIQUE, u, del->uniques[u].str, del->exts);
+ }
+ LY_ARRAY_FOR(del->dflts, u) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, del->dflts[u].str, del->exts);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, 1);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, 1);
+}
+
+static void
+yang_print_parsed_linkage(struct lys_ypr_ctx *pctx, const struct lysp_module *modp)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(modp->imports, u) {
+ if (modp->imports[u].flags & LYS_INTERNAL) {
+ continue;
+ }
+
+ YPR_EXTRA_LINE_PRINT(pctx);
+ ly_print_(pctx->out, "%*simport %s {\n", INDENT, modp->imports[u].name);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_IMPORT, 0, modp->imports[u].exts, NULL);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, modp->imports[u].prefix, modp->imports[u].exts);
+ if (modp->imports[u].rev[0]) {
+ ypr_substmt(pctx, LY_STMT_REVISION_DATE, 0, modp->imports[u].rev, modp->imports[u].exts);
+ }
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, modp->imports[u].dsc, modp->imports[u].exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, modp->imports[u].ref, modp->imports[u].exts);
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ }
+ YPR_EXTRA_LINE(modp->imports, pctx);
+
+ LY_ARRAY_FOR(modp->includes, u) {
+ if (modp->includes[u].injected) {
+ /* do not print the includes injected from submodules */
+ continue;
+ }
+ YPR_EXTRA_LINE_PRINT(pctx);
+ if (modp->includes[u].rev[0] || modp->includes[u].dsc || modp->includes[u].ref || modp->includes[u].exts) {
+ ly_print_(pctx->out, "%*sinclude %s {\n", INDENT, modp->includes[u].name);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_INCLUDE, 0, modp->includes[u].exts, NULL);
+ if (modp->includes[u].rev[0]) {
+ ypr_substmt(pctx, LY_STMT_REVISION_DATE, 0, modp->includes[u].rev, modp->includes[u].exts);
+ }
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, modp->includes[u].dsc, modp->includes[u].exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, modp->includes[u].ref, modp->includes[u].exts);
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ } else {
+ ly_print_(pctx->out, "\n%*sinclude \"%s\";\n", INDENT, modp->includes[u].name);
+ }
+ }
+ YPR_EXTRA_LINE(modp->includes, pctx);
+}
+
+static void
+yang_print_parsed_body(struct lys_ypr_ctx *pctx, const struct lysp_module *modp)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node *data;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *grp;
+ struct lysp_node_augment *aug;
+
+ LY_ARRAY_FOR(modp->extensions, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_extension(pctx, &modp->extensions[u]);
+ }
+
+ YPR_EXTRA_LINE(modp->extensions, pctx);
+
+ if (modp->exts) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_extension_instances(pctx, LY_STMT_MODULE, 0, modp->exts, NULL);
+ }
+
+ YPR_EXTRA_LINE(modp->exts, pctx);
+
+ LY_ARRAY_FOR(modp->features, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_feature(pctx, &modp->features[u]);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ YPR_EXTRA_LINE(modp->features, pctx);
+
+ LY_ARRAY_FOR(modp->identities, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_identity(pctx, &modp->identities[u]);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ YPR_EXTRA_LINE(modp->identities, pctx);
+
+ LY_ARRAY_FOR(modp->typedefs, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_typedef(pctx, &modp->typedefs[u]);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ YPR_EXTRA_LINE(modp->typedefs, pctx);
+
+ LY_LIST_FOR(modp->groupings, grp) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_grouping(pctx, grp);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ YPR_EXTRA_LINE(modp->groupings, pctx);
+
+ LY_LIST_FOR(modp->data, data) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_node(pctx, data);
+ }
+
+ YPR_EXTRA_LINE(modp->data, pctx);
+
+ LY_LIST_FOR(modp->augments, aug) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_augment(pctx, aug);
+ }
+
+ YPR_EXTRA_LINE(modp->augments, pctx);
+
+ LY_LIST_FOR(modp->rpcs, action) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_action(pctx, action);
+ }
+
+ YPR_EXTRA_LINE(modp->rpcs, pctx);
+
+ LY_LIST_FOR(modp->notifs, notif) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_notification(pctx, notif);
+ }
+
+ YPR_EXTRA_LINE(modp->notifs, pctx);
+
+ LY_ARRAY_FOR(modp->deviations, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_deviation(pctx, &modp->deviations[u]);
+ }
+
+ YPR_EXTRA_LINE(modp->deviations, pctx);
+}
+
+LY_ERR
+yang_print_parsed_module(struct ly_out *out, const struct lysp_module *modp, uint32_t options)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lys_module *module = modp->mod;
+ struct lys_ypr_ctx pctx_ = {
+ .out = out,
+ .level = 0,
+ .options = options,
+ .module = module,
+ .schema = LYS_YPR_PARSED
+ }, *pctx = &pctx_;
+
+ ly_print_(pctx->out, "%*smodule %s {\n", INDENT, module->name);
+ LEVEL++;
+
+ /* module-header-stmts */
+ if (modp->version) {
+ ypr_substmt(pctx, LY_STMT_YANG_VERSION, 0, modp->version == LYS_VERSION_1_1 ? "1.1" : "1", modp->exts);
+ }
+ ypr_substmt(pctx, LY_STMT_NAMESPACE, 0, module->ns, modp->exts);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, module->prefix, modp->exts);
+
+ YPR_EXTRA_LINE(1, pctx);
+
+ /* linkage-stmts (import/include) */
+ yang_print_parsed_linkage(pctx, modp);
+
+ /* meta-stmts */
+ if (module->org || module->contact || module->dsc || module->ref) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ }
+ ypr_substmt(pctx, LY_STMT_ORGANIZATION, 0, module->org, modp->exts);
+ ypr_substmt(pctx, LY_STMT_CONTACT, 0, module->contact, modp->exts);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, module->dsc, modp->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, module->ref, modp->exts);
+
+ YPR_EXTRA_LINE(module->org || module->contact || module->dsc || module->ref, pctx);
+
+ /* revision-stmts */
+ LY_ARRAY_FOR(modp->revs, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_revision(pctx, &modp->revs[u]);
+ }
+
+ YPR_EXTRA_LINE(modp->revs, pctx);
+
+ /* body-stmts */
+ yang_print_parsed_body(pctx, modp);
+
+ LEVEL--;
+ ly_print_(out, "%*s}\n", INDENT);
+ ly_print_flush(out);
+
+ return LY_SUCCESS;
+}
+
+static void
+yprp_belongsto(struct lys_ypr_ctx *pctx, const struct lysp_submodule *submodp)
+{
+ ly_print_(pctx->out, "%*sbelongs-to %s {\n", INDENT, submodp->mod->name);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_BELONGS_TO, 0, submodp->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, submodp->prefix, submodp->exts);
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+}
+
+LY_ERR
+yang_print_parsed_submodule(struct ly_out *out, const struct lysp_submodule *submodp, uint32_t options)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lys_ypr_ctx pctx_ = {
+ .out = out,
+ .level = 0,
+ .options = options,
+ .module = submodp->mod,
+ .schema = LYS_YPR_PARSED
+ }, *pctx = &pctx_;
+
+ ly_print_(pctx->out, "%*ssubmodule %s {\n", INDENT, submodp->name);
+ LEVEL++;
+
+ /* submodule-header-stmts */
+ if (submodp->version) {
+ ypr_substmt(pctx, LY_STMT_YANG_VERSION, 0, submodp->version == LYS_VERSION_1_1 ? "1.1" : "1", submodp->exts);
+ }
+
+ yprp_belongsto(pctx, submodp);
+
+ YPR_EXTRA_LINE(1, pctx);
+
+ /* linkage-stmts (import/include) */
+ yang_print_parsed_linkage(pctx, (struct lysp_module *)submodp);
+
+ /* meta-stmts */
+ if (submodp->org || submodp->contact || submodp->dsc || submodp->ref) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ }
+ ypr_substmt(pctx, LY_STMT_ORGANIZATION, 0, submodp->org, submodp->exts);
+ ypr_substmt(pctx, LY_STMT_CONTACT, 0, submodp->contact, submodp->exts);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, submodp->dsc, submodp->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, submodp->ref, submodp->exts);
+
+ YPR_EXTRA_LINE(submodp->org || submodp->contact || submodp->dsc || submodp->ref, pctx);
+
+ /* revision-stmts */
+ LY_ARRAY_FOR(submodp->revs, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprp_revision(pctx, &submodp->revs[u]);
+ }
+
+ YPR_EXTRA_LINE(submodp->revs, pctx);
+
+ /* body-stmts */
+ yang_print_parsed_body(pctx, (struct lysp_module *)submodp);
+
+ LEVEL--;
+ ly_print_(out, "%*s}\n", INDENT);
+ ly_print_flush(out);
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+yang_print_compiled_node(struct ly_out *out, const struct lysc_node *node, uint32_t options)
+{
+ struct lys_ypr_ctx pctx_ = {
+ .out = out,
+ .level = 0,
+ .options = options,
+ .module = node->module,
+ .schema = LYS_YPR_COMPILED
+ }, *pctx = &pctx_;
+
+ yprc_node(pctx, node);
+
+ ly_print_flush(out);
+ return LY_SUCCESS;
+}
+
+LY_ERR
+yang_print_compiled(struct ly_out *out, const struct lys_module *module, uint32_t options)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_module *modc = module->compiled;
+ struct lys_ypr_ctx pctx_ = {
+ .out = out,
+ .level = 0,
+ .options = options,
+ .module = module,
+ .schema = LYS_YPR_COMPILED
+ }, *pctx = &pctx_;
+
+ ly_print_(pctx->out, "%*smodule %s {\n", INDENT, module->name);
+ LEVEL++;
+
+ /* module-header-stmts */
+ ypr_substmt(pctx, LY_STMT_NAMESPACE, 0, module->ns, modc->exts);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, module->prefix, modc->exts);
+
+ YPR_EXTRA_LINE(1, pctx);
+
+ /* no linkage-stmts */
+
+ /* meta-stmts */
+ if (module->org || module->contact || module->dsc || module->ref) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ }
+ ypr_substmt(pctx, LY_STMT_ORGANIZATION, 0, module->org, modc->exts);
+ ypr_substmt(pctx, LY_STMT_CONTACT, 0, module->contact, modc->exts);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, module->dsc, modc->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, module->ref, modc->exts);
+
+ YPR_EXTRA_LINE(module->org || module->contact || module->dsc || module->ref, pctx);
+
+ /* revision-stmts */
+ if (module->revision) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ ly_print_(pctx->out, "%*srevision %s;\n", INDENT, module->revision);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ /* body-stmts */
+ if (modc->exts) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprc_extension_instances(pctx, LY_STMT_MODULE, 0, module->compiled->exts, NULL);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ LY_ARRAY_FOR(module->identities, u) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprc_identity(pctx, &module->identities[u]);
+ YPR_EXTRA_LINE(1, pctx);
+ }
+
+ if (!(pctx->options & LYS_PRINT_NO_SUBSTMT)) {
+ struct lysc_node *data;
+ struct lysc_node_action *rpc;
+ struct lysc_node_notif *notif;
+
+ LY_LIST_FOR(modc->data, data) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprc_node(pctx, data);
+ }
+
+ YPR_EXTRA_LINE(modc->data, pctx);
+
+ LY_LIST_FOR(modc->rpcs, rpc) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprc_action(pctx, rpc);
+ }
+
+ YPR_EXTRA_LINE(modc->rpcs, pctx);
+
+ LY_LIST_FOR(modc->notifs, notif) {
+ YPR_EXTRA_LINE_PRINT(pctx);
+ yprc_notification(pctx, notif);
+ }
+
+ YPR_EXTRA_LINE(modc->notifs, pctx);
+ }
+
+ LEVEL--;
+ ly_print_(out, "%*s}\n", INDENT);
+ ly_print_flush(out);
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF void
+lyplg_ext_print_info_extension_instance(struct lyspr_ctx *ctx, const struct lysc_ext_instance *ext, ly_bool *flag)
+{
+ struct lys_ypr_ctx *pctx = (struct lys_ypr_ctx *)ctx;
+ LY_ARRAY_COUNT_TYPE u, v;
+ ly_bool data_printed = 0;
+
+ LY_ARRAY_FOR(ext->substmts, u) {
+ switch (ext->substmts[u].stmt) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_USES: {
+ const struct lysc_node *node;
+
+ if (data_printed) {
+ break;
+ }
+
+ LY_LIST_FOR(*(const struct lysc_node **)ext->substmts[u].storage, node) {
+ ypr_open(pctx->out, flag);
+ if (ext->substmts[u].stmt == LY_STMT_NOTIFICATION) {
+ yprc_notification(pctx, (struct lysc_node_notif *)node);
+ } else if (ext->substmts[u].stmt & (LY_STMT_INPUT | LY_STMT_OUTPUT)) {
+ yprc_inout(pctx, (struct lysc_node_action_inout *)node, flag);
+ } else if (ext->substmts[u].stmt & (LY_STMT_ACTION | LY_STMT_RPC)) {
+ yprc_action(pctx, (struct lysc_node_action *)node);
+ } else {
+ yprc_node(pctx, node);
+ }
+ }
+
+ /* all data nodes are stored in a linked list so all were printed */
+ data_printed = 1;
+ break;
+ }
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_KEY:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_UNITS:
+ if (*(const char **)ext->substmts[u].storage) {
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, ext->substmts[u].stmt, 0, *(const char **)ext->substmts[u].storage, ext->exts);
+ }
+ break;
+ case LY_STMT_BIT:
+ case LY_STMT_ENUM: {
+ const struct lysc_type_bitenum_item *items = *(struct lysc_type_bitenum_item **)ext->substmts[u].storage;
+
+ yprc_bits_enum(pctx, items, ext->substmts[u].stmt == LY_STMT_BIT ? LY_TYPE_BITS : LY_TYPE_ENUM, flag);
+ break;
+ }
+ case LY_STMT_CONFIG:
+ ypr_config(pctx, *(uint16_t *)ext->substmts[u].storage, ext->exts, flag);
+ break;
+ case LY_STMT_EXTENSION_INSTANCE:
+ yprc_extension_instances(pctx, LY_STMT_EXTENSION_INSTANCE, 0,
+ *(struct lysc_ext_instance **)ext->substmts[u].storage, flag);
+ break;
+ case LY_STMT_FRACTION_DIGITS:
+ if (*(uint8_t *)ext->substmts[u].storage) {
+ ypr_unsigned(pctx, LY_STMT_FRACTION_DIGITS, 0, ext->exts, *(uint8_t *)ext->substmts[u].storage, flag);
+ }
+ break;
+ case LY_STMT_IDENTITY: {
+ const struct lysc_ident *idents = *(struct lysc_ident **)ext->substmts[u].storage;
+
+ LY_ARRAY_FOR(idents, v) {
+ yprc_identity(pctx, &idents[v]);
+ }
+ break;
+ }
+ case LY_STMT_LENGTH:
+ if (*(struct lysc_range **)ext->substmts[u].storage) {
+ yprc_range(pctx, *(struct lysc_range **)ext->substmts[u].storage, LY_TYPE_STRING, flag);
+ }
+ break;
+ case LY_STMT_MANDATORY:
+ ypr_mandatory(pctx, *(uint16_t *)ext->substmts[u].storage, ext->exts, flag);
+ break;
+ case LY_STMT_MAX_ELEMENTS: {
+ uint32_t max = *(uint32_t *)ext->substmts[u].storage;
+
+ if (max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, ext->exts, max, flag);
+ } else {
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", ext->exts);
+ }
+ break;
+ }
+ case LY_STMT_MIN_ELEMENTS:
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, ext->exts, *(uint32_t *)ext->substmts[u].storage, flag);
+ break;
+ case LY_STMT_ORDERED_BY:
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0,
+ (*(uint16_t *)ext->substmts[u].storage & LYS_ORDBY_USER) ? "user" : "system", ext->exts);
+ break;
+ case LY_STMT_MUST: {
+ const struct lysc_must *musts = *(struct lysc_must **)ext->substmts[u].storage;
+
+ LY_ARRAY_FOR(musts, v) {
+ yprc_must(pctx, &musts[v], flag);
+ }
+ break;
+ }
+ case LY_STMT_PATTERN: {
+ const struct lysc_pattern *patterns = *(struct lysc_pattern **)ext->substmts[u].storage;
+
+ LY_ARRAY_FOR(patterns, v) {
+ yprc_pattern(pctx, &patterns[v], flag);
+ }
+ break;
+ }
+ case LY_STMT_POSITION:
+ if (*(int64_t *)ext->substmts[u].storage) {
+ ypr_unsigned(pctx, ext->substmts[u].stmt, 0, ext->exts, *(int64_t *)ext->substmts[u].storage, flag);
+ }
+ break;
+ case LY_STMT_VALUE:
+ if (*(int64_t *)ext->substmts[u].storage) {
+ ypr_signed(pctx, ext->substmts[u].stmt, 0, ext->exts, *(int64_t *)ext->substmts[u].storage, flag);
+ }
+ break;
+ case LY_STMT_RANGE:
+ if (*(struct lysc_range **)ext->substmts[u].storage) {
+ yprc_range(pctx, *(struct lysc_range **)ext->substmts[u].storage, LY_TYPE_UINT64, flag);
+ }
+ break;
+ case LY_STMT_REQUIRE_INSTANCE:
+ ypr_open(pctx->out, flag);
+ ypr_substmt(pctx, LY_STMT_REQUIRE_INSTANCE, 0, *(uint8_t *)ext->substmts[u].storage ? "true" : "false",
+ ext->exts);
+ break;
+ case LY_STMT_STATUS:
+ ypr_status(pctx, *(uint16_t *)ext->substmts[u].storage, ext->exts, flag);
+ break;
+ case LY_STMT_TYPE:
+ if (*(const struct lysc_type **)ext->substmts[u].storage) {
+ ypr_open(pctx->out, flag);
+ yprc_type(pctx, *(const struct lysc_type **)ext->substmts[u].storage);
+ }
+ break;
+ case LY_STMT_WHEN:
+ yprc_when(pctx, *(struct lysc_when **)ext->substmts[u].storage, flag);
+ break;
+ case LY_STMT_AUGMENT:
+ case LY_STMT_BASE:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ case LY_STMT_MODULE:
+ case LY_STMT_PATH:
+ case LY_STMT_PREFIX:
+ case LY_STMT_REFINE:
+ case LY_STMT_REVISION:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_UNIQUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ /* nothing to do */
+ break;
+ default:
+ LOGINT(pctx->module->ctx);
+ break;
+ }
+ }
+}
diff --git a/src/printer_yin.c b/src/printer_yin.c
new file mode 100644
index 0000000..74e0f0e
--- /dev/null
+++ b/src/printer_yin.c
@@ -0,0 +1,1501 @@
+/**
+ * @file printer_yin.c
+ * @author Fred Gan <ganshaolong@vip.qq.com>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YIN printer
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+#include "out.h"
+#include "out_internal.h"
+#include "printer_internal.h"
+#include "tree.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+#include "xpath.h"
+
+/**
+ * @brief YIN printer context.
+ */
+struct lys_ypr_ctx {
+ union {
+ struct {
+ struct ly_out *out; /**< output specification */
+ uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+ uint32_t options; /**< Schema output options (see @ref schemaprinterflags). */
+ const struct lys_module *module; /**< schema to print */
+ };
+ struct lyspr_ctx printer_ctx;
+ };
+
+ /* YIN printer specific members */
+};
+
+static void
+ypr_open(struct lys_ypr_ctx *pctx, const char *elem_name, const char *attr_name, const char *attr_value, int8_t flag)
+{
+ ly_print_(pctx->out, "%*s<%s", INDENT, elem_name);
+
+ if (attr_name) {
+ ly_print_(pctx->out, " %s=\"", attr_name);
+ lyxml_dump_text(pctx->out, attr_value, 1);
+ ly_print_(pctx->out, "\"%s", flag == -1 ? "/>\n" : flag == 1 ? ">\n" : "");
+ } else if (flag) {
+ ly_print_(pctx->out, flag == -1 ? "/>\n" : ">\n");
+ }
+}
+
+static void
+ypr_close(struct lys_ypr_ctx *pctx, const char *elem_name, int8_t flag)
+{
+ if (flag) {
+ ly_print_(pctx->out, "%*s</%s>\n", INDENT, elem_name);
+ } else {
+ ly_print_(pctx->out, "/>\n");
+ }
+}
+
+/*
+ * par_close_flag
+ * 0 - parent not yet closed, printing >\n, setting flag to 1
+ * 1 or NULL - parent already closed, do nothing
+ */
+static void
+ypr_close_parent(struct lys_ypr_ctx *pctx, int8_t *par_close_flag)
+{
+ if (par_close_flag && !(*par_close_flag)) {
+ (*par_close_flag) = 1;
+ ly_print_(pctx->out, ">\n");
+ }
+}
+
+static void
+ypr_yin_arg(struct lys_ypr_ctx *pctx, const char *arg, const char *text)
+{
+ ly_print_(pctx->out, "%*s<%s>", INDENT, arg);
+ lyxml_dump_text(pctx->out, text, 0);
+ ly_print_(pctx->out, "</%s>\n", arg);
+}
+
+static void
+yprp_stmt(struct lys_ypr_ctx *pctx, struct lysp_stmt *stmt)
+{
+ struct lysp_stmt *childstmt;
+ int8_t flag = stmt->child ? 1 : -1;
+
+ /* TODO:
+ the extension instance substatements in extension instances (LY_STMT_EXTENSION_INSTANCE)
+ cannot find the compiled information, so it is needed to be done,
+ currently it is ignored */
+ if (lys_stmt_str(stmt->kw)) {
+ if (lys_stmt_flags(stmt->kw) & LY_STMT_FLAG_YIN) {
+ ypr_open(pctx, stmt->stmt, NULL, NULL, flag);
+ ypr_yin_arg(pctx, lys_stmt_arg(stmt->kw), stmt->arg);
+ } else {
+ ypr_open(pctx, stmt->stmt, lys_stmt_arg(stmt->kw), stmt->arg, flag);
+ }
+ }
+
+ if (stmt->child) {
+ LEVEL++;
+ LY_LIST_FOR(stmt->child, childstmt) {
+ yprp_stmt(pctx, childstmt);
+ }
+ LEVEL--;
+ ypr_close(pctx, stmt->stmt, flag);
+ }
+}
+
+static void
+yprp_extension_instance(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index,
+ struct lysp_ext_instance *ext, int8_t *flag)
+{
+ struct lysp_stmt *stmt;
+ int8_t inner_flag;
+
+ if ((ext->flags & LYS_INTERNAL) || (ext->parent_stmt != substmt) || (ext->parent_stmt_index != substmt_index)) {
+ return;
+ }
+
+ ypr_close_parent(pctx, flag);
+ inner_flag = 0;
+
+ ypr_open(pctx, ext->name, (ext->def->flags & LYS_YINELEM_TRUE) ? NULL : ext->def->argname, ext->argument, inner_flag);
+ LEVEL++;
+ if (ext->def->flags & LYS_YINELEM_TRUE) {
+ const char *prefix, *name, *id;
+ size_t prefix_len, name_len;
+
+ ypr_close_parent(pctx, &inner_flag);
+
+ /* we need to use the same namespace as for the extension instance element */
+ id = ext->name;
+ ly_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len);
+ ly_print_(pctx->out, "%*s<%.*s:%s>", INDENT, (int)prefix_len, prefix, ext->def->argname);
+ lyxml_dump_text(pctx->out, ext->argument, 0);
+ ly_print_(pctx->out, "</%.*s:%s>\n", (int)prefix_len, prefix, ext->def->argname);
+ }
+ LY_LIST_FOR(ext->child, stmt) {
+ if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) {
+ continue;
+ }
+
+ ypr_close_parent(pctx, &inner_flag);
+ yprp_stmt(pctx, stmt);
+ }
+ LEVEL--;
+ ypr_close(pctx, ext->name, inner_flag);
+}
+
+static void
+yprp_extension_instances(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index,
+ struct lysp_ext_instance *exts, int8_t *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(exts, u) {
+ yprp_extension_instance(pctx, substmt, substmt_index, &exts[u], flag);
+ }
+}
+
+static void
+ypr_substmt(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index, const char *text, void *exts)
+{
+ int8_t extflag = 0;
+
+ if (!text) {
+ /* nothing to print */
+ return;
+ }
+
+ if (lys_stmt_flags(substmt) & LY_STMT_FLAG_YIN) {
+ extflag = 1;
+ ypr_open(pctx, lys_stmt_str(substmt), NULL, NULL, extflag);
+ } else {
+ ypr_open(pctx, lys_stmt_str(substmt), lys_stmt_arg(substmt), text, extflag);
+ }
+
+ LEVEL++;
+ yprp_extension_instances(pctx, substmt, substmt_index, exts, &extflag);
+
+ /* argument as yin-element */
+ if (lys_stmt_flags(substmt) & LY_STMT_FLAG_YIN) {
+ ypr_yin_arg(pctx, lys_stmt_arg(substmt), text);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, lys_stmt_str(substmt), extflag);
+}
+
+static void
+ypr_unsigned(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index, void *exts, unsigned long attr_value)
+{
+ char *str;
+
+ if (asprintf(&str, "%lu", attr_value) == -1) {
+ LOGMEM(pctx->module->ctx);
+ return;
+ }
+ ypr_substmt(pctx, substmt, substmt_index, str, exts);
+ free(str);
+}
+
+static void
+ypr_signed(struct lys_ypr_ctx *pctx, enum ly_stmt substmt, uint8_t substmt_index, void *exts, long attr_value)
+{
+ char *str;
+
+ if (asprintf(&str, "%ld", attr_value) == -1) {
+ LOGMEM(pctx->module->ctx);
+ return;
+ }
+ ypr_substmt(pctx, substmt, substmt_index, str, exts);
+ free(str);
+}
+
+static void
+yprp_revision(struct lys_ypr_ctx *pctx, const struct lysp_revision *rev)
+{
+ if (rev->dsc || rev->ref || rev->exts) {
+ ypr_open(pctx, "revision", "date", rev->date, 1);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_REVISION, 0, rev->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, rev->dsc, rev->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, rev->ref, rev->exts);
+ LEVEL--;
+ ypr_close(pctx, "revision", 1);
+ } else {
+ ypr_open(pctx, "revision", "date", rev->date, -1);
+ }
+}
+
+static void
+ypr_mandatory(struct lys_ypr_ctx *pctx, uint16_t flags, void *exts, int8_t *flag)
+{
+ if (flags & LYS_MAND_MASK) {
+ ypr_close_parent(pctx, flag);
+ ypr_substmt(pctx, LY_STMT_MANDATORY, 0, (flags & LYS_MAND_TRUE) ? "true" : "false", exts);
+ }
+}
+
+static void
+ypr_config(struct lys_ypr_ctx *pctx, uint16_t flags, void *exts, int8_t *flag)
+{
+ if (flags & LYS_CONFIG_MASK) {
+ ypr_close_parent(pctx, flag);
+ ypr_substmt(pctx, LY_STMT_CONFIG, 0, (flags & LYS_CONFIG_W) ? "true" : "false", exts);
+ }
+}
+
+static void
+ypr_status(struct lys_ypr_ctx *pctx, uint16_t flags, void *exts, int8_t *flag)
+{
+ const char *status = NULL;
+
+ if (flags & LYS_STATUS_CURR) {
+ ypr_close_parent(pctx, flag);
+ status = "current";
+ } else if (flags & LYS_STATUS_DEPRC) {
+ ypr_close_parent(pctx, flag);
+ status = "deprecated";
+ } else if (flags & LYS_STATUS_OBSLT) {
+ ypr_close_parent(pctx, flag);
+ status = "obsolete";
+ }
+
+ ypr_substmt(pctx, LY_STMT_STATUS, 0, status, exts);
+}
+
+static void
+ypr_description(struct lys_ypr_ctx *pctx, const char *dsc, void *exts, int8_t *flag)
+{
+ if (dsc) {
+ ypr_close_parent(pctx, flag);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, dsc, exts);
+ }
+}
+
+static void
+ypr_reference(struct lys_ypr_ctx *pctx, const char *ref, void *exts, int8_t *flag)
+{
+ if (ref) {
+ ypr_close_parent(pctx, flag);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, ref, exts);
+ }
+}
+
+static void
+yprp_iffeatures(struct lys_ypr_ctx *pctx, struct lysp_qname *iffs, struct lysp_ext_instance *exts, int8_t *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t extflag;
+
+ LY_ARRAY_FOR(iffs, u) {
+ ypr_close_parent(pctx, flag);
+ extflag = 0;
+
+ ly_print_(pctx->out, "%*s<if-feature name=\"%s", INDENT, iffs[u].str);
+
+ /* extensions */
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_IF_FEATURE, u, exts, &extflag);
+ LEVEL--;
+ ly_print_(pctx->out, "\"/>\n");
+ }
+}
+
+static void
+yprp_extension(struct lys_ypr_ctx *pctx, const struct lysp_ext *ext)
+{
+ int8_t flag = 0, flag2 = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ ypr_open(pctx, "extension", "name", ext->name, flag);
+ LEVEL++;
+
+ if (ext->exts) {
+ ypr_close_parent(pctx, &flag);
+ yprp_extension_instances(pctx, LY_STMT_EXTENSION, 0, ext->exts, &flag);
+ }
+
+ if (ext->argname) {
+ ypr_close_parent(pctx, &flag);
+ ypr_open(pctx, "argument", "name", ext->argname, flag2);
+
+ LEVEL++;
+ if (ext->exts) {
+ u = -1;
+ while ((u = lysp_ext_instance_iter(ext->exts, u + 1, LY_STMT_ARGUMENT)) != LY_ARRAY_COUNT(ext->exts)) {
+ ypr_close_parent(pctx, &flag2);
+ yprp_extension_instance(pctx, LY_STMT_ARGUMENT, 0, &ext->exts[u], &flag2);
+ }
+ }
+ if ((ext->flags & LYS_YINELEM_MASK) ||
+ (ext->exts && (lysp_ext_instance_iter(ext->exts, 0, LY_STMT_YIN_ELEMENT) != LY_ARRAY_COUNT(ext->exts)))) {
+ ypr_close_parent(pctx, &flag2);
+ ypr_substmt(pctx, LY_STMT_YIN_ELEMENT, 0, (ext->flags & LYS_YINELEM_TRUE) ? "true" : "false", ext->exts);
+ }
+ LEVEL--;
+ ypr_close(pctx, "argument", flag2);
+ }
+
+ ypr_status(pctx, ext->flags, ext->exts, &flag);
+ ypr_description(pctx, ext->dsc, ext->exts, &flag);
+ ypr_reference(pctx, ext->ref, ext->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, "extension", flag);
+}
+
+static void
+yprp_feature(struct lys_ypr_ctx *pctx, const struct lysp_feature *feat)
+{
+ int8_t flag = 0;
+
+ ypr_open(pctx, "feature", "name", feat->name, flag);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_FEATURE, 0, feat->exts, &flag);
+ yprp_iffeatures(pctx, feat->iffeatures, feat->exts, &flag);
+ ypr_status(pctx, feat->flags, feat->exts, &flag);
+ ypr_description(pctx, feat->dsc, feat->exts, &flag);
+ ypr_reference(pctx, feat->ref, feat->exts, &flag);
+ LEVEL--;
+ ypr_close(pctx, "feature", flag);
+}
+
+static void
+yprp_identity(struct lys_ypr_ctx *pctx, const struct lysp_ident *ident)
+{
+ int8_t flag = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ ypr_open(pctx, "identity", "name", ident->name, flag);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_IDENTITY, 0, ident->exts, &flag);
+ yprp_iffeatures(pctx, ident->iffeatures, ident->exts, &flag);
+
+ LY_ARRAY_FOR(ident->bases, u) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_BASE, u, ident->bases[u], ident->exts);
+ }
+
+ ypr_status(pctx, ident->flags, ident->exts, &flag);
+ ypr_description(pctx, ident->dsc, ident->exts, &flag);
+ ypr_reference(pctx, ident->ref, ident->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, "identity", flag);
+}
+
+static void
+yprp_restr(struct lys_ypr_ctx *pctx, const struct lysp_restr *restr, enum ly_stmt stmt, const char *attr, int8_t *flag)
+{
+ (void)flag;
+ int8_t inner_flag = 0;
+
+ if (!restr) {
+ return;
+ }
+
+ ly_print_(pctx->out, "%*s<%s %s=\"", INDENT, lyplg_ext_stmt2str(stmt), attr);
+ lyxml_dump_text(pctx->out,
+ (restr->arg.str[0] != LYSP_RESTR_PATTERN_NACK && restr->arg.str[0] != LYSP_RESTR_PATTERN_ACK) ?
+ restr->arg.str : &restr->arg.str[1], 1);
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprp_extension_instances(pctx, stmt, 0, restr->exts, &inner_flag);
+ if (restr->arg.str[0] == LYSP_RESTR_PATTERN_NACK) {
+ ypr_close_parent(pctx, &inner_flag);
+ /* special byte value in pattern's expression: 0x15 - invert-match, 0x06 - match */
+ ypr_substmt(pctx, LY_STMT_MODIFIER, 0, "invert-match", restr->exts);
+ }
+ if (restr->emsg) {
+ ypr_close_parent(pctx, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_MESSAGE, 0, restr->emsg, restr->exts);
+ }
+ if (restr->eapptag) {
+ ypr_close_parent(pctx, &inner_flag);
+ ypr_substmt(pctx, LY_STMT_ERROR_APP_TAG, 0, restr->eapptag, restr->exts);
+ }
+ ypr_description(pctx, restr->dsc, restr->exts, &inner_flag);
+ ypr_reference(pctx, restr->ref, restr->exts, &inner_flag);
+
+ LEVEL--;
+ ypr_close(pctx, lyplg_ext_stmt2str(stmt), inner_flag);
+}
+
+static void
+yprp_when(struct lys_ypr_ctx *pctx, struct lysp_when *when, int8_t *flag)
+{
+ int8_t inner_flag = 0;
+
+ if (!when) {
+ return;
+ }
+
+ ypr_close_parent(pctx, flag);
+ ly_print_(pctx->out, "%*s<when condition=\"", INDENT);
+ lyxml_dump_text(pctx->out, when->cond, 1);
+ ly_print_(pctx->out, "\"");
+
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
+ ypr_description(pctx, when->dsc, when->exts, &inner_flag);
+ ypr_reference(pctx, when->ref, when->exts, &inner_flag);
+ LEVEL--;
+ ypr_close(pctx, "when", inner_flag);
+}
+
+static void
+yprp_enum(struct lys_ypr_ctx *pctx, const struct lysp_type_enum *items, LY_DATA_TYPE type, int8_t *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t inner_flag;
+
+ (void)flag;
+
+ LY_ARRAY_FOR(items, u) {
+ if (type == LY_TYPE_BITS) {
+ ly_print_(pctx->out, "%*s<bit name=\"", INDENT);
+ lyxml_dump_text(pctx->out, items[u].name, 1);
+ ly_print_(pctx->out, "\"");
+ } else { /* LY_TYPE_ENUM */
+ ly_print_(pctx->out, "%*s<enum name=\"", INDENT);
+ lyxml_dump_text(pctx->out, items[u].name, 1);
+ ly_print_(pctx->out, "\"");
+ }
+ inner_flag = 0;
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_ENUM, 0, items[u].exts, &inner_flag);
+ yprp_iffeatures(pctx, items[u].iffeatures, items[u].exts, &inner_flag);
+ if (items[u].flags & LYS_SET_VALUE) {
+ if (type == LY_TYPE_BITS) {
+ ypr_close_parent(pctx, &inner_flag);
+ ypr_unsigned(pctx, LY_STMT_POSITION, 0, items[u].exts, items[u].value);
+ } else { /* LY_TYPE_ENUM */
+ ypr_close_parent(pctx, &inner_flag);
+ ypr_signed(pctx, LY_STMT_VALUE, 0, items[u].exts, items[u].value);
+ }
+ }
+ ypr_status(pctx, items[u].flags, items[u].exts, &inner_flag);
+ ypr_description(pctx, items[u].dsc, items[u].exts, &inner_flag);
+ ypr_reference(pctx, items[u].ref, items[u].exts, &inner_flag);
+ LEVEL--;
+ ypr_close(pctx, type == LY_TYPE_BITS ? "bit" : "enum", inner_flag);
+ }
+}
+
+static void
+yprp_type(struct lys_ypr_ctx *pctx, const struct lysp_type *type)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+
+ if (!pctx || !type) {
+ return;
+ }
+
+ ypr_open(pctx, "type", "name", type->name, flag);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_TYPE, 0, type->exts, &flag);
+
+ if (type->range || type->length || type->patterns || type->bits || type->enums) {
+ ypr_close_parent(pctx, &flag);
+ }
+ yprp_restr(pctx, type->range, LY_STMT_RANGE, "value", &flag);
+ yprp_restr(pctx, type->length, LY_STMT_LENGTH, "value", &flag);
+ LY_ARRAY_FOR(type->patterns, u) {
+ yprp_restr(pctx, &type->patterns[u], LY_STMT_PATTERN, "value", &flag);
+ }
+ yprp_enum(pctx, type->bits, LY_TYPE_BITS, &flag);
+ yprp_enum(pctx, type->enums, LY_TYPE_ENUM, &flag);
+
+ if (type->path) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_PATH, 0, type->path->expr, type->exts);
+ }
+ if (type->flags & LYS_SET_REQINST) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_REQUIRE_INSTANCE, 0, type->require_instance ? "true" : "false", type->exts);
+ }
+ if (type->flags & LYS_SET_FRDIGITS) {
+ ypr_close_parent(pctx, &flag);
+ ypr_unsigned(pctx, LY_STMT_FRACTION_DIGITS, 0, type->exts, type->fraction_digits);
+ }
+ LY_ARRAY_FOR(type->bases, u) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_BASE, u, type->bases[u], type->exts);
+ }
+ LY_ARRAY_FOR(type->types, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_type(pctx, &type->types[u]);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "type", flag);
+}
+
+static void
+yprp_typedef(struct lys_ypr_ctx *pctx, const struct lysp_tpdf *tpdf)
+{
+ ypr_open(pctx, "typedef", "name", tpdf->name, 1);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_TYPEDEF, 0, tpdf->exts, NULL);
+
+ yprp_type(pctx, &tpdf->type);
+
+ if (tpdf->units) {
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, tpdf->units, tpdf->exts);
+ }
+ if (tpdf->dflt.str) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, tpdf->dflt.str, tpdf->exts);
+ }
+
+ ypr_status(pctx, tpdf->flags, tpdf->exts, NULL);
+ ypr_description(pctx, tpdf->dsc, tpdf->exts, NULL);
+ ypr_reference(pctx, tpdf->ref, tpdf->exts, NULL);
+
+ LEVEL--;
+ ypr_close(pctx, "typedef", 1);
+}
+
+static void yprp_node(struct lys_ypr_ctx *pctx, const struct lysp_node *node);
+static void yprp_action(struct lys_ypr_ctx *pctx, const struct lysp_node_action *action);
+static void yprp_notification(struct lys_ypr_ctx *pctx, const struct lysp_node_notif *notif);
+
+static void
+yprp_grouping(struct lys_ypr_ctx *pctx, const struct lysp_node_grp *grp)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node *data;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *subgrp;
+
+ ypr_open(pctx, "grouping", "name", grp->name, flag);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_GROUPING, 0, grp->exts, &flag);
+ ypr_status(pctx, grp->flags, grp->exts, &flag);
+ ypr_description(pctx, grp->dsc, grp->exts, &flag);
+ ypr_reference(pctx, grp->ref, grp->exts, &flag);
+
+ LY_ARRAY_FOR(grp->typedefs, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_typedef(pctx, &grp->typedefs[u]);
+ }
+
+ LY_LIST_FOR(grp->groupings, subgrp) {
+ ypr_close_parent(pctx, &flag);
+ yprp_grouping(pctx, subgrp);
+ }
+
+ LY_LIST_FOR(grp->child, data) {
+ ypr_close_parent(pctx, &flag);
+ yprp_node(pctx, data);
+ }
+
+ LY_LIST_FOR(grp->actions, action) {
+ ypr_close_parent(pctx, &flag);
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(grp->notifs, notif) {
+ ypr_close_parent(pctx, &flag);
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "grouping", flag);
+}
+
+static void
+yprp_inout(struct lys_ypr_ctx *pctx, const struct lysp_node_action_inout *inout, int8_t *flag)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node *data;
+ struct lysp_node_grp *grp;
+
+ if (!inout->child) {
+ /* input/output is empty */
+ return;
+ }
+ ypr_close_parent(pctx, flag);
+
+ ypr_open(pctx, inout->name, NULL, NULL, *flag);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, lyplg_ext_nodetype2stmt(inout->nodetype), 0, inout->exts, NULL);
+ LY_ARRAY_FOR(inout->musts, u) {
+ yprp_restr(pctx, &inout->musts[u], LY_STMT_MUST, "condition", NULL);
+ }
+ LY_ARRAY_FOR(inout->typedefs, u) {
+ yprp_typedef(pctx, &inout->typedefs[u]);
+ }
+ LY_LIST_FOR(inout->groupings, grp) {
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(inout->child, data) {
+ yprp_node(pctx, data);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, inout->name, 1);
+}
+
+static void
+yprp_notification(struct lys_ypr_ctx *pctx, const struct lysp_node_notif *notif)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node *data;
+ struct lysp_node_grp *grp;
+
+ ypr_open(pctx, "notification", "name", notif->name, flag);
+
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_NOTIFICATION, 0, notif->exts, &flag);
+ yprp_iffeatures(pctx, notif->iffeatures, notif->exts, &flag);
+
+ LY_ARRAY_FOR(notif->musts, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_restr(pctx, &notif->musts[u], LY_STMT_MUST, "condition", &flag);
+ }
+ ypr_status(pctx, notif->flags, notif->exts, &flag);
+ ypr_description(pctx, notif->dsc, notif->exts, &flag);
+ ypr_reference(pctx, notif->ref, notif->exts, &flag);
+
+ LY_ARRAY_FOR(notif->typedefs, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_typedef(pctx, &notif->typedefs[u]);
+ }
+
+ LY_LIST_FOR(notif->groupings, grp) {
+ ypr_close_parent(pctx, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(notif->child, data) {
+ ypr_close_parent(pctx, &flag);
+ yprp_node(pctx, data);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "notification", flag);
+}
+
+static void
+yprp_action(struct lys_ypr_ctx *pctx, const struct lysp_node_action *action)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node_grp *grp;
+
+ ypr_open(pctx, action->parent ? "action" : "rpc", "name", action->name, flag);
+
+ LEVEL++;
+ yprp_extension_instances(pctx, lyplg_ext_nodetype2stmt(action->nodetype), 0, action->exts, &flag);
+ yprp_iffeatures(pctx, action->iffeatures, action->exts, &flag);
+ ypr_status(pctx, action->flags, action->exts, &flag);
+ ypr_description(pctx, action->dsc, action->exts, &flag);
+ ypr_reference(pctx, action->ref, action->exts, &flag);
+
+ LY_ARRAY_FOR(action->typedefs, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_typedef(pctx, &action->typedefs[u]);
+ }
+
+ LY_LIST_FOR(action->groupings, grp) {
+ ypr_close_parent(pctx, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ yprp_inout(pctx, &action->input, &flag);
+ yprp_inout(pctx, &action->output, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, action->parent ? "action" : "rpc", flag);
+}
+
+static void
+yprp_node_common1(struct lys_ypr_ctx *pctx, const struct lysp_node *node, int8_t *flag)
+{
+ ypr_open(pctx, lys_nodetype2str(node->nodetype), "name", node->name, *flag);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, lyplg_ext_nodetype2stmt(node->nodetype), 0, node->exts, flag);
+ yprp_when(pctx, lysp_node_when(node), flag);
+ yprp_iffeatures(pctx, node->iffeatures, node->exts, flag);
+}
+
+static void
+yprp_node_common2(struct lys_ypr_ctx *pctx, const struct lysp_node *node, int8_t *flag)
+{
+ ypr_config(pctx, node->flags, node->exts, flag);
+ if (node->nodetype & (LYS_CHOICE | LYS_LEAF | LYS_ANYDATA)) {
+ ypr_mandatory(pctx, node->flags, node->exts, flag);
+ }
+ ypr_status(pctx, node->flags, node->exts, flag);
+ ypr_description(pctx, node->dsc, node->exts, flag);
+ ypr_reference(pctx, node->ref, node->exts, flag);
+}
+
+static void
+yprp_container(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *grp;
+ struct lysp_node_container *cont = (struct lysp_node_container *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(cont->musts, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_restr(pctx, &cont->musts[u], LY_STMT_MUST, "condition", &flag);
+ }
+ if (cont->presence) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_PRESENCE, 0, cont->presence, cont->exts);
+ }
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_ARRAY_FOR(cont->typedefs, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_typedef(pctx, &cont->typedefs[u]);
+ }
+
+ LY_LIST_FOR(cont->groupings, grp) {
+ ypr_close_parent(pctx, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(cont->child, child) {
+ ypr_close_parent(pctx, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LY_LIST_FOR(cont->actions, action) {
+ ypr_close_parent(pctx, &flag);
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(cont->notifs, notif) {
+ ypr_close_parent(pctx, &flag);
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "container", flag);
+}
+
+static void
+yprp_case(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ int8_t flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_case *cas = (struct lysp_node_case *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_LIST_FOR(cas->child, child) {
+ ypr_close_parent(pctx, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "case", flag);
+}
+
+static void
+yprp_choice(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ int8_t flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_choice *choice = (struct lysp_node_choice *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ if (choice->dflt.str) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, choice->dflt.str, choice->exts);
+ }
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_LIST_FOR(choice->child, child) {
+ ypr_close_parent(pctx, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "choice", flag);
+}
+
+static void
+yprp_leaf(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node_leaf *leaf = (struct lysp_node_leaf *)node;
+
+ int8_t flag = 1;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ yprp_type(pctx, &leaf->type);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, leaf->units, leaf->exts);
+ LY_ARRAY_FOR(leaf->musts, u) {
+ yprp_restr(pctx, &leaf->musts[u], LY_STMT_MUST, "condition", &flag);
+ }
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, leaf->dflt.str, leaf->exts);
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, "leaf", flag);
+}
+
+static void
+yprp_leaflist(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node_leaflist *llist = (struct lysp_node_leaflist *)node;
+ int8_t flag = 1;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ yprp_type(pctx, &llist->type);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, llist->units, llist->exts);
+ LY_ARRAY_FOR(llist->musts, u) {
+ yprp_restr(pctx, &llist->musts[u], LY_STMT_MUST, "condition", NULL);
+ }
+ LY_ARRAY_FOR(llist->dflts, u) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, llist->dflts[u].str, llist->exts);
+ }
+
+ ypr_config(pctx, node->flags, node->exts, NULL);
+
+ if (llist->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, llist->exts, llist->min);
+ }
+ if (llist->flags & LYS_SET_MAX) {
+ if (llist->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, llist->exts, llist->max);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", llist->exts);
+ }
+ }
+
+ if (llist->flags & LYS_ORDBY_MASK) {
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0, (llist->flags & LYS_ORDBY_USER) ? "user" : "system", llist->exts);
+ }
+
+ ypr_status(pctx, node->flags, node->exts, &flag);
+ ypr_description(pctx, node->dsc, node->exts, &flag);
+ ypr_reference(pctx, node->ref, node->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, "leaf-list", flag);
+}
+
+static void
+yprp_list(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node *child;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *grp;
+ struct lysp_node_list *list = (struct lysp_node_list *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(list->musts, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_restr(pctx, &list->musts[u], LY_STMT_MUST, "condition", &flag);
+ }
+ if (list->key) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_KEY, 0, list->key, list->exts);
+ }
+ LY_ARRAY_FOR(list->uniques, u) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_UNIQUE, u, list->uniques[u].str, list->exts);
+ }
+
+ ypr_config(pctx, node->flags, node->exts, &flag);
+
+ if (list->flags & LYS_SET_MIN) {
+ ypr_close_parent(pctx, &flag);
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, list->exts, list->min);
+ }
+ if (list->flags & LYS_SET_MAX) {
+ ypr_close_parent(pctx, &flag);
+ if (list->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, list->exts, list->max);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", list->exts);
+ }
+ }
+
+ if (list->flags & LYS_ORDBY_MASK) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_ORDERED_BY, 0, (list->flags & LYS_ORDBY_USER) ? "user" : "system", list->exts);
+ }
+
+ ypr_status(pctx, node->flags, node->exts, &flag);
+ ypr_description(pctx, node->dsc, node->exts, &flag);
+ ypr_reference(pctx, node->ref, node->exts, &flag);
+
+ LY_ARRAY_FOR(list->typedefs, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_typedef(pctx, &list->typedefs[u]);
+ }
+
+ LY_LIST_FOR(list->groupings, grp) {
+ ypr_close_parent(pctx, &flag);
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(list->child, child) {
+ ypr_close_parent(pctx, &flag);
+ yprp_node(pctx, child);
+ }
+
+ LY_LIST_FOR(list->actions, action) {
+ ypr_close_parent(pctx, &flag);
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(list->notifs, notif) {
+ ypr_close_parent(pctx, &flag);
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "list", flag);
+}
+
+static void
+yprp_refine(struct lys_ypr_ctx *pctx, struct lysp_refine *refine)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+
+ ypr_open(pctx, "refine", "target-node", refine->nodeid, flag);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_REFINE, 0, refine->exts, &flag);
+ yprp_iffeatures(pctx, refine->iffeatures, refine->exts, &flag);
+
+ LY_ARRAY_FOR(refine->musts, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_restr(pctx, &refine->musts[u], LY_STMT_MUST, "condition", &flag);
+ }
+
+ if (refine->presence) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_PRESENCE, 0, refine->presence, refine->exts);
+ }
+
+ LY_ARRAY_FOR(refine->dflts, u) {
+ ypr_close_parent(pctx, &flag);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, refine->dflts[u].str, refine->exts);
+ }
+
+ ypr_config(pctx, refine->flags, refine->exts, &flag);
+ ypr_mandatory(pctx, refine->flags, refine->exts, &flag);
+
+ if (refine->flags & LYS_SET_MIN) {
+ ypr_close_parent(pctx, &flag);
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, refine->exts, refine->min);
+ }
+ if (refine->flags & LYS_SET_MAX) {
+ ypr_close_parent(pctx, &flag);
+ if (refine->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, refine->exts, refine->max);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", refine->exts);
+ }
+ }
+
+ ypr_description(pctx, refine->dsc, refine->exts, &flag);
+ ypr_reference(pctx, refine->ref, refine->exts, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, "refine", flag);
+}
+
+static void
+yprp_augment(struct lys_ypr_ctx *pctx, const struct lysp_node_augment *aug)
+{
+ struct lysp_node *child;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+
+ ypr_open(pctx, "augment", "target-node", aug->nodeid, 1);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_AUGMENT, 0, aug->exts, NULL);
+ yprp_when(pctx, aug->when, NULL);
+ yprp_iffeatures(pctx, aug->iffeatures, aug->exts, NULL);
+ ypr_status(pctx, aug->flags, aug->exts, NULL);
+ ypr_description(pctx, aug->dsc, aug->exts, NULL);
+ ypr_reference(pctx, aug->ref, aug->exts, NULL);
+
+ LY_LIST_FOR(aug->child, child) {
+ yprp_node(pctx, child);
+ }
+
+ LY_LIST_FOR(aug->actions, action) {
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(aug->notifs, notif) {
+ yprp_notification(pctx, notif);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "augment", 1);
+}
+
+static void
+yprp_uses(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node_uses *uses = (struct lysp_node_uses *)node;
+ struct lysp_node_augment *aug;
+
+ yprp_node_common1(pctx, node, &flag);
+ yprp_node_common2(pctx, node, &flag);
+
+ LY_ARRAY_FOR(uses->refines, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_refine(pctx, &uses->refines[u]);
+ }
+
+ LY_LIST_FOR(uses->augments, aug) {
+ ypr_close_parent(pctx, &flag);
+ yprp_augment(pctx, aug);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "uses", flag);
+}
+
+static void
+yprp_anydata(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ int8_t flag = 0;
+ struct lysp_node_anydata *any = (struct lysp_node_anydata *)node;
+
+ yprp_node_common1(pctx, node, &flag);
+
+ LY_ARRAY_FOR(any->musts, u) {
+ ypr_close_parent(pctx, &flag);
+ yprp_restr(pctx, &any->musts[u], LY_STMT_MUST, "condition", &flag);
+ }
+
+ yprp_node_common2(pctx, node, &flag);
+
+ LEVEL--;
+ ypr_close(pctx, lys_nodetype2str(node->nodetype), flag);
+}
+
+static void
+yprp_node(struct lys_ypr_ctx *pctx, const struct lysp_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ yprp_container(pctx, node);
+ break;
+ case LYS_CHOICE:
+ yprp_choice(pctx, node);
+ break;
+ case LYS_LEAF:
+ yprp_leaf(pctx, node);
+ break;
+ case LYS_LEAFLIST:
+ yprp_leaflist(pctx, node);
+ break;
+ case LYS_LIST:
+ yprp_list(pctx, node);
+ break;
+ case LYS_USES:
+ yprp_uses(pctx, node);
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ yprp_anydata(pctx, node);
+ break;
+ case LYS_CASE:
+ yprp_case(pctx, node);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+yprp_deviation(struct lys_ypr_ctx *pctx, const struct lysp_deviation *deviation)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_deviate_add *add;
+ struct lysp_deviate_rpl *rpl;
+ struct lysp_deviate_del *del;
+ struct lysp_deviate *elem;
+
+ ypr_open(pctx, "deviation", "target-node", deviation->nodeid, 1);
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATION, 0, deviation->exts, NULL);
+ ypr_description(pctx, deviation->dsc, deviation->exts, NULL);
+ ypr_reference(pctx, deviation->ref, deviation->exts, NULL);
+
+ LY_LIST_FOR(deviation->deviates, elem) {
+ ly_print_(pctx->out, "%*s<deviate value=\"", INDENT);
+ if (elem->mod == LYS_DEV_NOT_SUPPORTED) {
+ if (elem->exts) {
+ ly_print_(pctx->out, "not-supported\">\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, elem->exts, NULL);
+ } else {
+ ly_print_(pctx->out, "not-supported\"/>\n");
+ continue;
+ }
+ } else if (elem->mod == LYS_DEV_ADD) {
+ add = (struct lysp_deviate_add *)elem;
+ ly_print_(pctx->out, "add\">\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, add->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, add->units, add->exts);
+ LY_ARRAY_FOR(add->musts, u) {
+ yprp_restr(pctx, &add->musts[u], LY_STMT_MUST, "condition", NULL);
+ }
+ LY_ARRAY_FOR(add->uniques, u) {
+ ypr_substmt(pctx, LY_STMT_UNIQUE, u, add->uniques[u].str, add->exts);
+ }
+ LY_ARRAY_FOR(add->dflts, u) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, add->dflts[u].str, add->exts);
+ }
+ ypr_config(pctx, add->flags, add->exts, NULL);
+ ypr_mandatory(pctx, add->flags, add->exts, NULL);
+ if (add->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, add->exts, add->min);
+ }
+ if (add->flags & LYS_SET_MAX) {
+ if (add->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, add->exts, add->max);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", add->exts);
+ }
+ }
+ } else if (elem->mod == LYS_DEV_REPLACE) {
+ rpl = (struct lysp_deviate_rpl *)elem;
+ ly_print_(pctx->out, "replace\">\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, rpl->exts, NULL);
+ if (rpl->type) {
+ yprp_type(pctx, rpl->type);
+ }
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, rpl->units, rpl->exts);
+ ypr_substmt(pctx, LY_STMT_DEFAULT, 0, rpl->dflt.str, rpl->exts);
+ ypr_config(pctx, rpl->flags, rpl->exts, NULL);
+ ypr_mandatory(pctx, rpl->flags, rpl->exts, NULL);
+ if (rpl->flags & LYS_SET_MIN) {
+ ypr_unsigned(pctx, LY_STMT_MIN_ELEMENTS, 0, rpl->exts, rpl->min);
+ }
+ if (rpl->flags & LYS_SET_MAX) {
+ if (rpl->max) {
+ ypr_unsigned(pctx, LY_STMT_MAX_ELEMENTS, 0, rpl->exts, rpl->max);
+ } else {
+ ypr_substmt(pctx, LY_STMT_MAX_ELEMENTS, 0, "unbounded", rpl->exts);
+ }
+ }
+ } else if (elem->mod == LYS_DEV_DELETE) {
+ del = (struct lysp_deviate_del *)elem;
+ ly_print_(pctx->out, "delete\">\n");
+ LEVEL++;
+
+ yprp_extension_instances(pctx, LY_STMT_DEVIATE, 0, del->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_UNITS, 0, del->units, del->exts);
+ LY_ARRAY_FOR(del->musts, u) {
+ yprp_restr(pctx, &del->musts[u], LY_STMT_MUST, "condition", NULL);
+ }
+ LY_ARRAY_FOR(del->uniques, u) {
+ ypr_substmt(pctx, LY_STMT_UNIQUE, u, del->uniques[u].str, del->exts);
+ }
+ LY_ARRAY_FOR(del->dflts, u) {
+ ypr_substmt(pctx, LY_STMT_DEFAULT, u, del->dflts[u].str, del->exts);
+ }
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "deviate", 1);
+ }
+
+ LEVEL--;
+ ypr_close(pctx, "deviation", 1);
+}
+
+static void
+ypr_xmlns(struct lys_ypr_ctx *pctx, const struct lys_module *module, uint16_t indent)
+{
+ ly_print_(pctx->out, "%*sxmlns=\"%s\"", indent + INDENT, YIN_NS_URI);
+ ly_print_(pctx->out, "\n%*sxmlns:%s=\"%s\"", indent + INDENT, module->prefix, module->ns);
+}
+
+static void
+ypr_import_xmlns(struct lys_ypr_ctx *pctx, const struct lysp_module *modp, uint16_t indent)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(modp->imports, u){
+ ly_print_(pctx->out, "\n%*sxmlns:%s=\"%s\"", indent + INDENT, modp->imports[u].prefix, modp->imports[u].module->ns);
+ }
+}
+
+static void
+yin_print_parsed_linkage(struct lys_ypr_ctx *pctx, const struct lysp_module *modp)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(modp->imports, u) {
+ if (modp->imports[u].flags & LYS_INTERNAL) {
+ continue;
+ }
+
+ ypr_open(pctx, "import", "module", modp->imports[u].name, 1);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_IMPORT, 0, modp->imports[u].exts, NULL);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, modp->imports[u].prefix, modp->imports[u].exts);
+ if (modp->imports[u].rev[0]) {
+ ypr_substmt(pctx, LY_STMT_REVISION_DATE, 0, modp->imports[u].rev, modp->imports[u].exts);
+ }
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, modp->imports[u].dsc, modp->imports[u].exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, modp->imports[u].ref, modp->imports[u].exts);
+ LEVEL--;
+ ypr_close(pctx, "import", 1);
+ }
+ LY_ARRAY_FOR(modp->includes, u) {
+ if (modp->includes[u].injected) {
+ /* do not print the includes injected from submodules */
+ continue;
+ }
+ if (modp->includes[u].rev[0] || modp->includes[u].dsc || modp->includes[u].ref || modp->includes[u].exts) {
+ ypr_open(pctx, "include", "module", modp->includes[u].name, 1);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_INCLUDE, 0, modp->includes[u].exts, NULL);
+ if (modp->includes[u].rev[0]) {
+ ypr_substmt(pctx, LY_STMT_REVISION_DATE, 0, modp->includes[u].rev, modp->includes[u].exts);
+ }
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, modp->includes[u].dsc, modp->includes[u].exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, modp->includes[u].ref, modp->includes[u].exts);
+ LEVEL--;
+ ly_print_(pctx->out, "%*s}\n", INDENT);
+ } else {
+ ypr_open(pctx, "include", "module", modp->includes[u].name, -1);
+ }
+ }
+}
+
+static void
+yin_print_parsed_body(struct lys_ypr_ctx *pctx, const struct lysp_module *modp)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_node *data;
+ struct lysp_node_action *action;
+ struct lysp_node_notif *notif;
+ struct lysp_node_grp *grp;
+ struct lysp_node_augment *aug;
+
+ LY_ARRAY_FOR(modp->extensions, u) {
+ yprp_extension(pctx, &modp->extensions[u]);
+ }
+ if (modp->exts) {
+ yprp_extension_instances(pctx, LY_STMT_MODULE, 0, modp->exts, NULL);
+ }
+
+ LY_ARRAY_FOR(modp->features, u) {
+ yprp_feature(pctx, &modp->features[u]);
+ }
+
+ LY_ARRAY_FOR(modp->identities, u) {
+ yprp_identity(pctx, &modp->identities[u]);
+ }
+
+ LY_ARRAY_FOR(modp->typedefs, u) {
+ yprp_typedef(pctx, &modp->typedefs[u]);
+ }
+
+ LY_LIST_FOR(modp->groupings, grp) {
+ yprp_grouping(pctx, grp);
+ }
+
+ LY_LIST_FOR(modp->data, data) {
+ yprp_node(pctx, data);
+ }
+
+ LY_LIST_FOR(modp->augments, aug) {
+ yprp_augment(pctx, aug);
+ }
+
+ LY_LIST_FOR(modp->rpcs, action) {
+ yprp_action(pctx, action);
+ }
+
+ LY_LIST_FOR(modp->notifs, notif) {
+ yprp_notification(pctx, notif);
+ }
+
+ LY_ARRAY_FOR(modp->deviations, u) {
+ yprp_deviation(pctx, &modp->deviations[u]);
+ }
+}
+
+LY_ERR
+yin_print_parsed_module(struct ly_out *out, const struct lysp_module *modp, uint32_t options)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lys_module *module = modp->mod;
+ struct lys_ypr_ctx pctx_ = {.out = out, .level = 0, .module = module, .options = options}, *pctx = &pctx_;
+
+ ly_print_(pctx->out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ ly_print_(pctx->out, "%*s<module name=\"%s\"\n", INDENT, module->name);
+ ypr_xmlns(pctx, module, XML_NS_INDENT);
+ ypr_import_xmlns(pctx, modp, XML_NS_INDENT);
+ ly_print_(pctx->out, ">\n");
+
+ LEVEL++;
+
+ /* module-header-stmts */
+ if (modp->version) {
+ ypr_substmt(pctx, LY_STMT_YANG_VERSION, 0, modp->version == LYS_VERSION_1_1 ? "1.1" : "1", modp->exts);
+ }
+ ypr_substmt(pctx, LY_STMT_NAMESPACE, 0, module->ns, modp->exts);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, module->prefix, modp->exts);
+
+ /* linkage-stmts (import/include) */
+ yin_print_parsed_linkage(pctx, modp);
+
+ /* meta-stmts */
+ if (module->org || module->contact || module->dsc || module->ref) {
+ ly_print_(out, "\n");
+ }
+ ypr_substmt(pctx, LY_STMT_ORGANIZATION, 0, module->org, modp->exts);
+ ypr_substmt(pctx, LY_STMT_CONTACT, 0, module->contact, modp->exts);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, module->dsc, modp->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, module->ref, modp->exts);
+
+ /* revision-stmts */
+ if (modp->revs) {
+ ly_print_(out, "\n");
+ }
+ LY_ARRAY_FOR(modp->revs, u) {
+ yprp_revision(pctx, &modp->revs[u]);
+ }
+
+ /* body-stmts */
+ yin_print_parsed_body(pctx, modp);
+
+ LEVEL--;
+ ly_print_(out, "%*s</module>\n", INDENT);
+ ly_print_flush(out);
+
+ return LY_SUCCESS;
+}
+
+static void
+yprp_belongsto(struct lys_ypr_ctx *pctx, const struct lysp_submodule *submodp)
+{
+ ypr_open(pctx, "belongs-to", "module", submodp->mod->name, 1);
+ LEVEL++;
+ yprp_extension_instances(pctx, LY_STMT_BELONGS_TO, 0, submodp->exts, NULL);
+ ypr_substmt(pctx, LY_STMT_PREFIX, 0, submodp->prefix, submodp->exts);
+ LEVEL--;
+ ypr_close(pctx, "belongs-to", 1);
+}
+
+LY_ERR
+yin_print_parsed_submodule(struct ly_out *out, const struct lysp_submodule *submodp, uint32_t options)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lys_ypr_ctx pctx_ = {.out = out, .level = 0, .module = submodp->mod, .options = options}, *pctx = &pctx_;
+
+ ly_print_(pctx->out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ ly_print_(pctx->out, "%*s<submodule name=\"%s\"\n", INDENT, submodp->name);
+ ypr_xmlns(pctx, submodp->mod, XML_NS_INDENT);
+ ypr_import_xmlns(pctx, (struct lysp_module *)submodp, XML_NS_INDENT);
+ ly_print_(pctx->out, ">\n");
+
+ LEVEL++;
+
+ /* submodule-header-stmts */
+ if (submodp->version) {
+ ypr_substmt(pctx, LY_STMT_YANG_VERSION, 0, submodp->version == LYS_VERSION_1_1 ? "1.1" : "1", submodp->exts);
+ }
+ yprp_belongsto(pctx, submodp);
+
+ /* linkage-stmts (import/include) */
+ yin_print_parsed_linkage(pctx, (struct lysp_module *)submodp);
+
+ /* meta-stmts */
+ if (submodp->org || submodp->contact || submodp->dsc || submodp->ref) {
+ ly_print_(out, "\n");
+ }
+ ypr_substmt(pctx, LY_STMT_ORGANIZATION, 0, submodp->org, submodp->exts);
+ ypr_substmt(pctx, LY_STMT_CONTACT, 0, submodp->contact, submodp->exts);
+ ypr_substmt(pctx, LY_STMT_DESCRIPTION, 0, submodp->dsc, submodp->exts);
+ ypr_substmt(pctx, LY_STMT_REFERENCE, 0, submodp->ref, submodp->exts);
+
+ /* revision-stmts */
+ if (submodp->revs) {
+ ly_print_(out, "\n");
+ }
+ LY_ARRAY_FOR(submodp->revs, u) {
+ yprp_revision(pctx, &submodp->revs[u]);
+ }
+
+ /* body-stmts */
+ yin_print_parsed_body(pctx, (struct lysp_module *)submodp);
+
+ LEVEL--;
+ ly_print_(out, "%*s</submodule>\n", INDENT);
+ ly_print_flush(out);
+
+ return LY_SUCCESS;
+}
diff --git a/src/schema_compile.c b/src/schema_compile.c
new file mode 100644
index 0000000..ed768ba
--- /dev/null
+++ b/src/schema_compile.c
@@ -0,0 +1,1798 @@
+/**
+ * @file schema_compile.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Schema compilation.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "schema_compile.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "in.h"
+#include "log.h"
+#include "parser_schema.h"
+#include "path.h"
+#include "plugins.h"
+#include "plugins_exts.h"
+#include "plugins_internal.h"
+#include "plugins_types.h"
+#include "schema_compile_amend.h"
+#include "schema_compile_node.h"
+#include "schema_features.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+#include "tree_schema_free.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+void
+lysc_update_path(struct lysc_ctx *ctx, const struct lys_module *parent_module, const char *name)
+{
+ int len;
+ uint8_t nextlevel = 0; /* 0 - no starttag, 1 - '/' starttag, 2 - '=' starttag + '}' endtag */
+
+ if (!name) {
+ /* removing last path segment */
+ if (ctx->path[ctx->path_len - 1] == '}') {
+ for ( ; ctx->path[ctx->path_len] != '=' && ctx->path[ctx->path_len] != '{'; --ctx->path_len) {}
+ if (ctx->path[ctx->path_len] == '=') {
+ ctx->path[ctx->path_len++] = '}';
+ } else {
+ /* not a top-level special tag, remove also preceiding '/' */
+ goto remove_nodelevel;
+ }
+ } else {
+remove_nodelevel:
+ for ( ; ctx->path[ctx->path_len] != '/'; --ctx->path_len) {}
+ if (ctx->path_len == 0) {
+ /* top-level (last segment) */
+ ctx->path_len = 1;
+ }
+ }
+ /* set new terminating NULL-byte */
+ ctx->path[ctx->path_len] = '\0';
+ } else {
+ if (ctx->path_len > 1) {
+ if (!parent_module && (ctx->path[ctx->path_len - 1] == '}') && (ctx->path[ctx->path_len - 2] != '\'')) {
+ /* extension of the special tag */
+ nextlevel = 2;
+ --ctx->path_len;
+ } else {
+ /* there is already some path, so add next level */
+ nextlevel = 1;
+ }
+ } /* else the path is just initiated with '/', so do not add additional slash in case of top-level nodes */
+
+ if (nextlevel != 2) {
+ if ((parent_module && (parent_module == ctx->cur_mod)) || (!parent_module && (ctx->path_len > 1) && (name[0] == '{'))) {
+ /* module not changed, print the name unprefixed */
+ len = snprintf(&ctx->path[ctx->path_len], LYSC_CTX_BUFSIZE - ctx->path_len, "%s%s", nextlevel ? "/" : "", name);
+ } else {
+ len = snprintf(&ctx->path[ctx->path_len], LYSC_CTX_BUFSIZE - ctx->path_len, "%s%s:%s", nextlevel ? "/" : "", ctx->cur_mod->name, name);
+ }
+ } else {
+ len = snprintf(&ctx->path[ctx->path_len], LYSC_CTX_BUFSIZE - ctx->path_len, "='%s'}", name);
+ }
+ if (len >= LYSC_CTX_BUFSIZE - (int)ctx->path_len) {
+ /* output truncated */
+ ctx->path_len = LYSC_CTX_BUFSIZE - 1;
+ } else {
+ ctx->path_len += len;
+ }
+ }
+
+ LOG_LOCBACK(0, 0, 1, 0);
+ LOG_LOCSET(NULL, NULL, ctx->path, NULL);
+}
+
+/**
+ * @brief Fill in the prepared compiled extensions definition structure according to the parsed extension definition.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] extp Parsed extension instance.
+ * @param[out] ext Compiled extension definition.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_extension(struct lysc_ctx *ctx, struct lysp_ext_instance *extp, struct lysc_ext **ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_ext *ep = extp->def;
+
+ if (!ep->compiled) {
+ lysc_update_path(ctx, NULL, "{extension}");
+ lysc_update_path(ctx, NULL, ep->name);
+
+ /* compile the extension definition */
+ *ext = ep->compiled = calloc(1, sizeof **ext);
+ (*ext)->refcount = 1;
+ DUP_STRING_GOTO(ctx->ctx, ep->name, (*ext)->name, ret, cleanup);
+ DUP_STRING_GOTO(ctx->ctx, ep->argname, (*ext)->argname, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_ext_find_definition(ctx->ctx, extp, (const struct lys_module **)&(*ext)->module, NULL),
+ cleanup);
+
+ /* compile nested extensions */
+ COMPILE_EXTS_GOTO(ctx, ep->exts, (*ext)->exts, *ext, ret, cleanup);
+
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+
+ /* find extension definition plugin */
+ (*ext)->plugin = extp->record ? (struct lyplg_ext *)&extp->record->plugin : NULL;
+ }
+
+ *ext = ep->compiled;
+
+cleanup:
+ if (ret) {
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ }
+ return ret;
+}
+
+LY_ERR
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *extp, struct lysc_ext_instance *ext, void *parent)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING_GOTO(ctx->ctx, extp->argument, ext->argument, ret, cleanup);
+ ext->module = ctx->cur_mod;
+ ext->parent = parent;
+ ext->parent_stmt = extp->parent_stmt;
+ ext->parent_stmt_index = extp->parent_stmt_index;
+
+ lysc_update_path(ctx, (ext->parent_stmt & LY_STMT_NODE_MASK) ? ((struct lysc_node *)ext->parent)->module : NULL,
+ "{extension}");
+ lysc_update_path(ctx, NULL, extp->name);
+
+ /* compile extension if not already */
+ LY_CHECK_GOTO(ret = lys_compile_extension(ctx, extp, &ext->def), cleanup);
+
+ /* compile */
+ if (ext->def->plugin && ext->def->plugin->compile) {
+ if (ext->argument) {
+ lysc_update_path(ctx, ext->module, ext->argument);
+ }
+ ret = ext->def->plugin->compile(ctx, extp, ext);
+ if (ret == LY_ENOT) {
+ lysc_ext_instance_free(&ctx->free_ctx, ext);
+ }
+ if (ext->argument) {
+ lysc_update_path(ctx, NULL, NULL);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ return ret;
+}
+
+static void
+lysc_unres_must_free(struct lysc_unres_must *m)
+{
+ LY_ARRAY_FREE(m->local_mods);
+ free(m);
+}
+
+static void
+lysc_unres_dflt_free(const struct ly_ctx *ctx, struct lysc_unres_dflt *r)
+{
+ assert(!r->dflt || !r->dflts);
+ if (r->dflt) {
+ lysp_qname_free((struct ly_ctx *)ctx, r->dflt);
+ free(r->dflt);
+ } else {
+ FREE_ARRAY((struct ly_ctx *)ctx, r->dflts, lysp_qname_free);
+ }
+ free(r);
+}
+
+LY_ERR
+lys_identity_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
+ const struct lysp_ident *identities_p, struct lysc_ident **identities)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_ctx cctx;
+ struct lysc_ident *ident;
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(ctx_sc || ctx);
+
+ if (!ctx_sc) {
+ if (parsed_mod) {
+ LYSC_CTX_INIT_PMOD(cctx, parsed_mod, NULL);
+ } else {
+ LYSC_CTX_INIT_CTX(cctx, ctx);
+ }
+ ctx_sc = &cctx;
+ }
+
+ if (!identities_p) {
+ return LY_SUCCESS;
+ }
+
+ lysc_update_path(ctx_sc, NULL, "{identity}");
+ LY_ARRAY_FOR(identities_p, u) {
+ lysc_update_path(ctx_sc, NULL, identities_p[u].name);
+
+ /* add new compiled identity */
+ LY_ARRAY_NEW_GOTO(ctx_sc->ctx, *identities, ident, ret, done);
+
+ DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].name, ident->name, ret, done);
+ DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].dsc, ident->dsc, ret, done);
+ DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].ref, ident->ref, ret, done);
+ ident->module = ctx_sc->cur_mod;
+ /* backlinks (derived) can be added no sooner than when all the identities in the current module are present */
+ COMPILE_EXTS_GOTO(ctx_sc, identities_p[u].exts, ident->exts, ident, ret, done);
+ ident->flags = identities_p[u].flags;
+
+ lysc_update_path(ctx_sc, NULL, NULL);
+ }
+ lysc_update_path(ctx_sc, NULL, NULL);
+
+done:
+ if (ret) {
+ lysc_update_path(ctx_sc, NULL, NULL);
+ lysc_update_path(ctx_sc, NULL, NULL);
+ }
+ return ret;
+}
+
+/**
+ * @brief Check circular dependency of identities - identity MUST NOT reference itself (via their base statement).
+ *
+ * The function works in the same way as lys_compile_feature_circular_check() with different structures and error messages.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] ident The base identity (its derived list is being extended by the identity being currently processed).
+ * @param[in] derived The list of derived identities of the identity being currently processed (not the one provided as @p ident)
+ * @return LY_SUCCESS if everything is ok.
+ * @return LY_EVALID if the identity is derived from itself.
+ */
+static LY_ERR
+lys_compile_identity_circular_check(struct lysc_ctx *ctx, struct lysc_ident *ident, struct lysc_ident **derived)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct ly_set recursion = {0};
+ struct lysc_ident *drv;
+
+ if (!derived) {
+ return LY_SUCCESS;
+ }
+
+ for (u = 0; u < LY_ARRAY_COUNT(derived); ++u) {
+ if (ident == derived[u]) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Identity \"%s\" is indirectly derived from itself.", ident->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ret = ly_set_add(&recursion, derived[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ for (v = 0; v < recursion.count; ++v) {
+ drv = recursion.objs[v];
+ for (u = 0; u < LY_ARRAY_COUNT(drv->derived); ++u) {
+ if (ident == drv->derived[u]) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Identity \"%s\" is indirectly derived from itself.", ident->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ret = ly_set_add(&recursion, drv->derived[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ ly_set_erase(&recursion, NULL);
+ return ret;
+}
+
+LY_ERR
+lys_compile_identity_bases(struct lysc_ctx *ctx, const struct lysp_module *base_pmod, const char **bases_p,
+ struct lysc_ident *ident, struct lysc_ident ***bases)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ const char *s, *name;
+ const struct lys_module *mod;
+ struct lysc_ident **idref;
+
+ assert(ident || bases);
+
+ if ((LY_ARRAY_COUNT(bases_p) > 1) && (ctx->pmod->version < LYS_VERSION_1_1)) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Multiple bases in %s are allowed only in YANG 1.1 modules.", ident ? "identity" : "identityref type");
+ return LY_EVALID;
+ }
+
+ LY_ARRAY_FOR(bases_p, u) {
+ s = strchr(bases_p[u], ':');
+ if (s) {
+ /* prefixed identity */
+ name = &s[1];
+ mod = ly_resolve_prefix(ctx->ctx, bases_p[u], s - bases_p[u], LY_VALUE_SCHEMA, (void *)base_pmod);
+ } else {
+ name = bases_p[u];
+ mod = base_pmod->mod;
+ }
+ if (!mod) {
+ if (ident) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid prefix used for base (%s) of identity \"%s\".", bases_p[u], ident->name);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid prefix used for base (%s) of identityref.", bases_p[u]);
+ }
+ return LY_EVALID;
+ }
+
+ idref = NULL;
+ LY_ARRAY_FOR(mod->identities, v) {
+ if (!strcmp(name, mod->identities[v].name)) {
+ if (ident) {
+ if (ident == &mod->identities[v]) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Identity \"%s\" is derived from itself.", ident->name);
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(lys_compile_identity_circular_check(ctx, &mod->identities[v], ident->derived));
+ /* we have match! store the backlink */
+ LY_ARRAY_NEW_RET(ctx->ctx, mod->identities[v].derived, idref, LY_EMEM);
+ *idref = ident;
+ } else {
+ /* we have match! store the found identity */
+ LY_ARRAY_NEW_RET(ctx->ctx, *bases, idref, LY_EMEM);
+ *idref = &mod->identities[v];
+ }
+ break;
+ }
+ }
+ if (!idref) {
+ if (ident) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Unable to find base (%s) of identity \"%s\".", bases_p[u], ident->name);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Unable to find base (%s) of identityref.", bases_p[u]);
+ }
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief For the given array of identities, set the backlinks from all their base identities.
+ *
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] idents_p Array of identities definitions from the parsed schema structure.
+ * @param[in,out] idents Array of referencing identities to which the backlinks are supposed to be set.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident **idents)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ lysc_update_path(ctx, NULL, "{identity}");
+
+ for (u = 0; u < LY_ARRAY_COUNT(*idents); ++u) {
+ /* find matching parsed identity */
+ for (v = 0; v < LY_ARRAY_COUNT(idents_p); ++v) {
+ if (idents_p[v].name == (*idents)[u].name) {
+ break;
+ }
+ }
+
+ if ((v == LY_ARRAY_COUNT(idents_p)) || !idents_p[v].bases) {
+ /* identity not found (it may be from a submodule) or identity without bases */
+ continue;
+ }
+
+ lysc_update_path(ctx, NULL, (*idents)[u].name);
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, ctx->pmod, idents_p[v].bases, &(*idents)[u], NULL));
+ lysc_update_path(ctx, NULL, NULL);
+ }
+
+ lysc_update_path(ctx, NULL, NULL);
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *expr, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool implement, struct lys_glob_unres *unres, const struct lys_module **mod_p)
+{
+ uint32_t i;
+ const char *ptr, *start, **imp_f, *all_f[] = {"*", NULL};
+ const struct lys_module *mod;
+
+ assert(implement || mod_p);
+
+ for (i = 0; i < expr->used; ++i) {
+ if ((expr->tokens[i] != LYXP_TOKEN_NAMETEST) && (expr->tokens[i] != LYXP_TOKEN_LITERAL)) {
+ /* token cannot have a prefix */
+ continue;
+ }
+
+ start = expr->expr + expr->tok_pos[i];
+ if (!(ptr = ly_strnchr(start, ':', expr->tok_len[i]))) {
+ /* token without a prefix */
+ continue;
+ }
+
+ if (!(mod = ly_resolve_prefix(ctx, start, ptr - start, format, prefix_data))) {
+ /* unknown prefix, do not care right now */
+ continue;
+ }
+
+ /* unimplemented module found */
+ if (!mod->implemented && !implement) {
+ /* should not be implemented now */
+ *mod_p = mod;
+ break;
+ }
+
+ if (!mod->implemented) {
+ /* implement if not implemented */
+ imp_f = (ctx->flags & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL;
+ LY_CHECK_RET(lys_implement((struct lys_module *)mod, imp_f, unres));
+ }
+ if (!mod->compiled) {
+ /* compile if not implemented before or only marked for compilation */
+ LY_CHECK_RET(lys_compile((struct lys_module *)mod, &unres->ds_unres));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check and optionally implement modules referenced by a when expression.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] when When to check.
+ * @param[in,out] unres Global unres structure.
+ * @return LY_ERECOMPILE if the whole dep set needs to be recompiled for these whens to evaluate.
+ * @return LY_ENOT if full check of this when should be skipped.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lys_compile_unres_when_implement(struct lysc_ctx *ctx, const struct lysc_when *when, struct lys_glob_unres *unres)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const struct lys_module *mod = NULL;
+
+ /* check whether all the referenced modules are implemented */
+ rc = lys_compile_expr_implement(ctx->ctx, when->cond, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
+ ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod);
+ if (rc) {
+ goto cleanup;
+ } else if (mod) {
+ LOGWRN(ctx->ctx, "When condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
+ when->cond->expr, mod->name);
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+
+cleanup:
+ return rc;
+}
+
+/**
+ * @brief Check when for cyclic dependencies.
+ *
+ * @param[in] set Set with all the referenced nodes.
+ * @param[in] node Node whose "when" referenced nodes are in @p set.
+ * @return LY_ERR value
+ */
+static LY_ERR
+lys_compile_unres_when_cyclic(struct lyxp_set *set, const struct lysc_node *node)
+{
+ struct lyxp_set tmp_set;
+ struct lyxp_set_scnode *xp_scnode;
+ uint32_t i, j;
+ LY_ARRAY_COUNT_TYPE u;
+ LY_ERR ret = LY_SUCCESS;
+
+ memset(&tmp_set, 0, sizeof tmp_set);
+
+ /* prepare in_ctx of the set */
+ for (i = 0; i < set->used; ++i) {
+ xp_scnode = &set->val.scnodes[i];
+
+ if (xp_scnode->in_ctx != LYXP_SET_SCNODE_START_USED) {
+ /* check node when, skip the context node (it was just checked) */
+ xp_scnode->in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ }
+ }
+
+ for (i = 0; i < set->used; ++i) {
+ xp_scnode = &set->val.scnodes[i];
+ if (xp_scnode->in_ctx != LYXP_SET_SCNODE_ATOM_CTX) {
+ /* already checked */
+ continue;
+ }
+
+ if ((xp_scnode->type != LYXP_NODE_ELEM) || !lysc_node_when(xp_scnode->scnode)) {
+ /* no when to check */
+ xp_scnode->in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ continue;
+ }
+
+ node = xp_scnode->scnode;
+ do {
+ struct lysc_when **when_list, *when;
+
+ LOG_LOCSET(node, NULL, NULL, NULL);
+ when_list = lysc_node_when(node);
+ LY_ARRAY_FOR(when_list, u) {
+ when = when_list[u];
+ ret = lyxp_atomize(set->ctx, when->cond, node->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
+ when->context, when->context, &tmp_set, LYXP_SCNODE_SCHEMA);
+ if (ret != LY_SUCCESS) {
+ LOGVAL(set->ctx, LYVE_SEMANTICS, "Invalid when condition \"%s\".", when->cond->expr);
+ LOG_LOCBACK(1, 0, 0, 0);
+ goto cleanup;
+ }
+
+ for (j = 0; j < tmp_set.used; ++j) {
+ /* skip roots'n'stuff */
+ if (tmp_set.val.scnodes[j].type == LYXP_NODE_ELEM) {
+ /* try to find this node in our set */
+ uint32_t idx;
+
+ if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
+ (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
+ LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
+ tmp_set.val.scnodes[j].scnode->name);
+ ret = LY_EVALID;
+ LOG_LOCBACK(1, 0, 0, 0);
+ goto cleanup;
+ }
+
+ /* needs to be checked, if in both sets, will be ignored */
+ tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ } else {
+ /* no when, nothing to check */
+ tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ }
+ }
+
+ /* merge this set into the global when set */
+ lyxp_set_scnode_merge(set, &tmp_set);
+ }
+
+ /* check when of non-data parents as well */
+ node = node->parent;
+
+ LOG_LOCBACK(1, 0, 0, 0);
+ } while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
+
+ /* this node when was checked (xp_scnode could have been reallocd) */
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ }
+
+cleanup:
+ lyxp_set_free_content(&tmp_set);
+ return ret;
+}
+
+LY_ERR
+lysc_check_status(struct lysc_ctx *ctx, uint16_t flags1, void *mod1, const char *name1, uint16_t flags2, void *mod2,
+ const char *name2)
+{
+ uint16_t flg1, flg2;
+
+ flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+
+ if ((flg1 < flg2) && (mod1 == mod2)) {
+ if (ctx) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "A %s definition \"%s\" is not allowed to reference %s definition \"%s\".",
+ flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1,
+ flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2);
+ }
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check when expressions of a node on a complete compiled schema tree.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] when When to check.
+ * @param[in] node Node with @p when.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_unres_when(struct lysc_ctx *ctx, const struct lysc_when *when, const struct lysc_node *node)
+{
+ struct lyxp_set tmp_set = {0};
+ uint32_t i, opts;
+ LY_ERR ret = LY_SUCCESS;
+
+ opts = LYXP_SCNODE_SCHEMA | ((node->flags & LYS_IS_OUTPUT) ? LYXP_SCNODE_OUTPUT : 0);
+
+ /* check "when" */
+ ret = lyxp_atomize(ctx->ctx, when->cond, node->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes, when->context,
+ when->context, &tmp_set, opts);
+ if (ret) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid when condition \"%s\".", when->cond->expr);
+ goto cleanup;
+ }
+
+ ctx->path[0] = '\0';
+ lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
+ for (i = 0; i < tmp_set.used; ++i) {
+ /* skip roots'n'stuff */
+ if ((tmp_set.val.scnodes[i].type == LYXP_NODE_ELEM) &&
+ (tmp_set.val.scnodes[i].in_ctx != LYXP_SET_SCNODE_START_USED)) {
+ struct lysc_node *schema = tmp_set.val.scnodes[i].scnode;
+
+ /* XPath expression cannot reference "lower" status than the node that has the definition */
+ if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
+ schema->name)) {
+ LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
+ (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
+ }
+
+ /* check dummy node children/value accessing */
+ if (lysc_data_parent(schema) == node) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* check cyclic dependencies */
+ ret = lys_compile_unres_when_cyclic(&tmp_set, node);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ lyxp_set_free_content(&tmp_set);
+ return ret;
+}
+
+/**
+ * @brief Check must expressions of a node on a complete compiled schema tree.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Node to check.
+ * @param[in] local_mods Sized array of local modules for musts of @p node at the same index.
+ * @param[in,out] unres Global unres structure.
+ * @return LY_ERECOMPILE
+ * @return LY_ERR value
+ */
+static LY_ERR
+lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const struct lysp_module **local_mods,
+ struct lys_glob_unres *unres)
+{
+ struct lyxp_set tmp_set;
+ uint32_t i, opts;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_must *musts = NULL;
+ LY_ERR ret = LY_SUCCESS;
+ const struct lys_module *mod;
+ uint16_t flg;
+
+ LOG_LOCSET(node, NULL, NULL, NULL);
+
+ memset(&tmp_set, 0, sizeof tmp_set);
+ opts = LYXP_SCNODE_SCHEMA | ((node->flags & LYS_IS_OUTPUT) ? LYXP_SCNODE_OUTPUT : 0);
+
+ musts = lysc_node_musts(node);
+ LY_ARRAY_FOR(musts, u) {
+ /* first check whether all the referenced modules are implemented */
+ mod = NULL;
+ ret = lys_compile_expr_implement(ctx->ctx, musts[u].cond, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes,
+ ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod);
+ if (ret) {
+ goto cleanup;
+ } else if (mod) {
+ LOGWRN(ctx->ctx, "Must condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
+ musts[u].cond->expr, mod->name);
+ continue;
+ }
+
+ /* check "must" */
+ ret = lyxp_atomize(ctx->ctx, musts[u].cond, node->module, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes, node,
+ node, &tmp_set, opts);
+ if (ret) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid must condition \"%s\".", musts[u].cond->expr);
+ goto cleanup;
+ }
+
+ ctx->path[0] = '\0';
+ lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
+ for (i = 0; i < tmp_set.used; ++i) {
+ /* skip roots'n'stuff */
+ if (tmp_set.val.scnodes[i].type == LYXP_NODE_ELEM) {
+ struct lysc_node *schema = tmp_set.val.scnodes[i].scnode;
+
+ /* XPath expression cannot reference "lower" status than the node that has the definition */
+ if (local_mods[u]->mod == node->module) {
+ /* use flags of the context node since the definition is local */
+ flg = node->flags;
+ } else {
+ /* definition is foreign (deviation, refine), always current */
+ flg = LYS_STATUS_CURR;
+ }
+ if (lysc_check_status(NULL, flg, local_mods[u]->mod, node->name, schema->flags, schema->module,
+ schema->name)) {
+ LOGWRN(ctx->ctx, "Must condition \"%s\" may be referencing %s node \"%s\".", musts[u].cond->expr,
+ (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
+ break;
+ }
+ }
+ }
+
+ lyxp_set_free_content(&tmp_set);
+ }
+
+cleanup:
+ lyxp_set_free_content(&tmp_set);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Remove all disabled bits/enums from a sized array.
+ *
+ * @param[in] ctx Context with the dictionary.
+ * @param[in] items Sized array of bits/enums.
+ */
+static void
+lys_compile_unres_disabled_bitenum_remove(struct lysf_ctx *ctx, struct lysc_type_bitenum_item *items)
+{
+ LY_ARRAY_COUNT_TYPE u = 0, last_u;
+
+ while (u < LY_ARRAY_COUNT(items)) {
+ if (items[u].flags & LYS_DISABLED) {
+ /* free the disabled item */
+ lysc_enum_item_free(ctx, &items[u]);
+
+ /* replace it with the following items */
+ last_u = LY_ARRAY_COUNT(items) - 1;
+ if (u < last_u) {
+ memmove(items + u, items + u + 1, (last_u - u) * sizeof *items);
+ }
+
+ /* one item less */
+ LY_ARRAY_DECREMENT(items);
+ continue;
+ }
+
+ ++u;
+ }
+}
+
+/**
+ * @brief Find and remove all disabled bits/enums in a leaf/leaf-list type.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] leaf Leaf/leaf-list to check.
+ * @return LY_ERR value
+ */
+static LY_ERR
+lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf)
+{
+ struct lysc_type **t;
+ LY_ARRAY_COUNT_TYPE u, count;
+ struct lysc_type_enum *ent;
+
+ if (leaf->type->basetype == LY_TYPE_UNION) {
+ t = ((struct lysc_type_union *)leaf->type)->types;
+ count = LY_ARRAY_COUNT(t);
+ } else {
+ t = &leaf->type;
+ count = 1;
+ }
+ for (u = 0; u < count; ++u) {
+ if ((t[u]->basetype == LY_TYPE_BITS) || (t[u]->basetype == LY_TYPE_ENUM)) {
+ /* remove all disabled items */
+ ent = (struct lysc_type_enum *)(t[u]);
+ lys_compile_unres_disabled_bitenum_remove(&ctx->free_ctx, ent->enums);
+
+ if (!LY_ARRAY_COUNT(ent->enums)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "%s type of node \"%s\" without any (or all disabled) valid values.",
+ (ent->basetype == LY_TYPE_BITS) ? "Bits" : "Enumeration", leaf->name);
+ return LY_EVALID;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check leafref for its target existence on a complete compiled schema tree.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Context node for the leafref.
+ * @param[in] lref Leafref to check/resolve.
+ * @param[in] local_mod Local module for the leafref type.
+ * @param[in,out] unres Global unres structure.
+ * @return LY_ERECOMPILE if context recompilation is needed,
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, struct lysc_type_leafref *lref,
+ const struct lysp_module *local_mod, struct lys_glob_unres *unres)
+{
+ const struct lysc_node *target = NULL;
+ struct ly_path *p;
+ struct lysc_type *type;
+ uint16_t flg;
+
+ assert(node->nodetype & (LYS_LEAF | LYS_LEAFLIST));
+
+ /* first implement all the modules in the path */
+ LY_CHECK_RET(lys_compile_expr_implement(ctx->ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+
+ /* try to find the target, current module is that of the context node (RFC 7950 6.4.1 second bullet) */
+ LY_CHECK_RET(ly_path_compile_leafref(ctx->ctx, node, ctx->ext, lref->path,
+ (node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, &p));
+
+ /* get the target node */
+ target = p[LY_ARRAY_COUNT(p) - 1].node;
+ ly_path_free(node->module->ctx, p);
+
+ if (!(target->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid leafref path \"%s\" - target node is %s instead of leaf or leaf-list.",
+ lref->path->expr, lys_nodetype2str(target->nodetype));
+ return LY_EVALID;
+ }
+
+ /* check status */
+ ctx->path[0] = '\0';
+ lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
+ ctx->path_len = strlen(ctx->path);
+ if (node->module == local_mod->mod) {
+ /* use flags of the context node since the definition is local */
+ flg = node->flags;
+ } else {
+ /* definition is foreign (deviation), always current */
+ flg = LYS_STATUS_CURR;
+ }
+ if (lysc_check_status(ctx, flg, local_mod->mod, node->name, target->flags, target->module, target->name)) {
+ return LY_EVALID;
+ }
+ ctx->path_len = 1;
+ ctx->path[1] = '\0';
+
+ /* check config */
+ if (lref->require_instance) {
+ if ((node->flags & LYS_CONFIG_W) && (target->flags & LYS_CONFIG_R)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid leafref path \"%s\" - target is supposed"
+ " to represent configuration data (as the leafref does), but it does not.", lref->path->expr);
+ return LY_EVALID;
+ }
+ }
+
+ /* check for circular chain of leafrefs */
+ for (type = ((struct lysc_node_leaf *)target)->type;
+ type && (type->basetype == LY_TYPE_LEAFREF);
+ type = ((struct lysc_type_leafref *)type)->realtype) {
+ if (type == (struct lysc_type *)lref) {
+ /* circular chain detected */
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid leafref path \"%s\" - circular chain of leafrefs detected.",
+ lref->path->expr);
+ return LY_EVALID;
+ }
+ }
+
+ /* store the type */
+ lref->realtype = ((struct lysc_node_leaf *)target)->type;
+ ++lref->realtype->refcount;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile default value(s) for leaf or leaf-list expecting a complete compiled schema tree.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Leaf or leaf-list to compile the default value(s) for.
+ * @param[in] type Type of the default value.
+ * @param[in] dflt Default value.
+ * @param[in] dflt_pmod Parsed module of the @p dflt to resolve possible prefixes.
+ * @param[in,out] storage Storage for the compiled default value.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_unres_dflt(struct lysc_ctx *ctx, struct lysc_node *node, struct lysc_type *type, const char *dflt,
+ const struct lysp_module *dflt_pmod, struct lyd_value *storage, struct lys_glob_unres *unres)
+{
+ LY_ERR ret;
+ uint32_t options;
+ struct ly_err_item *err = NULL;
+
+ options = (ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED) ? LYPLG_TYPE_STORE_IMPLEMENT : 0;
+ ret = type->plugin->store(ctx->ctx, type, dflt, strlen(dflt), options, LY_VALUE_SCHEMA, (void *)dflt_pmod,
+ LYD_HINT_SCHEMA, node, storage, unres, &err);
+ if (ret == LY_ERECOMPILE) {
+ /* fine, but we need to recompile */
+ return LY_ERECOMPILE;
+ } else if (ret == LY_EINCOMPLETE) {
+ /* we have no data so we will not be resolving it */
+ ret = LY_SUCCESS;
+ }
+
+ if (ret) {
+ LOG_LOCSET(node, NULL, NULL, NULL);
+ if (err) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid default - value does not fit the type (%s).", err->msg);
+ ly_err_free(err);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid default - value does not fit the type.");
+ }
+ LOG_LOCBACK(1, 0, 0, 0);
+ return ret;
+ }
+
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)storage->realtype)->refcount);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile default value of a leaf expecting a complete compiled schema tree.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] leaf Leaf that the default value is for.
+ * @param[in] dflt Default value to compile.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_unres_leaf_dlft(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf, struct lysp_qname *dflt,
+ struct lys_glob_unres *unres)
+{
+ LY_ERR ret;
+
+ assert(!leaf->dflt);
+
+ if (leaf->flags & (LYS_MAND_TRUE | LYS_KEY)) {
+ /* ignore default values for keys and mandatory leaves */
+ return LY_SUCCESS;
+ }
+
+ /* allocate the default value */
+ leaf->dflt = calloc(1, sizeof *leaf->dflt);
+ LY_CHECK_ERR_RET(!leaf->dflt, LOGMEM(ctx->ctx), LY_EMEM);
+
+ /* store the default value */
+ ret = lys_compile_unres_dflt(ctx, &leaf->node, leaf->type, dflt->str, dflt->mod, leaf->dflt, unres);
+ if (ret) {
+ free(leaf->dflt);
+ leaf->dflt = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Compile default values of a leaf-list expecting a complete compiled schema tree.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] llist Leaf-list that the default value(s) are for.
+ * @param[in] dflt Default value to compile, in case of a single value.
+ * @param[in] dflts Sized array of default values, in case of more values.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_unres_llist_dflts(struct lysc_ctx *ctx, struct lysc_node_leaflist *llist, struct lysp_qname *dflt,
+ struct lysp_qname *dflts, struct lys_glob_unres *unres)
+{
+ LY_ERR ret;
+ LY_ARRAY_COUNT_TYPE orig_count, u, v;
+
+ assert(dflt || dflts);
+
+ /* in case there were already some defaults and we are adding new by deviations */
+ orig_count = LY_ARRAY_COUNT(llist->dflts);
+
+ /* allocate new items */
+ LY_ARRAY_CREATE_RET(ctx->ctx, llist->dflts, orig_count + (dflts ? LY_ARRAY_COUNT(dflts) : 1), LY_EMEM);
+
+ /* fill each new default value */
+ if (dflts) {
+ LY_ARRAY_FOR(dflts, u) {
+ llist->dflts[orig_count + u] = calloc(1, sizeof **llist->dflts);
+ ret = lys_compile_unres_dflt(ctx, &llist->node, llist->type, dflts[u].str, dflts[u].mod,
+ llist->dflts[orig_count + u], unres);
+ LY_CHECK_ERR_RET(ret, free(llist->dflts[orig_count + u]), ret);
+ LY_ARRAY_INCREMENT(llist->dflts);
+ }
+ } else {
+ llist->dflts[orig_count] = calloc(1, sizeof **llist->dflts);
+ ret = lys_compile_unres_dflt(ctx, &llist->node, llist->type, dflt->str, dflt->mod,
+ llist->dflts[orig_count], unres);
+ LY_CHECK_ERR_RET(ret, free(llist->dflts[orig_count]), ret);
+ LY_ARRAY_INCREMENT(llist->dflts);
+ }
+
+ /* check default value uniqueness */
+ if (llist->flags & LYS_CONFIG_W) {
+ /* configuration data values must be unique - so check the default values */
+ for (u = orig_count; u < LY_ARRAY_COUNT(llist->dflts); ++u) {
+ for (v = 0; v < u; ++v) {
+ if (!llist->dflts[u]->realtype->plugin->compare(llist->dflts[u], llist->dflts[v])) {
+ lysc_update_path(ctx, llist->parent ? llist->parent->module : NULL, llist->name);
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Configuration leaf-list has multiple defaults of the same value \"%s\".",
+ llist->dflts[u]->realtype->plugin->print(ctx->ctx, llist->dflts[u], LY_VALUE_CANON, NULL, NULL, NULL));
+ lysc_update_path(ctx, NULL, NULL);
+ return LY_EVALID;
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Iteratively get all leafrefs from @p node
+ * if the node is of type union, otherwise just return the leafref.
+ *
+ * @param[in] node Node that may contain the leafref.
+ * @param[in,out] index Value that is passed between function calls.
+ * For each new node, initialize value of the @p index to 0, otherwise
+ * do not modify the value between calls.
+ * @return Pointer to the leafref or next leafref, otherwise NULL.
+ */
+static struct lysc_type_leafref *
+lys_type_leafref_next(const struct lysc_node *node, uint64_t *index)
+{
+ struct lysc_type_leafref *ret = NULL;
+ struct lysc_type_union *uni;
+ struct lysc_type *leaf_type;
+
+ assert(node->nodetype & LYD_NODE_TERM);
+
+ leaf_type = ((struct lysc_node_leaf *)node)->type;
+ if (leaf_type->basetype == LY_TYPE_UNION) {
+ uni = (struct lysc_type_union *)leaf_type;
+
+ /* find next union leafref */
+ while (*index < LY_ARRAY_COUNT(uni->types)) {
+ if (uni->types[*index]->basetype == LY_TYPE_LEAFREF) {
+ ret = (struct lysc_type_leafref *)uni->types[*index];
+ ++(*index);
+ break;
+ }
+
+ ++(*index);
+ }
+ } else {
+ /* return just the single leafref */
+ if (*index == 0) {
+ ++(*index);
+ assert(leaf_type->basetype == LY_TYPE_LEAFREF);
+ ret = (struct lysc_type_leafref *)leaf_type;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Finish dependency set compilation by resolving all the unres sets.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres structure with the sets to resolve.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE if the dep set needs to be recompiled.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lys_compile_unres_depset(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lysc_node *node;
+ struct lysc_type *typeiter;
+ struct lysc_type_leafref *lref;
+ struct lysc_ctx cctx = {0};
+ struct lys_depset_unres *ds_unres = &unres->ds_unres;
+ struct ly_path *path;
+ LY_ARRAY_COUNT_TYPE v;
+ struct lysc_unres_leafref *l;
+ struct lysc_unres_when *w;
+ struct lysc_unres_must *m;
+ struct lysc_unres_dflt *d;
+ uint32_t i, processed_leafrefs = 0;
+
+resolve_all:
+ /* check disabled leafrefs first */
+ while (ds_unres->disabled_leafrefs.count) {
+ /* remember index, it can change before we get to free this item */
+ i = ds_unres->disabled_leafrefs.count - 1;
+ l = ds_unres->disabled_leafrefs.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, l->node->module->parsed, l->ext);
+
+ LOG_LOCSET(l->node, NULL, NULL, NULL);
+ v = 0;
+ while ((ret == LY_SUCCESS) && (lref = lys_type_leafref_next(l->node, &v))) {
+ ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod, unres);
+ }
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ly_set_rm_index(&ds_unres->disabled_leafrefs, i, free);
+ }
+
+ /* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
+ * can be also leafref, in case it is already resolved, go through the chain and check that it does not
+ * point to the starting leafref type). The second round stores the first non-leafref type for later data validation.
+ * Also do the same check for set of the disabled leafrefs, but without the second round. */
+ for (i = processed_leafrefs; i < ds_unres->leafrefs.count; ++i) {
+ l = ds_unres->leafrefs.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, l->node->module->parsed, l->ext);
+
+ LOG_LOCSET(l->node, NULL, NULL, NULL);
+ v = 0;
+ while ((ret == LY_SUCCESS) && (lref = lys_type_leafref_next(l->node, &v))) {
+ ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod, unres);
+ }
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ for (i = processed_leafrefs; i < ds_unres->leafrefs.count; ++i) {
+ l = ds_unres->leafrefs.objs[i];
+
+ /* store pointer to the real type */
+ v = 0;
+ while ((lref = lys_type_leafref_next(l->node, &v))) {
+ for (typeiter = lref->realtype;
+ typeiter->basetype == LY_TYPE_LEAFREF;
+ typeiter = ((struct lysc_type_leafref *)typeiter)->realtype) {}
+
+ lysc_type_free(&cctx.free_ctx, lref->realtype);
+ lref->realtype = typeiter;
+ ++lref->realtype->refcount;
+ }
+
+ /* if 'goto' will be used on the 'resolve_all' label, then the current leafref will not be processed again */
+ processed_leafrefs++;
+ }
+
+ /* check when, first implement all the referenced modules (for the cyclic check in the next loop to work) */
+ i = 0;
+ while (i < ds_unres->whens.count) {
+ w = ds_unres->whens.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, w->node->module->parsed, NULL);
+
+ LOG_LOCSET(w->node, NULL, NULL, NULL);
+ r = lys_compile_unres_when_implement(&cctx, w->when, unres);
+ LOG_LOCBACK(w->node ? 1 : 0, 0, 0, 0);
+
+ if (r == LY_ENOT) {
+ /* skip full when check, remove from the set */
+ free(w);
+ ly_set_rm_index(&ds_unres->whens, i, NULL);
+ continue;
+ } else if (r) {
+ /* error */
+ ret = r;
+ goto cleanup;
+ }
+
+ ++i;
+ }
+ while (ds_unres->whens.count) {
+ i = ds_unres->whens.count - 1;
+ w = ds_unres->whens.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, w->node->module->parsed, NULL);
+
+ LOG_LOCSET(w->node, NULL, NULL, NULL);
+ ret = lys_compile_unres_when(&cctx, w->when, w->node);
+ LOG_LOCBACK(w->node ? 1 : 0, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ free(w);
+ ly_set_rm_index(&ds_unres->whens, i, NULL);
+ }
+
+ /* check must */
+ while (ds_unres->musts.count) {
+ i = ds_unres->musts.count - 1;
+ m = ds_unres->musts.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, m->node->module->parsed, m->ext);
+
+ LOG_LOCSET(m->node, NULL, NULL, NULL);
+ ret = lys_compile_unres_must(&cctx, m->node, m->local_mods, unres);
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ lysc_unres_must_free(m);
+ ly_set_rm_index(&ds_unres->musts, i, NULL);
+ }
+
+ /* remove disabled enums/bits */
+ while (ds_unres->disabled_bitenums.count) {
+ i = ds_unres->disabled_bitenums.count - 1;
+ node = ds_unres->disabled_bitenums.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, node->module->parsed, NULL);
+
+ LOG_LOCSET(node, NULL, NULL, NULL);
+ ret = lys_compile_unres_disabled_bitenum(&cctx, (struct lysc_node_leaf *)node);
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ly_set_rm_index(&ds_unres->disabled_bitenums, i, NULL);
+ }
+
+ /* finish incomplete default values compilation */
+ while (ds_unres->dflts.count) {
+ i = ds_unres->dflts.count - 1;
+ d = ds_unres->dflts.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, d->leaf->module->parsed, NULL);
+
+ LOG_LOCSET(&d->leaf->node, NULL, NULL, NULL);
+ if (d->leaf->nodetype == LYS_LEAF) {
+ ret = lys_compile_unres_leaf_dlft(&cctx, d->leaf, d->dflt, unres);
+ } else {
+ ret = lys_compile_unres_llist_dflts(&cctx, d->llist, d->dflt, d->dflts, unres);
+ }
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ lysc_unres_dflt_free(ctx, d);
+ ly_set_rm_index(&ds_unres->dflts, i, NULL);
+ }
+
+ /* some unres items may have been added */
+ if ((processed_leafrefs != ds_unres->leafrefs.count) || ds_unres->disabled_leafrefs.count ||
+ ds_unres->whens.count || ds_unres->musts.count || ds_unres->dflts.count) {
+ goto resolve_all;
+ }
+
+ /* finally, remove all disabled nodes */
+ for (i = 0; i < ds_unres->disabled.count; ++i) {
+ node = ds_unres->disabled.snodes[i];
+ if (node->flags & LYS_KEY) {
+ LOG_LOCSET(node, NULL, NULL, NULL);
+ LOGVAL(ctx, LYVE_REFERENCE, "Key \"%s\" is disabled.", node->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ LYSC_CTX_INIT_PMOD(cctx, node->module->parsed, NULL);
+
+ lysc_node_free(&cctx.free_ctx, node, 1);
+ }
+
+ /* also check if the leafref target has not been disabled */
+ for (i = 0; i < ds_unres->leafrefs.count; ++i) {
+ l = ds_unres->leafrefs.objs[i];
+ LYSC_CTX_INIT_PMOD(cctx, l->node->module->parsed, l->ext);
+
+ v = 0;
+ while ((lref = lys_type_leafref_next(l->node, &v))) {
+ ret = ly_path_compile_leafref(cctx.ctx, l->node, cctx.ext, lref->path,
+ (l->node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, &path);
+ ly_path_free(l->node->module->ctx, path);
+
+ assert(ret != LY_ERECOMPILE);
+ if (ret) {
+ LOG_LOCSET(l->node, NULL, NULL, NULL);
+ LOGVAL(ctx, LYVE_REFERENCE, "Target of leafref \"%s\" cannot be referenced because it is disabled.",
+ l->node->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ lysf_ctx_erase(&cctx.free_ctx);
+ return ret;
+}
+
+/**
+ * @brief Erase dep set unres.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres structure with the sets to resolve.
+ */
+static void
+lys_compile_unres_depset_erase(const struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ uint32_t i;
+
+ ly_set_erase(&unres->ds_unres.whens, free);
+ for (i = 0; i < unres->ds_unres.musts.count; ++i) {
+ lysc_unres_must_free(unres->ds_unres.musts.objs[i]);
+ }
+ ly_set_erase(&unres->ds_unres.musts, NULL);
+ ly_set_erase(&unres->ds_unres.leafrefs, free);
+ for (i = 0; i < unres->ds_unres.dflts.count; ++i) {
+ lysc_unres_dflt_free(ctx, unres->ds_unres.dflts.objs[i]);
+ }
+ ly_set_erase(&unres->ds_unres.dflts, NULL);
+ ly_set_erase(&unres->ds_unres.disabled, NULL);
+ ly_set_erase(&unres->ds_unres.disabled_leafrefs, free);
+ ly_set_erase(&unres->ds_unres.disabled_bitenums, NULL);
+}
+
+/**
+ * @brief Compile all flagged modules in a dependency set, recursively if recompilation is needed.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] dep_set Dependency set to compile.
+ * @param[in,out] unres Global unres to use.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_depset_r(struct ly_ctx *ctx, struct ly_set *dep_set, struct lys_glob_unres *unres)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysf_ctx fctx = {.ctx = ctx};
+ struct lys_module *mod;
+ uint32_t i;
+
+ for (i = 0; i < dep_set->count; ++i) {
+ mod = dep_set->objs[i];
+ if (!mod->to_compile) {
+ /* skip */
+ continue;
+ }
+ assert(mod->implemented);
+
+ /* free the compiled module, if any */
+ lysc_module_free(&fctx, mod->compiled);
+ mod->compiled = NULL;
+
+ /* (re)compile the module */
+ LY_CHECK_GOTO(ret = lys_compile(mod, &unres->ds_unres), cleanup);
+ }
+
+ /* resolve dep set unres */
+ ret = lys_compile_unres_depset(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);
+ return lys_compile_depset_r(ctx, dep_set, unres);
+ } else if (ret) {
+ /* error */
+ goto cleanup;
+ }
+
+ /* 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];
+ mod->to_compile = 0;
+ }
+
+cleanup:
+ lys_compile_unres_depset_erase(ctx, unres);
+ lysf_ctx_erase(&fctx);
+ return ret;
+}
+
+/**
+ * @brief Check if-feature of all features of all modules in a dep set.
+ *
+ * @param[in] dep_set Dep set to check.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_depset_check_features(struct ly_set *dep_set)
+{
+ struct lys_module *mod;
+ uint32_t i;
+
+ for (i = 0; i < dep_set->count; ++i) {
+ mod = dep_set->objs[i];
+ if (!mod->to_compile) {
+ /* skip */
+ continue;
+ }
+
+ /* check features of this module */
+ LY_CHECK_RET(lys_check_features(mod->parsed));
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_compile_depset_all(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ uint32_t i;
+
+ for (i = 0; i < unres->dep_sets.count; ++i) {
+ LY_CHECK_RET(lys_compile_depset_check_features(unres->dep_sets.objs[i]));
+ LY_CHECK_RET(lys_compile_depset_r(ctx, unres->dep_sets.objs[i], unres));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Finish compilation of all the module unres sets in a compile context.
+ *
+ * @param[in] ctx Compile context with unres sets.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_unres_mod(struct lysc_ctx *ctx)
+{
+ struct lysc_augment *aug;
+ struct lysc_deviation *dev;
+ struct lys_module *orig_mod = ctx->cur_mod;
+ uint32_t i;
+
+ /* check that all augments were applied */
+ for (i = 0; i < ctx->augs.count; ++i) {
+ aug = ctx->augs.objs[i];
+ ctx->cur_mod = aug->aug_pmod->mod;
+ if (aug->ext) {
+ lysc_update_path(ctx, NULL, "{extension}");
+ lysc_update_path(ctx, NULL, aug->ext->name);
+ }
+ lysc_update_path(ctx, NULL, "{augment}");
+ lysc_update_path(ctx, NULL, aug->nodeid->expr);
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Augment%s target node \"%s\" from module \"%s\" was not found.",
+ aug->ext ? " extension" : "", aug->nodeid->expr, LYSP_MODULE_NAME(aug->aug_pmod));
+ ctx->cur_mod = orig_mod;
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ if (aug->ext) {
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ }
+ }
+ if (ctx->augs.count) {
+ return LY_ENOTFOUND;
+ }
+
+ /* check that all deviations were applied */
+ for (i = 0; i < ctx->devs.count; ++i) {
+ dev = ctx->devs.objs[i];
+ lysc_update_path(ctx, NULL, "{deviation}");
+ lysc_update_path(ctx, NULL, dev->nodeid->expr);
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Deviation(s) target node \"%s\" from module \"%s\" was not found.",
+ dev->nodeid->expr, LYSP_MODULE_NAME(dev->dev_pmods[0]));
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ }
+ if (ctx->devs.count) {
+ return LY_ENOTFOUND;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Erase all the module unres sets in a compile context.
+ *
+ * @param[in] ctx Compile context with unres sets.
+ * @param[in] error Whether the compilation finished with an error or not.
+ */
+static void
+lys_compile_unres_mod_erase(struct lysc_ctx *ctx, ly_bool error)
+{
+ uint32_t i;
+
+ ly_set_erase(&ctx->groupings, NULL);
+ ly_set_erase(&ctx->tpdf_chain, NULL);
+
+ if (!error) {
+ /* there can be no leftover deviations or augments */
+ LY_CHECK_ERR_RET(ctx->augs.count, LOGINT(ctx->ctx), );
+ LY_CHECK_ERR_RET(ctx->devs.count, LOGINT(ctx->ctx), );
+
+ ly_set_erase(&ctx->augs, NULL);
+ ly_set_erase(&ctx->devs, NULL);
+ ly_set_erase(&ctx->uses_augs, NULL);
+ ly_set_erase(&ctx->uses_rfns, NULL);
+ } else {
+ for (i = 0; i < ctx->augs.count; ++i) {
+ lysc_augment_free(ctx->ctx, ctx->augs.objs[i]);
+ }
+ ly_set_erase(&ctx->augs, NULL);
+ for (i = 0; i < ctx->devs.count; ++i) {
+ lysc_deviation_free(ctx->ctx, ctx->devs.objs[i]);
+ }
+ ly_set_erase(&ctx->devs, NULL);
+ for (i = 0; i < ctx->uses_augs.count; ++i) {
+ lysc_augment_free(ctx->ctx, ctx->uses_augs.objs[i]);
+ }
+ ly_set_erase(&ctx->uses_augs, NULL);
+ for (i = 0; i < ctx->uses_rfns.count; ++i) {
+ lysc_refine_free(ctx->ctx, ctx->uses_rfns.objs[i]);
+ }
+ ly_set_erase(&ctx->uses_rfns, NULL);
+ }
+}
+
+LY_ERR
+lys_compile(struct lys_module *mod, struct lys_depset_unres *unres)
+{
+ struct lysc_ctx ctx;
+ struct lysc_module *mod_c = NULL;
+ struct lysp_module *sp;
+ struct lysp_submodule *submod;
+ struct lysp_node *pnode;
+ struct lysp_node_grp *grp;
+ LY_ARRAY_COUNT_TYPE u;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(NULL, mod, mod->parsed, !mod->compiled, mod->ctx, LY_EINVAL);
+
+ assert(mod->implemented && mod->to_compile);
+
+ sp = mod->parsed;
+ LYSC_CTX_INIT_PMOD(ctx, sp, NULL);
+ ctx.unres = unres;
+
+ ++mod->ctx->change_count;
+ mod->compiled = mod_c = calloc(1, sizeof *mod_c);
+ LY_CHECK_ERR_RET(!mod_c, LOGMEM(mod->ctx), LY_EMEM);
+ mod_c->mod = mod;
+
+ /* compile augments and deviations of our module from other modules so they can be applied during compilation */
+ LY_CHECK_GOTO(ret = lys_precompile_own_augments(&ctx), cleanup);
+ LY_CHECK_GOTO(ret = lys_precompile_own_deviations(&ctx), cleanup);
+
+ /* data nodes */
+ LY_LIST_FOR(sp->data, pnode) {
+ LY_CHECK_GOTO(ret = lys_compile_node(&ctx, pnode, NULL, 0, NULL), cleanup);
+ }
+
+ /* top-level RPCs */
+ LY_LIST_FOR((struct lysp_node *)sp->rpcs, pnode) {
+ LY_CHECK_GOTO(ret = lys_compile_node(&ctx, pnode, NULL, 0, NULL), cleanup);
+ }
+
+ /* top-level notifications */
+ LY_LIST_FOR((struct lysp_node *)sp->notifs, pnode) {
+ LY_CHECK_GOTO(ret = lys_compile_node(&ctx, pnode, NULL, 0, NULL), cleanup);
+ }
+
+ /* module extension instances */
+ COMPILE_EXTS_GOTO(&ctx, sp->exts, mod_c->exts, mod_c, ret, cleanup);
+
+ /* the same for submodules */
+ LY_ARRAY_FOR(sp->includes, u) {
+ submod = sp->includes[u].submodule;
+ ctx.pmod = (struct lysp_module *)submod;
+
+ LY_LIST_FOR(submod->data, pnode) {
+ ret = lys_compile_node(&ctx, pnode, NULL, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ LY_LIST_FOR((struct lysp_node *)submod->rpcs, pnode) {
+ ret = lys_compile_node(&ctx, pnode, NULL, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ LY_LIST_FOR((struct lysp_node *)submod->notifs, pnode) {
+ ret = lys_compile_node(&ctx, pnode, NULL, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ COMPILE_EXTS_GOTO(&ctx, submod->exts, mod_c->exts, mod_c, ret, cleanup);
+ }
+ ctx.pmod = sp;
+
+ /* validate non-instantiated groupings from the parsed schema,
+ * without it we would accept even the schemas with invalid grouping specification */
+ ctx.compile_opts |= LYS_COMPILE_GROUPING;
+ LY_LIST_FOR(sp->groupings, grp) {
+ if (!(grp->flags & LYS_USED_GRP)) {
+ LY_CHECK_GOTO(ret = lys_compile_grouping(&ctx, NULL, grp), cleanup);
+ }
+ }
+ LY_LIST_FOR(sp->data, pnode) {
+ LY_LIST_FOR((struct lysp_node_grp *)lysp_node_groupings(pnode), grp) {
+ if (!(grp->flags & LYS_USED_GRP)) {
+ LY_CHECK_GOTO(ret = lys_compile_grouping(&ctx, pnode, grp), cleanup);
+ }
+ }
+ }
+ LY_ARRAY_FOR(sp->includes, u) {
+ submod = sp->includes[u].submodule;
+ ctx.pmod = (struct lysp_module *)submod;
+
+ LY_LIST_FOR(submod->groupings, grp) {
+ if (!(grp->flags & LYS_USED_GRP)) {
+ LY_CHECK_GOTO(ret = lys_compile_grouping(&ctx, NULL, grp), cleanup);
+ }
+ }
+ LY_LIST_FOR(submod->data, pnode) {
+ LY_LIST_FOR((struct lysp_node_grp *)lysp_node_groupings(pnode), grp) {
+ if (!(grp->flags & LYS_USED_GRP)) {
+ LY_CHECK_GOTO(ret = lys_compile_grouping(&ctx, pnode, grp), cleanup);
+ }
+ }
+ }
+ }
+ ctx.pmod = sp;
+
+ LOG_LOCBACK(0, 0, 1, 0);
+
+ /* finish compilation for all unresolved module items in the context */
+ LY_CHECK_GOTO(ret = lys_compile_unres_mod(&ctx), cleanup);
+
+cleanup:
+ LOG_LOCBACK(0, 0, 1, 0);
+ lys_compile_unres_mod_erase(&ctx, ret);
+ if (ret) {
+ lysc_module_free(&ctx.free_ctx, mod_c);
+ mod->compiled = NULL;
+ }
+ return ret;
+}
+
+LY_ERR
+lys_compile_identities(struct lys_module *mod)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysc_ctx ctx;
+ struct lysp_submodule *submod;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* pre-compile identities of the module and any submodules */
+ rc = lys_identity_precompile(NULL, mod->ctx, mod->parsed, mod->parsed->identities, &mod->identities);
+ LY_CHECK_GOTO(rc, cleanup);
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ submod = mod->parsed->includes[u].submodule;
+ rc = lys_identity_precompile(NULL, mod->ctx, (struct lysp_module *)submod, submod->identities, &mod->identities);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ /* prepare context */
+ LYSC_CTX_INIT_PMOD(ctx, mod->parsed, NULL);
+
+ if (mod->parsed->identities) {
+ rc = lys_compile_identities_derived(&ctx, mod->parsed->identities, &mod->identities);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ lysc_update_path(&ctx, NULL, "{submodule}");
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ submod = mod->parsed->includes[u].submodule;
+ if (submod->identities) {
+ ctx.pmod = (struct lysp_module *)submod;
+ lysc_update_path(&ctx, NULL, submod->name);
+ rc = lys_compile_identities_derived(&ctx, submod->identities, &mod->identities);
+ lysc_update_path(&ctx, NULL, NULL);
+ }
+
+ if (rc) {
+ break;
+ }
+ }
+ lysc_update_path(&ctx, NULL, NULL);
+
+cleanup:
+ /* always needed when using lysc_update_path() */
+ LOG_LOCBACK(0, 0, 1, 0);
+ return rc;
+}
+
+/**
+ * @brief Check whether a module does not have any (recursive) compiled import.
+ *
+ * @param[in] mod Module to examine.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE on required recompilation of the dep set.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lys_has_compiled_import_r(struct lys_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lys_module *m;
+
+ LY_ARRAY_FOR(mod->parsed->imports, u) {
+ m = mod->parsed->imports[u].module;
+ if (!m->implemented) {
+ continue;
+ }
+
+ if (!m->to_compile) {
+ /* module was not/will not be compiled in this compilation (so disabled nodes are not present) */
+ m->to_compile = 1;
+ return LY_ERECOMPILE;
+ }
+
+ /* recursive */
+ LY_CHECK_RET(lys_has_compiled_import_r(m));
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_implement(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
+{
+ LY_ERR ret;
+ struct lys_module *m;
+
+ assert(!mod->implemented);
+
+ /* check collision with other implemented revision */
+ m = ly_ctx_get_module_implemented(mod->ctx, mod->name);
+ if (m) {
+ assert(m != mod);
+ if (!strcmp(mod->name, "yang") && (strcmp(m->revision, mod->revision) > 0)) {
+ /* special case for newer internal module, continue */
+ LOGVRB("Internal module \"%s@%s\" is already implemented in revision \"%s\", using it instead.",
+ mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
+ } else {
+ LOGERR(mod->ctx, LY_EDENIED, "Module \"%s@%s\" is already implemented in revision \"%s\".",
+ mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
+ return LY_EDENIED;
+ }
+ }
+
+ /* set features */
+ ret = lys_set_features(mod->parsed, features);
+ if (ret && (ret != LY_EEXIST)) {
+ return ret;
+ }
+
+ /*
+ * mark the module implemented, which means
+ * 1) to (re)compile it only ::lys_compile() call is needed
+ * 2) its compilation will never cause new modules to be implemented (::lys_compile() does not return ::LY_ERECOMPILE)
+ * but there can be some unres items added that do
+ */
+ mod->implemented = 1;
+
+ /* this module is compiled in this compilation */
+ mod->to_compile = 1;
+
+ /* add the module into newly implemented module set */
+ LY_CHECK_RET(ly_set_add(&unres->implementing, mod, 1, NULL));
+
+ /* mark target modules with our augments and deviations */
+ LY_CHECK_RET(lys_precompile_augments_deviations(mod, unres));
+
+ /* check whether this module may reference any modules compiled previously */
+ LY_CHECK_RET(lys_has_compiled_import_r(mod));
+
+ return LY_SUCCESS;
+}
diff --git a/src/schema_compile.h b/src/schema_compile.h
new file mode 100644
index 0000000..d45156e
--- /dev/null
+++ b/src/schema_compile.h
@@ -0,0 +1,397 @@
+/**
+ * @file schema_compile.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for schema compilation.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SCHEMA_COMPILE_H_
+#define LY_SCHEMA_COMPILE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "plugins_exts.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_schema.h"
+#include "tree_schema_free.h"
+
+struct lyxp_expr;
+
+/**
+ * @brief YANG schema compilation context.
+ */
+struct lysc_ctx {
+ struct ly_ctx *ctx; /**< libyang context */
+ struct lys_module *cur_mod; /**< module currently being compiled,
+ - identifier/path - used as the current module for unprefixed nodes
+ - augment - module where the augment is defined
+ - deviation - module where the deviation is defined
+ - uses - module where the uses is defined */
+ struct lysp_module *pmod; /**< parsed module being processed,
+ - identifier/path - used for searching imports to resolve prefixed nodes
+ - augment - module where the augment is defined
+ - deviation - module where the deviation is defined
+ - uses - module where the grouping is defined */
+ struct lysc_ext_instance *ext; /**< extension instance being processed and serving as a source for its substatements
+ instead of the module itself */
+ struct ly_set groupings; /**< stack for groupings circular check */
+ struct ly_set tpdf_chain; /**< stack for typedefs circular check */
+ struct ly_set augs; /**< set of compiled non-applied top-level augments (stored ::lysc_augment *) */
+ struct ly_set devs; /**< set of compiled non-applied deviations (stored ::lysc_deviation *) */
+ struct ly_set uses_augs; /**< set of compiled non-applied uses augments (stored ::lysc_augment *) */
+ struct ly_set uses_rfns; /**< set of compiled non-applied uses refines (stored ::lysc_refine *) */
+ struct lys_depset_unres *unres; /**< dependency set unres sets */
+ uint32_t path_len; /**< number of path bytes used */
+ uint32_t compile_opts; /**< various @ref scflags. */
+ struct lysf_ctx free_ctx; /**< freeing context for errors/recompilation */
+
+#define LYSC_CTX_BUFSIZE 4078
+ char path[LYSC_CTX_BUFSIZE];/**< Path identifying the schema node currently being processed */
+};
+
+/**
+ * @brief Initalize local compilation context using libyang context.
+ *
+ * @param[out] CCTX Compile context.
+ * @param[in] CTX libyang context.
+ */
+#define LYSC_CTX_INIT_CTX(CCTX, CTX) \
+ memset(&(CCTX), 0, sizeof (CCTX)); \
+ (CCTX).ctx = (CTX); \
+ (CCTX).path_len = 1; \
+ (CCTX).path[0] = '/'; \
+ (CCTX).free_ctx.ctx = (CTX)
+
+/**
+ * @brief Initalize local compilation context using a parsed module.
+ *
+ * @param[out] CCTX Compile context.
+ * @param[in] PMOD Parsed module.
+ * @param[in] EXT Ancestor extension instance.
+ */
+#define LYSC_CTX_INIT_PMOD(CCTX, PMOD, EXT) \
+ memset(&(CCTX), 0, sizeof (CCTX)); \
+ (CCTX).ctx = (PMOD)->mod->ctx; \
+ (CCTX).cur_mod = (PMOD)->mod; \
+ (CCTX).pmod = (PMOD); \
+ (CCTX).ext = (EXT); \
+ (CCTX).path_len = 1; \
+ (CCTX).path[0] = '/'; \
+ (CCTX).free_ctx.ctx = (PMOD)->mod->ctx
+
+/**
+ * @brief Structure for unresolved items that may depend on any implemented module data in the dependency set
+ * so their resolution can only be performed after the whole dep set compilation is done.
+ */
+struct lys_depset_unres {
+ struct ly_set whens; /**< nodes with when to check */
+ struct ly_set musts; /**< set of musts to check */
+ struct ly_set leafrefs; /**< to validate target of leafrefs */
+ struct ly_set dflts; /**< set of incomplete default values */
+ struct ly_set disabled; /**< set of compiled nodes whose if-feature(s) was not satisfied
+ (stored ::lysc_node *) */
+ struct ly_set disabled_leafrefs; /**< subset of the lys_depset_unres.disabled to validate target of disabled leafrefs */
+ struct ly_set disabled_bitenums; /**< set of enumation/bits leaves/leaf-lists with bits/enums to disable
+ (stored ::lysc_node_leaf *) */
+};
+
+/**
+ * @brief Unres structure global for compilation.
+ */
+struct lys_glob_unres {
+ struct ly_set dep_sets; /**< set of dependency sets of modules, see ::lys_compile_depset_all() */
+ struct ly_set implementing; /**< set of YANG schemas being atomically implemented (compiled); the first added
+ module is always the explicitly implemented module, the other ones are dependencies */
+ struct ly_set creating; /**< set of YANG schemas being atomically created (parsed); it is a subset of implemented
+ and all these modules are freed if any error occurs */
+ struct lys_depset_unres ds_unres; /**< unres specific for the current dependency set */
+};
+
+/**
+ * @brief Structure for storing schema node with a when expression.
+ */
+struct lysc_unres_when {
+ struct lysc_node *node; /**< node with the when expression */
+ struct lysc_when *when; /**< one when expression of the node */
+};
+
+/**
+ * @brief Structure for storing schema nodes with must expressions and local module for each of them.
+ */
+struct lysc_unres_must {
+ struct lysc_node *node; /**< node with the must expression(s) */
+ const struct lysp_module **local_mods; /**< sized array of local modules for must(s) */
+ struct lysc_ext_instance *ext; /**< ancestor extension instance of the must(s) */
+};
+
+/**
+ * @brief Structure for storing leafref node and its local module.
+ */
+struct lysc_unres_leafref {
+ struct lysc_node *node; /**< leaf/leaf-list node with leafref type */
+ const struct lysp_module *local_mod; /**< local module of the leafref type */
+ struct lysc_ext_instance *ext; /**< ancestor extension instance of the leafref */
+};
+
+/**
+ * @brief Structure for remembering default values of leaves and leaf-lists. They are resolved at schema compilation
+ * end when the whole schema tree is available.
+ */
+struct lysc_unres_dflt {
+ union {
+ struct lysc_node_leaf *leaf;
+ struct lysc_node_leaflist *llist;
+ };
+ struct lysp_qname *dflt;
+ struct lysp_qname *dflts; /**< this is a sized array */
+};
+
+/**
+ * @brief Duplicate string into dictionary
+ * @param[in] CTX libyang context of the dictionary.
+ * @param[in] ORIG String to duplicate.
+ * @param[out] DUP Where to store the result.
+ * @param[out] RET Where to store the return code.
+ */
+#define DUP_STRING(CTX, ORIG, DUP, RET) RET = lydict_insert(CTX, ORIG, 0, &(DUP))
+#define DUP_STRING_RET(CTX, ORIG, DUP) LY_CHECK_RET(lydict_insert(CTX, ORIG, 0, &(DUP)))
+#define DUP_STRING_GOTO(CTX, ORIG, DUP, RET, GOTO) LY_CHECK_GOTO(RET = lydict_insert(CTX, ORIG, 0, &(DUP)), GOTO)
+
+#define DUP_ARRAY(CTX, ORIG_ARRAY, NEW_ARRAY, DUP_FUNC) \
+ if (ORIG_ARRAY) { \
+ LY_ARRAY_COUNT_TYPE __u; \
+ LY_ARRAY_CREATE_RET(CTX, NEW_ARRAY, LY_ARRAY_COUNT(ORIG_ARRAY), LY_EMEM); \
+ LY_ARRAY_FOR(ORIG_ARRAY, __u) { \
+ LY_ARRAY_INCREMENT(NEW_ARRAY); \
+ LY_CHECK_RET(DUP_FUNC(CTX, &(ORIG_ARRAY)[__u], &(NEW_ARRAY)[__u])); \
+ } \
+ }
+
+#define DUP_ARRAY2(CTX, PMOD, ORIG_ARRAY, NEW_ARRAY, DUP_FUNC) \
+ if (ORIG_ARRAY) { \
+ LY_ARRAY_COUNT_TYPE __u; \
+ LY_ARRAY_CREATE_RET(CTX, NEW_ARRAY, LY_ARRAY_COUNT(ORIG_ARRAY), LY_EMEM); \
+ LY_ARRAY_FOR(ORIG_ARRAY, __u) { \
+ LY_ARRAY_INCREMENT(NEW_ARRAY); \
+ LY_CHECK_RET(DUP_FUNC(CTX, PMOD, &(ORIG_ARRAY)[__u], &(NEW_ARRAY)[__u])); \
+ } \
+ }
+
+#define DUP_EXTS(CTX, PMOD, PARENT, PARENT_STMT, ORIG_ARRAY, NEW_ARRAY, DUP_FUNC) \
+ if (ORIG_ARRAY) { \
+ LY_ARRAY_COUNT_TYPE __u, __new_start; \
+ __new_start = LY_ARRAY_COUNT(NEW_ARRAY); \
+ LY_ARRAY_CREATE_RET(CTX, NEW_ARRAY, LY_ARRAY_COUNT(ORIG_ARRAY), LY_EMEM); \
+ LY_ARRAY_FOR(ORIG_ARRAY, __u) { \
+ LY_ARRAY_INCREMENT(NEW_ARRAY); \
+ LY_CHECK_RET(DUP_FUNC(CTX, PMOD, PARENT, PARENT_STMT, &(ORIG_ARRAY)[__u], &(NEW_ARRAY)[__new_start + __u])); \
+ } \
+ }
+
+#define COMPILE_OP_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, PARENT, FUNC, USES_STATUS, RET, GOTO) \
+ if (ARRAY_P) { \
+ LY_ARRAY_COUNT_TYPE __u = (ARRAY_C) ? LY_ARRAY_COUNT(ARRAY_C) : 0; \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, __u + LY_ARRAY_COUNT(ARRAY_P), RET, GOTO); \
+ LY_ARRAY_FOR(ARRAY_P, __u) { \
+ LY_ARRAY_INCREMENT(ARRAY_C); \
+ RET = FUNC(CTX, &(ARRAY_P)[__u], PARENT, &(ARRAY_C)[LY_ARRAY_COUNT(ARRAY_C) - 1], USES_STATUS); \
+ if (RET == LY_EDENIED) { \
+ LY_ARRAY_DECREMENT(ARRAY_C); \
+ RET = LY_SUCCESS; \
+ } else if (RET) { \
+ goto GOTO; \
+ } \
+ } \
+ }
+
+#define COMPILE_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, FUNC, RET, GOTO) \
+ if (ARRAY_P) { \
+ LY_ARRAY_COUNT_TYPE __u = (ARRAY_C) ? LY_ARRAY_COUNT(ARRAY_C) : 0; \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, __u + LY_ARRAY_COUNT(ARRAY_P), RET, GOTO); \
+ LY_ARRAY_FOR(ARRAY_P, __u) { \
+ LY_ARRAY_INCREMENT(ARRAY_C); \
+ RET = FUNC(CTX, &(ARRAY_P)[__u], &(ARRAY_C)[LY_ARRAY_COUNT(ARRAY_C) - 1]); \
+ LY_CHECK_GOTO(RET, GOTO); \
+ } \
+ }
+
+#define COMPILE_EXTS_GOTO(CTX, EXTS_P, EXT_C, PARENT, RET, GOTO) \
+ if (EXTS_P) { \
+ LY_ARRAY_COUNT_TYPE __u = (EXT_C) ? LY_ARRAY_COUNT(EXT_C) : 0; \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, EXT_C, __u + LY_ARRAY_COUNT(EXTS_P), RET, GOTO); \
+ LY_ARRAY_FOR(EXTS_P, __u) { \
+ LY_ARRAY_INCREMENT(EXT_C); \
+ RET = lys_compile_ext(CTX, &(EXTS_P)[__u], &(EXT_C)[LY_ARRAY_COUNT(EXT_C) - 1], PARENT); \
+ if (RET == LY_ENOT) { \
+ LY_ARRAY_DECREMENT(EXT_C); \
+ RET = LY_SUCCESS; \
+ } else if (RET) { \
+ goto GOTO; \
+ } \
+ } \
+ }
+
+/**
+ * @brief Update path in the compile context, which is used for logging where the compilation failed.
+ *
+ * @param[in] ctx Compile context with the path.
+ * @param[in] parent_module Module of the current node's parent to check difference with the currently processed module
+ * (taken from @p ctx).
+ * @param[in] name Name of the node to update path with. If NULL, the last segment is removed. If the format is
+ * `{keyword}`, the following call updates the segment to the form `{keyword='name'}` (to remove this compound segment,
+ * 2 calls with NULL @p name must be used).
+ */
+void lysc_update_path(struct lysc_ctx *ctx, const struct lys_module *parent_module, const char *name);
+
+/**
+ * @brief Fill in the prepared compiled extension instance structure according to the parsed extension instance.
+ *
+ * @param[in] ctx Compilation context.
+ * @param[in] extp Parsed extension instance.
+ * @param[in,out] ext Prepared compiled extension instance.
+ * @param[in] parent Extension instance parent.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the extension is disabled and should be ignored.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *extp, struct lysc_ext_instance *ext, void *parent);
+
+/**
+ * @brief Compile information from the identity statement
+ *
+ * The backlinks to the identities derived from this one are supposed to be filled later via ::lys_compile_identity_bases().
+ *
+ * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p parsed_mod.
+ * @param[in] ctx libyang context.
+ * @param[in] parsed_mod Module with the identities.
+ * @param[in] identities_p Array of the parsed identity definitions to precompile.
+ * @param[in,out] identities Pointer to the storage of the (pre)compiled identities array where the new identities are
+ * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed identities are going
+ * to be processed.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_identity_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
+ const struct lysp_ident *identities_p, struct lysc_ident **identities);
+
+/**
+ * @brief Find and process the referenced base identities from another identity or identityref
+ *
+ * For bases in identity set backlinks to them from the base identities. For identityref, store
+ * the array of pointers to the base identities. So one of the ident or bases parameter must be set
+ * to distinguish these two use cases.
+ *
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] base_pmod Module where to resolve @p bases_p prefixes.
+ * @param[in] bases_p Array of names (including prefix if necessary) of base identities.
+ * @param[in] ident Referencing identity to work with, NULL for identityref.
+ * @param[in] bases Array of bases of identityref to fill in.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_identity_bases(struct lysc_ctx *ctx, const struct lysp_module *base_pmod, const char **bases_p,
+ struct lysc_ident *ident, struct lysc_ident ***bases);
+
+/**
+ * @brief Perform a complet compilation of identites in a module and all its submodules.
+ *
+ * @param[in] mod Module to process.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_identities(struct lys_module *mod);
+
+/**
+ * @brief Compile schema into a validated schema linking all the references. Must have been implemented before.
+ *
+ * @param[in] mod Pointer to the schema structure holding pointers to both schema structure types. The ::lys_module#parsed
+ * member is used as input and ::lys_module#compiled is used to hold the result of the compilation.
+ * @param[in,out] unres Dep set unres structure to add to.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_compile(struct lys_module *mod, struct lys_depset_unres *unres);
+
+/**
+ * @brief Check statement's status for invalid combination.
+ *
+ * The modX parameters are used just to determine if both flags are in the same module,
+ * so any of the schema module structure can be used, but both modules must be provided
+ * in the same type.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] flags1 Flags of the referencing node.
+ * @param[in] mod1 Module of the referencing node,
+ * @param[in] name1 Schema node name of the referencing node.
+ * @param[in] flags2 Flags of the referenced node.
+ * @param[in] mod2 Module of the referenced node,
+ * @param[in] name2 Schema node name of the referenced node.
+ * @return LY_ERR value
+ */
+LY_ERR lysc_check_status(struct lysc_ctx *ctx, uint16_t flags1, void *mod1, const char *name1, uint16_t flags2,
+ void *mod2, const char *name2);
+
+/**
+ * @brief Check parsed expression for any prefixes of unimplemented modules.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] expr Parsed expression.
+ * @param[in] format Prefix format.
+ * @param[in] prefix_data Format-specific data (see ::ly_resolve_prefix()).
+ * @param[in] implement Whether all the non-implemented modules should are implemented or the first
+ * non-implemented module, if any, returned in @p mod_p.
+ * @param[in,out] unres Global unres structure of newly implemented modules.
+ * @param[out] mod_p Module that is not implemented.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE if @p implement is set.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *expr, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool implement, struct lys_glob_unres *unres, const struct lys_module **mod_p);
+
+/**
+ * @brief Compile all flagged modules in a dependency set, recursively if recompilation is needed.
+ *
+ * Steps taken when adding a new module (::ly_ctx_load_module(), ::lys_parse()):
+ *
+ * 1) parse module and add it into context with all imports and includes also parsed and in context
+ * (::lys_parse_load(), ::lys_parse_in(), lys_parse_localfile() - static)
+ * 2) implement it (perform one-time compilation tasks - compile identities and add reference to augment/deviation
+ * target modules, implement those as well, ::_lys_set_implemented())
+ * 3) create dep set of the module (::lys_unres_dep_sets_create())
+ * 4) (re)compile all the modules in the dep set and collect unres (::lys_compile_dep_set_r())
+ * 5) resolve unres (lys_compile_unres_depset() - static), new modules may be implemented like in 2) and if
+ * require recompilation, free all compiled modules and do 4)
+ * 6) all modules that needed to be (re)compiled are now, with all their dependencies
+ *
+ * What can cause new modules to be implemented when resolving unres in 5):
+ * - leafref
+ * - when, must
+ * - identityref, instance-identifier default value
+ * - new implemented module augments, deviations
+ *
+ * @param[in] ctx libyang context.
+ * @param[in,out] unres Global unres to use.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_depset_all(struct ly_ctx *ctx, struct lys_glob_unres *unres);
+
+/**
+ * @brief Implement a single module. Does not actually compile, only marks to_compile!
+ *
+ * @param[in] mod Module to implement.
+ * @param[in] features Features to set, see ::lys_set_features().
+ * @param[in,out] unres Global unres to use.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_implement(struct lys_module *mod, const char **features, struct lys_glob_unres *unres);
+
+#endif /* LY_SCHEMA_COMPILE_H_ */
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
new file mode 100644
index 0000000..9ca4e2e
--- /dev/null
+++ b/src/schema_compile_amend.c
@@ -0,0 +1,2547 @@
+/**
+ * @file schema_compile_amend.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Schema compilation of augments, deviations, and refines.
+ *
+ * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "schema_compile_amend.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "dict.h"
+#include "log.h"
+#include "schema_compile.h"
+#include "schema_compile_node.h"
+#include "schema_features.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+/**
+ * @brief Get module of a single nodeid node name test.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] nametest Nametest with an optional prefix.
+ * @param[in] nametest_len Length of @p nametest.
+ * @param[in] mod Both current and prefix module for resolving prefixes and to return in case of no prefix.
+ * @param[out] name Optional pointer to the name test without the prefix.
+ * @param[out] name_len Length of @p name.
+ * @return Resolved module.
+ */
+static const struct lys_module *
+lys_schema_node_get_module(const struct ly_ctx *ctx, const char *nametest, size_t nametest_len,
+ const struct lysp_module *mod, const char **name, size_t *name_len)
+{
+ const struct lys_module *target_mod;
+ const char *ptr;
+
+ ptr = ly_strnchr(nametest, ':', nametest_len);
+ if (ptr) {
+ target_mod = ly_resolve_prefix(ctx, nametest, ptr - nametest, LY_VALUE_SCHEMA, (void *)mod);
+ if (!target_mod) {
+ LOGVAL(ctx, LYVE_REFERENCE,
+ "Invalid absolute-schema-nodeid nametest \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+ (int)nametest_len, nametest, (int)(ptr - nametest), nametest, LYSP_MODULE_NAME(mod));
+ return NULL;
+ }
+
+ if (name) {
+ *name = ptr + 1;
+ *name_len = nametest_len - ((ptr - nametest) + 1);
+ }
+ } else {
+ target_mod = mod->mod;
+ if (name) {
+ *name = nametest;
+ *name_len = nametest_len;
+ }
+ }
+
+ return target_mod;
+}
+
+/**
+ * @brief Check the syntax of a node-id and collect all the referenced modules.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] nodeid Node-id to check.
+ * @param[in] abs Whether @p nodeid is absolute.
+ * @param[in,out] mod_set Set to add referenced modules into.
+ * @param[out] expr Optional node-id parsed into an expression.
+ * @param[out] target_mod Optional target module of the node-id.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_nodeid_mod_check(struct lysc_ctx *ctx, const char *nodeid, ly_bool abs, struct ly_set *mod_set,
+ struct lyxp_expr **expr, struct lys_module **target_mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *e = NULL;
+ struct lys_module *tmod = NULL, *mod;
+ const char *nodeid_type = abs ? "absolute-schema-nodeid" : "descendant-schema-nodeid";
+ uint32_t i;
+
+ /* parse */
+ ret = lyxp_expr_parse(ctx->ctx, nodeid, strlen(nodeid), 0, &e);
+ if (ret) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid %s value \"%s\" - invalid syntax.",
+ nodeid_type, nodeid);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (abs) {
+ /* absolute schema nodeid */
+ i = 0;
+ } else {
+ /* descendant schema nodeid */
+ if (e->tokens[0] != LYXP_TOKEN_NAMETEST) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid %s value \"%s\" - name test expected instead of \"%.*s\".",
+ nodeid_type, nodeid, e->tok_len[0], e->expr + e->tok_pos[0]);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ i = 1;
+ }
+
+ /* check all the tokens */
+ for ( ; i < e->used; i += 2) {
+ if (e->tokens[i] != LYXP_TOKEN_OPER_PATH) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid %s value \"%s\" - \"/\" expected instead of \"%.*s\".",
+ nodeid_type, nodeid, e->tok_len[i], e->expr + e->tok_pos[i]);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (e->used == i + 1) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid %s value \"%s\" - unexpected end of expression.", nodeid_type, e->expr);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (e->tokens[i + 1] != LYXP_TOKEN_NAMETEST) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid %s value \"%s\" - name test expected instead of \"%.*s\".",
+ nodeid_type, nodeid, e->tok_len[i + 1], e->expr + e->tok_pos[i + 1]);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (abs) {
+ mod = (struct lys_module *)lys_schema_node_get_module(ctx->ctx, e->expr + e->tok_pos[i + 1],
+ e->tok_len[i + 1], ctx->pmod, NULL, NULL);
+ LY_CHECK_ERR_GOTO(!mod, ret = LY_EVALID, cleanup);
+
+ /* only keep the first module */
+ if (!tmod) {
+ tmod = mod;
+ }
+
+ /* store the referenced module */
+ LY_CHECK_GOTO(ret = ly_set_add(mod_set, mod, 0, NULL), cleanup);
+ }
+ }
+
+cleanup:
+ if (ret || !expr) {
+ lyxp_expr_free(ctx->ctx, e);
+ e = NULL;
+ }
+ if (expr) {
+ *expr = ret ? NULL : e;
+ }
+ if (target_mod) {
+ *target_mod = ret ? NULL : tmod;
+ }
+ return ret;
+}
+
+/**
+ * @brief Check whether 2 schema nodeids match.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] exp1 First schema nodeid.
+ * @param[in] exp1p_mod Module of @p exp1 nodes without any prefix.
+ * @param[in] exp2 Second schema nodeid.
+ * @param[in] exp2_pmod Module of @p exp2 nodes without any prefix.
+ * @return Whether the schema nodeids match or not.
+ */
+static ly_bool
+lys_abs_schema_nodeid_match(const struct ly_ctx *ctx, const struct lyxp_expr *exp1, const struct lysp_module *exp1_pmod,
+ const struct lyxp_expr *exp2, const struct lysp_module *exp2_pmod)
+{
+ uint32_t i;
+ const struct lys_module *mod1, *mod2;
+ const char *name1 = NULL, *name2 = NULL;
+ size_t name1_len = 0, name2_len = 0;
+
+ if (exp1->used != exp2->used) {
+ return 0;
+ }
+
+ for (i = 0; i < exp1->used; ++i) {
+ assert(exp1->tokens[i] == exp2->tokens[i]);
+
+ if (exp1->tokens[i] == LYXP_TOKEN_NAMETEST) {
+ /* check modules of all the nodes in the node ID */
+ mod1 = lys_schema_node_get_module(ctx, exp1->expr + exp1->tok_pos[i], exp1->tok_len[i], exp1_pmod,
+ &name1, &name1_len);
+ assert(mod1);
+ mod2 = lys_schema_node_get_module(ctx, exp2->expr + exp2->tok_pos[i], exp2->tok_len[i], exp2_pmod,
+ &name2, &name2_len);
+ assert(mod2);
+
+ /* compare modules */
+ if (mod1 != mod2) {
+ return 0;
+ }
+
+ /* compare names */
+ if ((name1_len != name2_len) || strncmp(name1, name2, name1_len)) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+LY_ERR
+lys_precompile_uses_augments_refines(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, const struct lysc_node *ctx_node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ struct lysc_augment *aug;
+ struct lysp_node_augment *aug_p;
+ struct lysc_refine *rfn;
+ struct lysp_refine **new_rfn;
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t i;
+ struct ly_set mod_set = {0};
+
+ LY_LIST_FOR(uses_p->augments, aug_p) {
+ lysc_update_path(ctx, NULL, "{augment}");
+ lysc_update_path(ctx, NULL, aug_p->nodeid);
+
+ /* parse the nodeid */
+ LY_CHECK_GOTO(ret = lys_nodeid_mod_check(ctx, aug_p->nodeid, 0, &mod_set, &exp, NULL), cleanup);
+
+ /* allocate new compiled augment and store it in the set */
+ aug = calloc(1, sizeof *aug);
+ LY_CHECK_ERR_GOTO(!aug, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(&ctx->uses_augs, aug, 1, NULL), cleanup);
+
+ aug->nodeid = exp;
+ exp = NULL;
+ aug->aug_pmod = ctx->pmod;
+ aug->nodeid_ctx_node = ctx_node;
+ aug->aug_p = aug_p;
+
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ }
+
+ LY_ARRAY_FOR(uses_p->refines, u) {
+ lysc_update_path(ctx, NULL, "{refine}");
+ lysc_update_path(ctx, NULL, uses_p->refines[u].nodeid);
+
+ /* parse the nodeid */
+ LY_CHECK_GOTO(ret = lys_nodeid_mod_check(ctx, uses_p->refines[u].nodeid, 0, &mod_set, &exp, NULL), cleanup);
+
+ /* try to find the node in already compiled refines */
+ rfn = NULL;
+ for (i = 0; i < ctx->uses_rfns.count; ++i) {
+ if (lys_abs_schema_nodeid_match(ctx->ctx, exp, ctx->pmod, ((struct lysc_refine *)ctx->uses_rfns.objs[i])->nodeid,
+ ctx->pmod)) {
+ rfn = ctx->uses_rfns.objs[i];
+ break;
+ }
+ }
+
+ if (!rfn) {
+ /* allocate new compiled refine */
+ rfn = calloc(1, sizeof *rfn);
+ LY_CHECK_ERR_GOTO(!rfn, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(&ctx->uses_rfns, rfn, 1, NULL), cleanup);
+
+ rfn->nodeid = exp;
+ exp = NULL;
+ rfn->nodeid_pmod = ctx->cur_mod->parsed;
+ rfn->nodeid_ctx_node = ctx_node;
+ rfn->uses_p = uses_p;
+ } else {
+ /* just free exp */
+ lyxp_expr_free(ctx->ctx, exp);
+ exp = NULL;
+ }
+
+ /* add new parsed refine structure */
+ LY_ARRAY_NEW_GOTO(ctx->ctx, rfn->rfns, new_rfn, ret, cleanup);
+ *new_rfn = &uses_p->refines[u];
+
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ }
+
+cleanup:
+ if (ret) {
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ }
+ /* should include only this module, will fail later if not */
+ ly_set_erase(&mod_set, NULL);
+ lyxp_expr_free(ctx->ctx, exp);
+ return ret;
+}
+
+/**
+ * @brief Duplicate parsed extension children, recursively.
+ *
+ * @param[in] ctx Context.
+ * @param[in] orig_child First original child to duplicate.
+ * @param[in,out] child Duplicated children to add to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_ext_children_dup(const struct ly_ctx *ctx, const struct lysp_stmt *orig_child, struct lysp_stmt **child)
+{
+ struct lysp_stmt *ch = NULL;
+
+ assert(!*child);
+
+ LY_LIST_FOR(orig_child, orig_child) {
+ /* new child */
+ if (!*child) {
+ *child = ch = calloc(1, sizeof *ch);
+ LY_CHECK_ERR_RET(!ch, LOGMEM(ctx), LY_EMEM);
+ } else {
+ ch->next = calloc(1, sizeof *ch);
+ LY_CHECK_ERR_RET(!ch->next, LOGMEM(ctx), LY_EMEM);
+ ch = ch->next;
+ }
+
+ /* fill */
+ DUP_STRING_RET(ctx, orig_child->stmt, ch->stmt);
+ ch->flags = orig_child->flags;
+ DUP_STRING_RET(ctx, orig_child->arg, ch->arg);
+ ch->format = orig_child->format;
+ LY_CHECK_RET(ly_dup_prefix_data(ctx, orig_child->format, orig_child->prefix_data, &(ch->prefix_data)));
+ ch->kw = orig_child->kw;
+
+ /* recursive children */
+ LY_CHECK_RET(lysp_ext_children_dup(ctx, orig_child->child, &ch->child));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Duplicate parsed extension instance.
+ *
+ * @param[in] ctx Context.
+ * @param[in] pmod Current parsed module.
+ * @param[in] parent Parent of the duplicated ext instance.
+ * @param[in] parent_stmt Parent statement of the duplicated ext instance (should be @p parent).
+ * @param[out] ext Duplicated ext instance to fill.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_ext_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, void *parent, enum ly_stmt parent_stmt,
+ const struct lysp_ext_instance *orig_ext, struct lysp_ext_instance *ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_set pmods = {0};
+ struct lysp_ctx pctx = {.parsed_mods = &pmods};
+
+ DUP_STRING_GOTO(ctx, orig_ext->name, ext->name, ret, cleanup);
+ DUP_STRING_GOTO(ctx, orig_ext->argument, ext->argument, ret, cleanup);
+ ext->format = orig_ext->format;
+ LY_CHECK_GOTO(ret = ly_dup_prefix_data(ctx, orig_ext->format, orig_ext->prefix_data, &ext->prefix_data), cleanup);
+ ext->def = orig_ext->def;
+
+ ext->parent = parent;
+ ext->parent_stmt = parent_stmt;
+ ext->parent_stmt_index = orig_ext->parent_stmt_index;
+ ext->flags = orig_ext->flags;
+ ext->record = orig_ext->record;
+
+ LY_CHECK_GOTO(ret = lysp_ext_children_dup(ctx, orig_ext->child, &ext->child), cleanup);
+ if (ext->record && ext->record->plugin.parse) {
+ /* parse again */
+ LY_CHECK_GOTO(ret = ly_set_add(&pmods, pmod, 1, NULL), cleanup);
+ LY_CHECK_GOTO(ret = ext->record->plugin.parse(&pctx, ext), cleanup);
+ }
+
+cleanup:
+ ly_set_erase(&pmods, NULL);
+ return ret;
+}
+
+static LY_ERR
+lysp_restr_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, const struct lysp_restr *orig_restr,
+ struct lysp_restr *restr)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (orig_restr) {
+ DUP_STRING(ctx, orig_restr->arg.str, restr->arg.str, ret);
+ restr->arg.mod = orig_restr->arg.mod;
+ DUP_STRING(ctx, orig_restr->emsg, restr->emsg, ret);
+ DUP_STRING(ctx, orig_restr->eapptag, restr->eapptag, ret);
+ DUP_STRING(ctx, orig_restr->dsc, restr->dsc, ret);
+ DUP_STRING(ctx, orig_restr->ref, restr->ref, ret);
+ DUP_EXTS(ctx, pmod, restr, LY_STMT_MUST, orig_restr->exts, restr->exts, lysp_ext_dup);
+ }
+
+ return ret;
+}
+
+static LY_ERR
+lysp_string_dup(const struct ly_ctx *ctx, const char **orig_str, const char **str)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING(ctx, *orig_str, *str, ret);
+
+ return ret;
+}
+
+LY_ERR
+lysp_qname_dup(const struct ly_ctx *ctx, const struct lysp_qname *orig_qname, struct lysp_qname *qname)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (!orig_qname->str) {
+ return LY_SUCCESS;
+ }
+
+ DUP_STRING(ctx, orig_qname->str, qname->str, ret);
+ assert(orig_qname->mod);
+ qname->mod = orig_qname->mod;
+
+ return ret;
+}
+
+static LY_ERR
+lysp_type_enum_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, const struct lysp_type_enum *orig_enm,
+ struct lysp_type_enum *enm)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING(ctx, orig_enm->name, enm->name, ret);
+ DUP_STRING(ctx, orig_enm->dsc, enm->dsc, ret);
+ DUP_STRING(ctx, orig_enm->ref, enm->ref, ret);
+ enm->value = orig_enm->value;
+ DUP_ARRAY(ctx, orig_enm->iffeatures, enm->iffeatures, lysp_qname_dup);
+ DUP_EXTS(ctx, pmod, enm, LY_STMT_ENUM, orig_enm->exts, enm->exts, lysp_ext_dup);
+ enm->flags = orig_enm->flags;
+
+ return ret;
+}
+
+static LY_ERR
+lysp_type_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, const struct lysp_type *orig_type,
+ struct lysp_type *type)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ /* array macros read previous data so we must zero it */
+ memset(type, 0, sizeof *type);
+
+ DUP_STRING_GOTO(ctx, orig_type->name, type->name, ret, done);
+
+ if (orig_type->range) {
+ type->range = calloc(1, sizeof *type->range);
+ LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_RET(lysp_restr_dup(ctx, pmod, orig_type->range, type->range));
+ }
+
+ if (orig_type->length) {
+ type->length = calloc(1, sizeof *type->length);
+ LY_CHECK_ERR_RET(!type->length, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_RET(lysp_restr_dup(ctx, pmod, orig_type->length, type->length));
+ }
+
+ DUP_ARRAY2(ctx, pmod, orig_type->patterns, type->patterns, lysp_restr_dup);
+ DUP_ARRAY2(ctx, pmod, orig_type->enums, type->enums, lysp_type_enum_dup);
+ DUP_ARRAY2(ctx, pmod, orig_type->bits, type->bits, lysp_type_enum_dup);
+ LY_CHECK_GOTO(ret = lyxp_expr_dup(ctx, orig_type->path, 0, 0, &type->path), done);
+ DUP_ARRAY(ctx, orig_type->bases, type->bases, lysp_string_dup);
+ DUP_ARRAY2(ctx, pmod, orig_type->types, type->types, lysp_type_dup);
+ DUP_EXTS(ctx, pmod, type, LY_STMT_TYPE, orig_type->exts, type->exts, lysp_ext_dup);
+
+ type->pmod = orig_type->pmod;
+ type->compiled = orig_type->compiled;
+
+ type->fraction_digits = orig_type->fraction_digits;
+ type->require_instance = orig_type->require_instance;
+ type->flags = orig_type->flags;
+
+done:
+ return ret;
+}
+
+static LY_ERR
+lysp_when_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, struct lysp_when *when,
+ const struct lysp_when *orig_when)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING(ctx, orig_when->cond, when->cond, ret);
+ DUP_STRING(ctx, orig_when->dsc, when->dsc, ret);
+ DUP_STRING(ctx, orig_when->ref, when->ref, ret);
+ DUP_EXTS(ctx, pmod, when, LY_STMT_WHEN, orig_when->exts, when->exts, lysp_ext_dup);
+
+ return ret;
+}
+
+static LY_ERR
+lysp_node_common_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, struct lysp_node *node,
+ const struct lysp_node *orig)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ node->parent = NULL;
+ node->nodetype = orig->nodetype;
+ node->flags = orig->flags;
+ node->next = NULL;
+ DUP_STRING(ctx, orig->name, node->name, ret);
+ DUP_STRING(ctx, orig->dsc, node->dsc, ret);
+ DUP_STRING(ctx, orig->ref, node->ref, ret);
+ DUP_ARRAY(ctx, orig->iffeatures, node->iffeatures, lysp_qname_dup);
+ DUP_EXTS(ctx, pmod, node, lyplg_ext_nodetype2stmt(node->nodetype), orig->exts, node->exts, lysp_ext_dup);
+
+ return ret;
+}
+
+#define DUP_PWHEN(CTX, PMOD, ORIG, NEW) \
+ if (ORIG) { \
+ NEW = calloc(1, sizeof *NEW); \
+ LY_CHECK_ERR_RET(!NEW, LOGMEM(CTX), LY_EMEM); \
+ LY_CHECK_RET(lysp_when_dup(CTX, PMOD, NEW, ORIG)); \
+ }
+
+static LY_ERR
+lysp_node_dup(const struct ly_ctx *ctx, const struct lysp_module *pmod, struct lysp_node *node,
+ const struct lysp_node *orig)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node_container *cont;
+ const struct lysp_node_container *orig_cont;
+ struct lysp_node_leaf *leaf;
+ const struct lysp_node_leaf *orig_leaf;
+ struct lysp_node_leaflist *llist;
+ const struct lysp_node_leaflist *orig_llist;
+ struct lysp_node_list *list;
+ const struct lysp_node_list *orig_list;
+ struct lysp_node_choice *choice;
+ const struct lysp_node_choice *orig_choice;
+ struct lysp_node_case *cas;
+ const struct lysp_node_case *orig_cas;
+ struct lysp_node_anydata *any;
+ const struct lysp_node_anydata *orig_any;
+ struct lysp_node_action *action;
+ const struct lysp_node_action *orig_action;
+ struct lysp_node_action_inout *action_inout;
+ const struct lysp_node_action_inout *orig_action_inout;
+ struct lysp_node_notif *notif;
+ const struct lysp_node_notif *orig_notif;
+
+ assert(orig->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_ANYDATA |
+ LYS_RPC | LYS_ACTION | LYS_NOTIF));
+
+ /* common part */
+ LY_CHECK_RET(lysp_node_common_dup(ctx, pmod, node, orig));
+
+ /* specific part */
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ cont = (struct lysp_node_container *)node;
+ orig_cont = (const struct lysp_node_container *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_cont->when, cont->when);
+ DUP_ARRAY2(ctx, pmod, orig_cont->musts, cont->musts, lysp_restr_dup);
+ DUP_STRING(ctx, orig_cont->presence, cont->presence, ret);
+ /* we do not need the rest */
+ break;
+ case LYS_LEAF:
+ leaf = (struct lysp_node_leaf *)node;
+ orig_leaf = (const struct lysp_node_leaf *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_leaf->when, leaf->when);
+ DUP_ARRAY2(ctx, pmod, orig_leaf->musts, leaf->musts, lysp_restr_dup);
+ LY_CHECK_RET(lysp_type_dup(ctx, pmod, &orig_leaf->type, &leaf->type));
+ DUP_STRING(ctx, orig_leaf->units, leaf->units, ret);
+ LY_CHECK_RET(lysp_qname_dup(ctx, &orig_leaf->dflt, &leaf->dflt));
+ break;
+ case LYS_LEAFLIST:
+ llist = (struct lysp_node_leaflist *)node;
+ orig_llist = (const struct lysp_node_leaflist *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_llist->when, llist->when);
+ DUP_ARRAY2(ctx, pmod, orig_llist->musts, llist->musts, lysp_restr_dup);
+ LY_CHECK_RET(lysp_type_dup(ctx, pmod, &orig_llist->type, &llist->type));
+ DUP_STRING(ctx, orig_llist->units, llist->units, ret);
+ DUP_ARRAY(ctx, orig_llist->dflts, llist->dflts, lysp_qname_dup);
+ llist->min = orig_llist->min;
+ llist->max = orig_llist->max;
+ break;
+ case LYS_LIST:
+ list = (struct lysp_node_list *)node;
+ orig_list = (const struct lysp_node_list *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_list->when, list->when);
+ DUP_ARRAY2(ctx, pmod, orig_list->musts, list->musts, lysp_restr_dup);
+ DUP_STRING(ctx, orig_list->key, list->key, ret);
+ /* we do not need these arrays */
+ DUP_ARRAY(ctx, orig_list->uniques, list->uniques, lysp_qname_dup);
+ list->min = orig_list->min;
+ list->max = orig_list->max;
+ break;
+ case LYS_CHOICE:
+ choice = (struct lysp_node_choice *)node;
+ orig_choice = (const struct lysp_node_choice *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_choice->when, choice->when);
+ /* we do not need children */
+ LY_CHECK_RET(lysp_qname_dup(ctx, &orig_choice->dflt, &choice->dflt));
+ break;
+ case LYS_CASE:
+ cas = (struct lysp_node_case *)node;
+ orig_cas = (const struct lysp_node_case *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_cas->when, cas->when);
+ /* we do not need children */
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ any = (struct lysp_node_anydata *)node;
+ orig_any = (const struct lysp_node_anydata *)orig;
+
+ DUP_PWHEN(ctx, pmod, orig_any->when, any->when);
+ DUP_ARRAY2(ctx, pmod, orig_any->musts, any->musts, lysp_restr_dup);
+ break;
+ case LYS_RPC:
+ case LYS_ACTION:
+ action = (struct lysp_node_action *)node;
+ orig_action = (const struct lysp_node_action *)orig;
+
+ action->input.nodetype = orig_action->input.nodetype;
+ action->output.nodetype = orig_action->output.nodetype;
+ /* we do not need the rest */
+ break;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ action_inout = (struct lysp_node_action_inout *)node;
+ orig_action_inout = (const struct lysp_node_action_inout *)orig;
+
+ DUP_ARRAY2(ctx, pmod, orig_action_inout->musts, action_inout->musts, lysp_restr_dup);
+ /* we do not need the rest */
+ break;
+ case LYS_NOTIF:
+ notif = (struct lysp_node_notif *)node;
+ orig_notif = (const struct lysp_node_notif *)orig;
+
+ DUP_ARRAY2(ctx, pmod, orig_notif->musts, notif->musts, lysp_restr_dup);
+ /* we do not need the rest */
+ break;
+ default:
+ LOGINT_RET(ctx);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Duplicate a single parsed node. Only attributes that are used in compilation are copied.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] pmod Current parsed module.
+ * @param[in] pnode Node to duplicate.
+ * @param[in] with_links Whether to also copy any links (child, parent pointers).
+ * @param[out] dup_p Duplicated parsed node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_dup_single(struct lysc_ctx *cctx, const struct lysp_node *pnode, ly_bool with_links, struct lysp_node **dup_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node *dup = NULL;
+
+ if (!pnode) {
+ *dup_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ switch (pnode->nodetype) {
+ case LYS_CONTAINER:
+ dup = calloc(1, sizeof(struct lysp_node_container));
+ break;
+ case LYS_LEAF:
+ dup = calloc(1, sizeof(struct lysp_node_leaf));
+ break;
+ case LYS_LEAFLIST:
+ dup = calloc(1, sizeof(struct lysp_node_leaflist));
+ break;
+ case LYS_LIST:
+ dup = calloc(1, sizeof(struct lysp_node_list));
+ break;
+ case LYS_CHOICE:
+ dup = calloc(1, sizeof(struct lysp_node_choice));
+ break;
+ case LYS_CASE:
+ dup = calloc(1, sizeof(struct lysp_node_case));
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ dup = calloc(1, sizeof(struct lysp_node_anydata));
+ break;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ dup = calloc(1, sizeof(struct lysp_node_action_inout));
+ break;
+ case LYS_ACTION:
+ case LYS_RPC:
+ dup = calloc(1, sizeof(struct lysp_node_action));
+ break;
+ case LYS_NOTIF:
+ dup = calloc(1, sizeof(struct lysp_node_notif));
+ break;
+ default:
+ LOGINT_RET(cctx->ctx);
+ }
+ LY_CHECK_ERR_GOTO(!dup, LOGMEM(cctx->ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = lysp_node_dup(cctx->ctx, cctx->pmod, dup, pnode), cleanup);
+
+ if (with_links) {
+ /* copy also parent, child, action, and notification pointers */
+ dup->parent = pnode->parent;
+ switch (pnode->nodetype) {
+ case LYS_CONTAINER:
+ ((struct lysp_node_container *)dup)->child = ((struct lysp_node_container *)pnode)->child;
+ ((struct lysp_node_container *)dup)->actions = ((struct lysp_node_container *)pnode)->actions;
+ ((struct lysp_node_container *)dup)->notifs = ((struct lysp_node_container *)pnode)->notifs;
+ break;
+ case LYS_LIST:
+ ((struct lysp_node_list *)dup)->child = ((struct lysp_node_list *)pnode)->child;
+ ((struct lysp_node_list *)dup)->actions = ((struct lysp_node_list *)pnode)->actions;
+ ((struct lysp_node_list *)dup)->notifs = ((struct lysp_node_list *)pnode)->notifs;
+ break;
+ case LYS_CHOICE:
+ ((struct lysp_node_choice *)dup)->child = ((struct lysp_node_choice *)pnode)->child;
+ break;
+ case LYS_CASE:
+ ((struct lysp_node_case *)dup)->child = ((struct lysp_node_case *)pnode)->child;
+ break;
+ default:
+ break;
+ }
+ }
+
+cleanup:
+ if (ret) {
+ lysp_dev_node_free(cctx, dup);
+ } else {
+ *dup_p = dup;
+ }
+ return ret;
+}
+
+#define AMEND_WRONG_NODETYPE(AMEND_STR, OP_STR, PROPERTY) \
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid %s of %s node - it is not possible to %s \"%s\" property.", \
+ AMEND_STR, lys_nodetype2str(target->nodetype), OP_STR, PROPERTY);\
+ ret = LY_EVALID; \
+ goto cleanup;
+
+#define AMEND_CHECK_CARDINALITY(ARRAY, MAX, AMEND_STR, PROPERTY) \
+ if (LY_ARRAY_COUNT(ARRAY) > MAX) { \
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid %s of %s with too many (%"LY_PRI_ARRAY_COUNT_TYPE") %s properties.", \
+ AMEND_STR, lys_nodetype2str(target->nodetype), LY_ARRAY_COUNT(ARRAY), PROPERTY); \
+ ret = LY_EVALID; \
+ goto cleanup; \
+ }
+
+/**
+ * @brief Apply refine.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] rfn Refine to apply.
+ * @param[in] rfn_pmod Local module fo the refine.
+ * @param[in,out] target Refine target.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_apply_refine(struct lysc_ctx *ctx, struct lysp_refine *rfn, const struct lysp_module *rfn_pmod, struct lysp_node *target)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *orig_mod = ctx->cur_mod;
+ struct lysp_module *orig_pmod = ctx->pmod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_qname *qname;
+ struct lysp_restr **musts, *must;
+ uint32_t *num;
+
+ /* use module from the refine */
+ ctx->cur_mod = rfn_pmod->mod;
+ ctx->pmod = (struct lysp_module *)rfn_pmod;
+
+ /* keep the current path and add to it */
+ lysc_update_path(ctx, NULL, "{refine}");
+ lysc_update_path(ctx, NULL, rfn->nodeid);
+
+ /* default value */
+ if (rfn->dflts) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ AMEND_CHECK_CARDINALITY(rfn->dflts, 1, "refine", "default");
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_leaf *)target)->dflt.str);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &rfn->dflts[0], &((struct lysp_node_leaf *)target)->dflt), cleanup);
+ break;
+ case LYS_LEAFLIST:
+ if (rfn->dflts[0].mod->version < LYS_VERSION_1_1) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ FREE_ARRAY(ctx->ctx, ((struct lysp_node_leaflist *)target)->dflts, lysp_qname_free);
+ ((struct lysp_node_leaflist *)target)->dflts = NULL;
+ LY_ARRAY_FOR(rfn->dflts, u) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, ((struct lysp_node_leaflist *)target)->dflts, qname, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &rfn->dflts[u], qname), cleanup);
+ }
+ break;
+ case LYS_CHOICE:
+ AMEND_CHECK_CARDINALITY(rfn->dflts, 1, "refine", "default");
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_choice *)target)->dflt.str);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &rfn->dflts[0], &((struct lysp_node_choice *)target)->dflt), cleanup);
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("refine", "replace", "default");
+ }
+ }
+
+ /* description */
+ if (rfn->dsc) {
+ lydict_remove(ctx->ctx, target->dsc);
+ DUP_STRING_GOTO(ctx->ctx, rfn->dsc, target->dsc, ret, cleanup);
+ }
+
+ /* reference */
+ if (rfn->ref) {
+ lydict_remove(ctx->ctx, target->ref);
+ DUP_STRING_GOTO(ctx->ctx, rfn->ref, target->ref, ret, cleanup);
+ }
+
+ /* config */
+ if (rfn->flags & LYS_CONFIG_MASK) {
+ if (ctx->compile_opts & LYS_COMPILE_NO_CONFIG) {
+ LOGWRN(ctx->ctx, "Refining config inside %s has no effect (%s).",
+ (ctx->compile_opts & (LYS_IS_INPUT | LYS_IS_OUTPUT)) ? "RPC/action" :
+ ctx->compile_opts & LYS_IS_NOTIF ? "notification" : "a subtree ignoring config", ctx->path);
+ } else {
+ target->flags &= ~LYS_CONFIG_MASK;
+ target->flags |= rfn->flags & LYS_CONFIG_MASK;
+ }
+ }
+
+ /* mandatory */
+ if (rfn->flags & LYS_MAND_MASK) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_CHOICE:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("refine", "replace", "mandatory");
+ }
+
+ target->flags &= ~LYS_MAND_MASK;
+ target->flags |= rfn->flags & LYS_MAND_MASK;
+ }
+
+ /* presence */
+ if (rfn->presence) {
+ if (target->nodetype != LYS_CONTAINER) {
+ AMEND_WRONG_NODETYPE("refine", "replace", "presence");
+ }
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_container *)target)->presence);
+ DUP_STRING_GOTO(ctx->ctx, rfn->presence, ((struct lysp_node_container *)target)->presence, ret, cleanup);
+ }
+
+ /* must */
+ if (rfn->musts) {
+ switch (target->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ musts = &((struct lysp_node_container *)target)->musts;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("refine", "add", "must");
+ }
+
+ LY_ARRAY_FOR(rfn->musts, u) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, *musts, must, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_restr_dup(ctx->ctx, rfn_pmod, &rfn->musts[u], must), cleanup);
+ }
+ }
+
+ /* min-elements */
+ if (rfn->flags & LYS_SET_MIN) {
+ switch (target->nodetype) {
+ case LYS_LEAFLIST:
+ num = &((struct lysp_node_leaflist *)target)->min;
+ break;
+ case LYS_LIST:
+ num = &((struct lysp_node_list *)target)->min;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("refine", "replace", "min-elements");
+ }
+
+ *num = rfn->min;
+ }
+
+ /* max-elements */
+ if (rfn->flags & LYS_SET_MAX) {
+ switch (target->nodetype) {
+ case LYS_LEAFLIST:
+ num = &((struct lysp_node_leaflist *)target)->max;
+ break;
+ case LYS_LIST:
+ num = &((struct lysp_node_list *)target)->max;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("refine", "replace", "max-elements");
+ }
+
+ *num = rfn->max;
+ }
+
+ /* if-feature */
+ if (rfn->iffeatures) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ case LYS_LIST:
+ case LYS_CONTAINER:
+ case LYS_CHOICE:
+ case LYS_CASE:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("refine", "add", "if-feature");
+ }
+
+ LY_ARRAY_FOR(rfn->iffeatures, u) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, target->iffeatures, qname, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &rfn->iffeatures[u], qname), cleanup);
+ }
+ }
+
+ /* extension instances */
+ DUP_EXTS(ctx->ctx, rfn_pmod, target, lyplg_ext_nodetype2stmt(target->nodetype), rfn->exts, target->exts, lysp_ext_dup);
+
+cleanup:
+ ctx->cur_mod = orig_mod;
+ ctx->pmod = orig_pmod;
+
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ return ret;
+}
+
+/**
+ * @brief Apply deviate add.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] d Deviate add to apply.
+ * @param[in,out] target Deviation target.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_apply_deviate_add(struct lysc_ctx *ctx, struct lysp_deviate_add *d, struct lysp_node *target)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_qname *qname;
+ uint32_t *num;
+ struct lysp_restr **musts, *must;
+
+#define DEV_CHECK_NONPRESENCE(TYPE, MEMBER, PROPERTY, VALUEMEMBER) \
+ if (((TYPE)target)->MEMBER) { \
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid deviation adding \"%s\" property which already exists (with value \"%s\").", \
+ PROPERTY, ((TYPE)target)->VALUEMEMBER); \
+ ret = LY_EVALID; \
+ goto cleanup; \
+ }
+
+ /* [units-stmt] */
+ if (d->units) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "add", "units");
+ }
+
+ DEV_CHECK_NONPRESENCE(struct lysp_node_leaf *, units, "units", units);
+ DUP_STRING_GOTO(ctx->ctx, d->units, ((struct lysp_node_leaf *)target)->units, ret, cleanup);
+ }
+
+ /* *must-stmt */
+ if (d->musts) {
+ musts = lysp_node_musts_p(target);
+ if (!musts) {
+ AMEND_WRONG_NODETYPE("deviation", "add", "must");
+ }
+
+ LY_ARRAY_FOR(d->musts, u) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, *musts, must, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_restr_dup(ctx->ctx, ctx->pmod, &d->musts[u], must), cleanup);
+ }
+ }
+
+ /* *unique-stmt */
+ if (d->uniques) {
+ if (target->nodetype != LYS_LIST) {
+ AMEND_WRONG_NODETYPE("deviation", "add", "unique");
+ }
+
+ LY_ARRAY_FOR(d->uniques, u) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, ((struct lysp_node_list *)target)->uniques, qname, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->uniques[u], qname), cleanup);
+ }
+ }
+
+ /* *default-stmt */
+ if (d->dflts) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ AMEND_CHECK_CARDINALITY(d->dflts, 1, "deviation", "default");
+ DEV_CHECK_NONPRESENCE(struct lysp_node_leaf *, dflt.str, "default", dflt.str);
+
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflts[0], &((struct lysp_node_leaf *)target)->dflt), cleanup);
+ break;
+ case LYS_LEAFLIST:
+ LY_ARRAY_FOR(d->dflts, u) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, ((struct lysp_node_leaflist *)target)->dflts, qname, ret, cleanup);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflts[u], qname), cleanup);
+ }
+ break;
+ case LYS_CHOICE:
+ AMEND_CHECK_CARDINALITY(d->dflts, 1, "deviation", "default");
+ DEV_CHECK_NONPRESENCE(struct lysp_node_choice *, dflt.str, "default", dflt.str);
+
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflts[0], &((struct lysp_node_choice *)target)->dflt), cleanup);
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "add", "default");
+ }
+ }
+
+ /* [config-stmt] */
+ if (d->flags & LYS_CONFIG_MASK) {
+ switch (target->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ case LYS_LIST:
+ case LYS_CHOICE:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "add", "config");
+ }
+
+ if (target->flags & LYS_CONFIG_MASK) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid deviation adding \"config\" property which already exists (with value \"config %s\").",
+ target->flags & LYS_CONFIG_W ? "true" : "false");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ target->flags |= d->flags & LYS_CONFIG_MASK;
+ }
+
+ /* [mandatory-stmt] */
+ if (d->flags & LYS_MAND_MASK) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_CHOICE:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "add", "mandatory");
+ }
+
+ if (target->flags & LYS_MAND_MASK) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory %s\").",
+ target->flags & LYS_MAND_TRUE ? "true" : "false");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ target->flags |= d->flags & LYS_MAND_MASK;
+ }
+
+ /* [min-elements-stmt] */
+ if (d->flags & LYS_SET_MIN) {
+ switch (target->nodetype) {
+ case LYS_LEAFLIST:
+ num = &((struct lysp_node_leaflist *)target)->min;
+ break;
+ case LYS_LIST:
+ num = &((struct lysp_node_list *)target)->min;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "add", "min-elements");
+ }
+
+ if (target->flags & LYS_SET_MIN) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid deviation adding \"min-elements\" property which already exists (with value \"%u\").", *num);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ *num = d->min;
+ }
+
+ /* [max-elements-stmt] */
+ if (d->flags & LYS_SET_MAX) {
+ switch (target->nodetype) {
+ case LYS_LEAFLIST:
+ num = &((struct lysp_node_leaflist *)target)->max;
+ break;
+ case LYS_LIST:
+ num = &((struct lysp_node_list *)target)->max;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "add", "max-elements");
+ }
+
+ if (target->flags & LYS_SET_MAX) {
+ if (*num) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid deviation adding \"max-elements\" property which already exists (with value \"%u\").",
+ *num);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid deviation adding \"max-elements\" property which already exists (with value \"unbounded\").");
+ }
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ *num = d->max;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Apply deviate delete.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] d Deviate delete to apply.
+ * @param[in,out] target Deviation target.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_apply_deviate_delete(struct lysc_ctx *ctx, struct lysp_deviate_del *d, struct lysp_node *target)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_restr **musts;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysp_qname **uniques, **dflts;
+
+#define DEV_DEL_ARRAY(DEV_ARRAY, ORIG_ARRAY, DEV_MEMBER, ORIG_MEMBER, FREE_FUNC, FREE_CTX, PROPERTY) \
+ LY_ARRAY_FOR(d->DEV_ARRAY, u) { \
+ int found = 0; \
+ LY_ARRAY_FOR(ORIG_ARRAY, v) { \
+ if (!strcmp(d->DEV_ARRAY[u]DEV_MEMBER, (ORIG_ARRAY)[v]ORIG_MEMBER)) { \
+ found = 1; \
+ break; \
+ } \
+ } \
+ if (!found) { \
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, \
+ "Invalid deviation deleting \"%s\" property \"%s\" which does not match any of the target's property values.", \
+ PROPERTY, d->DEV_ARRAY[u]DEV_MEMBER); \
+ ret = LY_EVALID; \
+ goto cleanup; \
+ } \
+ LY_ARRAY_DECREMENT(ORIG_ARRAY); \
+ FREE_FUNC(FREE_CTX, &(ORIG_ARRAY)[v]); \
+ if (v < LY_ARRAY_COUNT(ORIG_ARRAY)) { \
+ memmove(&(ORIG_ARRAY)[v], &(ORIG_ARRAY)[v + 1], (LY_ARRAY_COUNT(ORIG_ARRAY) - v) * sizeof *(ORIG_ARRAY)); \
+ } \
+ } \
+ if (!LY_ARRAY_COUNT(ORIG_ARRAY)) { \
+ LY_ARRAY_FREE(ORIG_ARRAY); \
+ ORIG_ARRAY = NULL; \
+ }
+
+#define DEV_CHECK_PRESENCE_VALUE(TYPE, MEMBER, DEVTYPE, PROPERTY, VALUE) \
+ if (!((TYPE)target)->MEMBER) { \
+ LOGVAL(ctx->ctx, LY_VCODE_DEV_NOT_PRESENT, DEVTYPE, PROPERTY, VALUE); \
+ ret = LY_EVALID; \
+ goto cleanup; \
+ } else if (strcmp(((TYPE)target)->MEMBER, VALUE)) { \
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, \
+ "Invalid deviation deleting \"%s\" property \"%s\" which does not match the target's property value \"%s\".", \
+ PROPERTY, VALUE, ((TYPE)target)->MEMBER); \
+ ret = LY_EVALID; \
+ goto cleanup; \
+ }
+
+ /* [units-stmt] */
+ if (d->units) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "delete", "units");
+ }
+
+ DEV_CHECK_PRESENCE_VALUE(struct lysp_node_leaf *, units, "deleting", "units", d->units);
+ lydict_remove(ctx->ctx, ((struct lysp_node_leaf *)target)->units);
+ ((struct lysp_node_leaf *)target)->units = NULL;
+ }
+
+ /* *must-stmt */
+ if (d->musts) {
+ musts = lysp_node_musts_p(target);
+ if (!musts) {
+ AMEND_WRONG_NODETYPE("deviation", "delete", "must");
+ }
+
+ DEV_DEL_ARRAY(musts, *musts, .arg.str, .arg.str, lysp_restr_free, &ctx->free_ctx, "must");
+ }
+
+ /* *unique-stmt */
+ if (d->uniques) {
+ if (target->nodetype != LYS_LIST) {
+ AMEND_WRONG_NODETYPE("deviation", "delete", "unique");
+ }
+
+ uniques = &((struct lysp_node_list *)target)->uniques;
+ DEV_DEL_ARRAY(uniques, *uniques, .str, .str, lysp_qname_free, ctx->ctx, "unique");
+ }
+
+ /* *default-stmt */
+ if (d->dflts) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ AMEND_CHECK_CARDINALITY(d->dflts, 1, "deviation", "default");
+ DEV_CHECK_PRESENCE_VALUE(struct lysp_node_leaf *, dflt.str, "deleting", "default", d->dflts[0].str);
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_leaf *)target)->dflt.str);
+ ((struct lysp_node_leaf *)target)->dflt.str = NULL;
+ break;
+ case LYS_LEAFLIST:
+ dflts = &((struct lysp_node_leaflist *)target)->dflts;
+ DEV_DEL_ARRAY(dflts, *dflts, .str, .str, lysp_qname_free, ctx->ctx, "default");
+ break;
+ case LYS_CHOICE:
+ AMEND_CHECK_CARDINALITY(d->dflts, 1, "deviation", "default");
+ DEV_CHECK_PRESENCE_VALUE(struct lysp_node_choice *, dflt.str, "deleting", "default", d->dflts[0].str);
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_choice *)target)->dflt.str);
+ ((struct lysp_node_choice *)target)->dflt.str = NULL;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "delete", "default");
+ }
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Apply deviate replace.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] d Deviate replace to apply.
+ * @param[in,out] target Deviation target.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_apply_deviate_replace(struct lysc_ctx *ctx, struct lysp_deviate_rpl *d, struct lysp_node *target)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t *num;
+
+#define DEV_CHECK_PRESENCE(TYPE, MEMBER, DEVTYPE, PROPERTY, VALUE) \
+ if (!((TYPE)target)->MEMBER) { \
+ LOGVAL(ctx->ctx, LY_VCODE_DEV_NOT_PRESENT, DEVTYPE, PROPERTY, VALUE); \
+ ret = LY_EVALID; \
+ goto cleanup; \
+ }
+
+ /* [type-stmt] */
+ if (d->type) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "type");
+ }
+
+ lysp_type_free(&ctx->free_ctx, &((struct lysp_node_leaf *)target)->type);
+ lysp_type_dup(ctx->ctx, ctx->pmod, d->type, &((struct lysp_node_leaf *)target)->type);
+ }
+
+ /* [units-stmt] */
+ if (d->units) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "units");
+ }
+
+ DEV_CHECK_PRESENCE(struct lysp_node_leaf *, units, "replacing", "units", d->units);
+ lydict_remove(ctx->ctx, ((struct lysp_node_leaf *)target)->units);
+ DUP_STRING_GOTO(ctx->ctx, d->units, ((struct lysp_node_leaf *)target)->units, ret, cleanup);
+ }
+
+ /* [default-stmt] */
+ if (d->dflt.str) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ DEV_CHECK_PRESENCE(struct lysp_node_leaf *, dflt.str, "replacing", "default", d->dflt.str);
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_leaf *)target)->dflt.str);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflt, &((struct lysp_node_leaf *)target)->dflt), cleanup);
+ break;
+ case LYS_CHOICE:
+ DEV_CHECK_PRESENCE(struct lysp_node_choice *, dflt.str, "replacing", "default", d->dflt);
+
+ lydict_remove(ctx->ctx, ((struct lysp_node_choice *)target)->dflt.str);
+ LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflt, &((struct lysp_node_choice *)target)->dflt), cleanup);
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "default");
+ }
+ }
+
+ /* [config-stmt] */
+ if (d->flags & LYS_CONFIG_MASK) {
+ switch (target->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ case LYS_LIST:
+ case LYS_CHOICE:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "config");
+ }
+
+ if (!(target->flags & LYS_CONFIG_MASK)) {
+ LOGVAL(ctx->ctx, LY_VCODE_DEV_NOT_PRESENT, "replacing", "config",
+ d->flags & LYS_CONFIG_W ? "config true" : "config false");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ target->flags &= ~LYS_CONFIG_MASK;
+ target->flags |= d->flags & LYS_CONFIG_MASK;
+ }
+
+ /* [mandatory-stmt] */
+ if (d->flags & LYS_MAND_MASK) {
+ switch (target->nodetype) {
+ case LYS_LEAF:
+ case LYS_CHOICE:
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "mandatory");
+ }
+
+ if (!(target->flags & LYS_MAND_MASK)) {
+ LOGVAL(ctx->ctx, LY_VCODE_DEV_NOT_PRESENT, "replacing", "mandatory",
+ d->flags & LYS_MAND_TRUE ? "mandatory true" : "mandatory false");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ target->flags &= ~LYS_MAND_MASK;
+ target->flags |= d->flags & LYS_MAND_MASK;
+ }
+
+ /* [min-elements-stmt] */
+ if (d->flags & LYS_SET_MIN) {
+ switch (target->nodetype) {
+ case LYS_LEAFLIST:
+ num = &((struct lysp_node_leaflist *)target)->min;
+ break;
+ case LYS_LIST:
+ num = &((struct lysp_node_list *)target)->min;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "min-elements");
+ }
+
+ if (!(target->flags & LYS_SET_MIN)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid deviation replacing \"min-elements\" property which is not present.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ *num = d->min;
+ }
+
+ /* [max-elements-stmt] */
+ if (d->flags & LYS_SET_MAX) {
+ switch (target->nodetype) {
+ case LYS_LEAFLIST:
+ num = &((struct lysp_node_leaflist *)target)->max;
+ break;
+ case LYS_LIST:
+ num = &((struct lysp_node_list *)target)->max;
+ break;
+ default:
+ AMEND_WRONG_NODETYPE("deviation", "replace", "max-elements");
+ }
+
+ if (!(target->flags & LYS_SET_MAX)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid deviation replacing \"max-elements\" property which is not present.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ *num = d->max;
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Apply deviation with all its deviates.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] dev Deviation to apply.
+ * @param[in] dev_pmod Local module of the deviation.
+ * @param[in,out] target Deviation target.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_apply_deviation(struct lysc_ctx *ctx, struct lysp_deviation *dev, const struct lysp_module *dev_pmod,
+ struct lysp_node *target)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *orig_mod = ctx->cur_mod;
+ struct lysp_module *orig_pmod = ctx->pmod;
+ char orig_path[LYSC_CTX_BUFSIZE];
+ struct lysp_deviate *d;
+
+ /* clear path and set modules */
+ strcpy(orig_path, ctx->path);
+ ctx->path_len = 1;
+ ctx->cur_mod = dev_pmod->mod;
+ ctx->pmod = (struct lysp_module *)dev_pmod;
+
+ /* generate correct path */
+ lysc_update_path(ctx, NULL, "{deviation}");
+ lysc_update_path(ctx, NULL, dev->nodeid);
+
+ LY_LIST_FOR(dev->deviates, d) {
+ switch (d->mod) {
+ case LYS_DEV_ADD:
+ ret = lys_apply_deviate_add(ctx, (struct lysp_deviate_add *)d, target);
+ break;
+ case LYS_DEV_DELETE:
+ ret = lys_apply_deviate_delete(ctx, (struct lysp_deviate_del *)d, target);
+ break;
+ case LYS_DEV_REPLACE:
+ ret = lys_apply_deviate_replace(ctx, (struct lysp_deviate_rpl *)d, target);
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ ret = LY_EINT;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* deviation extension instances */
+ DUP_EXTS(ctx->ctx, dev_pmod, target, lyplg_ext_nodetype2stmt(target->nodetype), dev->exts, target->exts, lysp_ext_dup);
+
+cleanup:
+ ctx->cur_mod = orig_mod;
+ ctx->pmod = orig_pmod;
+
+ strcpy(ctx->path, orig_path);
+ ctx->path_len = strlen(ctx->path);
+ return ret;
+}
+
+/**
+ * @brief Check whether a compiled node matches a single schema nodeid name test.
+ *
+ * @param[in,out] node Compiled node to consider. On a match it is moved to its parent.
+ * @param[in] mod Expected module.
+ * @param[in] name Expected name.
+ * @param[in] name_len Length of @p name.
+ * @return Whether it is a match or not.
+ */
+static ly_bool
+lysp_schema_nodeid_match_node(const struct lysc_node **node, const struct lys_module *mod, const char *name,
+ size_t name_len)
+{
+ /* compare with the module of the node */
+ if ((*node)->module != mod) {
+ return 0;
+ }
+
+ /* compare names */
+ if (ly_strncmp((*node)->name, name, name_len)) {
+ return 0;
+ }
+
+ /* move to next parent */
+ *node = (*node)->parent;
+
+ return 1;
+}
+
+/**
+ * @brief Check whether a compiled ext instance matches a single schema nodeid name test.
+ *
+ * @param[in,out] ext Compiled ext instance to consider. On a match it is zeroed to not match again.
+ * @param[in] mod Expected module.
+ * @param[in] name Expected name.
+ * @param[in] name_len Length of @p name.
+ * @return Whether it is a match or not.
+ */
+static ly_bool
+lysp_schema_nodeid_match_ext(const struct lysc_ext_instance **ext, const struct lys_module *mod, const char *name,
+ size_t name_len)
+{
+ /* compare with the module */
+ if ((*ext)->module != mod) {
+ return 0;
+ }
+
+ /* compare names (argument) */
+ if (ly_strncmp((*ext)->argument, name, name_len)) {
+ return 0;
+ }
+
+ /* zero */
+ *ext = NULL;
+
+ return 1;
+}
+
+/**
+ * @brief Check whether a node matches specific schema nodeid.
+ *
+ * @param[in] exp Parsed nodeid to match.
+ * @param[in] exp_pmod Module to use for nodes in @p exp without a prefix.
+ * @param[in] exp_ext Extension instance in which @p exp is defined, it means it targets an extension instance.
+ * @param[in] ctx_node Initial context node that should match, only for descendant paths.
+ * @param[in] parent First compiled parent to consider. If @p pnode is NULL, it is condered the node to be matched.
+ * @param[in] pnode Parsed node to be matched. May be NULL if the target node was already compiled.
+ * @param[in] pnode_mod Compiled @p pnode to-be module.
+ * @param[in] pnode_ext Extension instance in which @p pnode is defined.
+ * @return Whether it is a match or not.
+ */
+static ly_bool
+lysp_schema_nodeid_match(const struct lyxp_expr *exp, const struct lysp_module *exp_pmod,
+ const struct lysp_ext_instance *exp_ext, const struct lysc_node *ctx_node, const struct lysc_node *parent,
+ const struct lysp_node *pnode, const struct lys_module *pnode_mod, const struct lysc_ext_instance *pnode_ext)
+{
+ uint32_t i;
+ const struct lys_module *mod;
+ const char *name = NULL;
+ size_t name_len = 0;
+
+ if (exp_ext && !pnode_ext) {
+ /* extension instance augment and standard node, will never match */
+ return 0;
+ } else if (!exp_ext && pnode_ext) {
+ /* standard augment and extension instance node, will never match */
+ return 0;
+ }
+
+ /* compare last node in the node ID */
+ i = exp->used - 1;
+
+ /* get exp node ID module */
+ mod = lys_schema_node_get_module(exp_pmod->mod->ctx, exp->expr + exp->tok_pos[i], exp->tok_len[i], exp_pmod, &name, &name_len);
+ assert(mod);
+
+ if (pnode) {
+ /* compare on the last parsed-only node */
+ if ((pnode_mod != mod) || ly_strncmp(pnode->name, name, name_len)) {
+ return 0;
+ }
+ } else {
+ /* using parent directly */
+ if (!lysp_schema_nodeid_match_node(&parent, mod, name, name_len)) {
+ return 0;
+ }
+ }
+
+ /* now compare all the compiled parents */
+ while (i > 1) {
+ i -= 2;
+ assert(exp->tokens[i] == LYXP_TOKEN_NAMETEST);
+
+ if (!parent && !pnode_ext) {
+ /* no more parents but path continues */
+ return 0;
+ }
+
+ /* get exp node ID module */
+ mod = lys_schema_node_get_module(exp_pmod->mod->ctx, exp->expr + exp->tok_pos[i], exp->tok_len[i], exp_pmod, &name,
+ &name_len);
+ assert(mod);
+
+ if (parent) {
+ /* compare with the parent */
+ if (!lysp_schema_nodeid_match_node(&parent, mod, name, name_len)) {
+ return 0;
+ }
+ } else {
+ /* compare with the ext instance */
+ if (!lysp_schema_nodeid_match_ext(&pnode_ext, mod, name, name_len)) {
+ return 0;
+ }
+ }
+ }
+
+ if (ctx_node && (ctx_node != parent)) {
+ /* descendant path has not finished in the context node */
+ return 0;
+ } else if (!ctx_node && (parent || pnode_ext)) {
+ /* some parent/extension was not matched */
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+lysc_augment_free(const struct ly_ctx *ctx, struct lysc_augment *aug)
+{
+ if (aug) {
+ lyxp_expr_free(ctx, aug->nodeid);
+
+ free(aug);
+ }
+}
+
+void
+lysc_deviation_free(const struct ly_ctx *ctx, struct lysc_deviation *dev)
+{
+ if (dev) {
+ lyxp_expr_free(ctx, dev->nodeid);
+ LY_ARRAY_FREE(dev->devs);
+ LY_ARRAY_FREE(dev->dev_pmods);
+
+ free(dev);
+ }
+}
+
+void
+lysc_refine_free(const struct ly_ctx *ctx, struct lysc_refine *rfn)
+{
+ if (rfn) {
+ lyxp_expr_free(ctx, rfn->nodeid);
+ LY_ARRAY_FREE(rfn->rfns);
+
+ free(rfn);
+ }
+}
+
+void
+lysp_dev_node_free(struct lysc_ctx *cctx, struct lysp_node *dev_pnode)
+{
+ if (!dev_pnode) {
+ return;
+ }
+
+ switch (dev_pnode->nodetype) {
+ case LYS_CONTAINER:
+ ((struct lysp_node_container *)dev_pnode)->child = NULL;
+ ((struct lysp_node_container *)dev_pnode)->actions = NULL;
+ ((struct lysp_node_container *)dev_pnode)->notifs = NULL;
+ break;
+ case LYS_LIST:
+ ((struct lysp_node_list *)dev_pnode)->child = NULL;
+ ((struct lysp_node_list *)dev_pnode)->actions = NULL;
+ ((struct lysp_node_list *)dev_pnode)->notifs = NULL;
+ break;
+ case LYS_CHOICE:
+ ((struct lysp_node_choice *)dev_pnode)->child = NULL;
+ break;
+ case LYS_CASE:
+ ((struct lysp_node_case *)dev_pnode)->child = NULL;
+ break;
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ /* no children */
+ break;
+ case LYS_NOTIF:
+ ((struct lysp_node_notif *)dev_pnode)->child = NULL;
+ break;
+ case LYS_RPC:
+ case LYS_ACTION:
+ ((struct lysp_node_action *)dev_pnode)->input.child = NULL;
+ ((struct lysp_node_action *)dev_pnode)->output.child = NULL;
+ break;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ ((struct lysp_node_action_inout *)dev_pnode)->child = NULL;
+ lysp_node_free(&cctx->free_ctx, dev_pnode);
+ free(dev_pnode);
+ return;
+ default:
+ LOGINT(cctx->ctx);
+ return;
+ }
+
+ lysp_node_free(&cctx->free_ctx, dev_pnode);
+}
+
+LY_ERR
+lys_compile_node_deviations_refines(struct lysc_ctx *ctx, const struct lysp_node *pnode, const struct lysc_node *parent,
+ struct lysp_node **dev_pnode, ly_bool *not_supported)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_refine *rfn;
+ struct lysc_deviation *dev;
+
+ *dev_pnode = NULL;
+ *not_supported = 0;
+
+ for (i = 0; i < ctx->uses_rfns.count; ) {
+ rfn = ctx->uses_rfns.objs[i];
+
+ if (!lysp_schema_nodeid_match(rfn->nodeid, rfn->nodeid_pmod, NULL, rfn->nodeid_ctx_node, parent, pnode,
+ ctx->cur_mod, ctx->ext)) {
+ /* not our target node */
+ ++i;
+ continue;
+ }
+
+ if (!*dev_pnode) {
+ /* first refine on this node, create a copy first */
+ LY_CHECK_GOTO(ret = lysp_dup_single(ctx, pnode, 1, dev_pnode), cleanup);
+ }
+
+ /* apply all the refines by changing (the copy of) the parsed node */
+ LY_ARRAY_FOR(rfn->rfns, u) {
+ LY_CHECK_GOTO(ret = lys_apply_refine(ctx, rfn->rfns[u], rfn->nodeid_pmod, *dev_pnode), cleanup);
+ }
+
+ /* refine was applied, remove it */
+ lysc_refine_free(ctx->ctx, rfn);
+ ly_set_rm_index(&ctx->uses_rfns, i, NULL);
+
+ /* refines use relative paths so more may apply to a single node */
+ }
+
+ for (i = 0; i < ctx->devs.count; ++i) {
+ dev = ctx->devs.objs[i];
+
+ if (!lysp_schema_nodeid_match(dev->nodeid, dev->dev_pmods[0], NULL, NULL, parent, pnode, ctx->cur_mod, ctx->ext)) {
+ /* not our target node */
+ continue;
+ }
+
+ if (dev->not_supported) {
+ /* it is not supported, no more deviations */
+ *not_supported = 1;
+ goto dev_applied;
+ }
+
+ if (!*dev_pnode) {
+ /* first deviation on this node, create a copy first */
+ LY_CHECK_GOTO(ret = lysp_dup_single(ctx, pnode, 1, dev_pnode), cleanup);
+ }
+
+ /* apply all the deviates by changing (the copy of) the parsed node */
+ LY_ARRAY_FOR(dev->devs, u) {
+ LY_CHECK_GOTO(ret = lys_apply_deviation(ctx, dev->devs[u], dev->dev_pmods[u], *dev_pnode), cleanup);
+ }
+
+dev_applied:
+ /* deviation was applied, remove it */
+ lysc_deviation_free(ctx->ctx, dev);
+ ly_set_rm_index(&ctx->devs, i, NULL);
+
+ /* all the deviations for one target node are in one structure, we are done */
+ break;
+ }
+
+cleanup:
+ if (ret) {
+ lysp_dev_node_free(ctx, *dev_pnode);
+ *dev_pnode = NULL;
+ *not_supported = 0;
+ }
+ return ret;
+}
+
+/**
+ * @brief Compile augment children.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] aug_when Parsed augment when to inherit.
+ * @param[in] aug_flags Parsed augment flags.
+ * @param[in] child First augment child to compile.
+ * @param[in] target Target node of the augment.
+ * @param[in] child_unres_disabled Whether the children are to be put into unres disabled set or not.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+static LY_ERR
+lys_compile_augment_children(struct lysc_ctx *ctx, struct lysp_when *aug_when, uint16_t aug_flags, struct lysp_node *child,
+ struct lysc_node *target, ly_bool child_unres_disabled)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysp_node *pnode;
+ struct lysc_node *node;
+ struct lysc_when *when_shared = NULL;
+ ly_bool enabled, allow_mand = 0;
+ struct ly_set child_set = {0};
+ uint32_t i, opt_prev = ctx->compile_opts;
+
+ /* check for mandatory nodes
+ * - new cases augmenting some choice can have mandatory nodes
+ * - mandatory nodes are allowed only in case the augmentation is made conditional with a when statement
+ */
+ if (aug_when || (target->nodetype == LYS_CHOICE) || (ctx->cur_mod == target->module)) {
+ allow_mand = 1;
+ }
+
+ LY_LIST_FOR(child, pnode) {
+ /* check if the subnode can be connected to the found target (e.g. case cannot be inserted into container) */
+ if (((pnode->nodetype == LYS_CASE) && (target->nodetype != LYS_CHOICE)) ||
+ ((pnode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && !(target->nodetype & (LYS_CONTAINER | LYS_LIST))) ||
+ ((pnode->nodetype == LYS_USES) && (target->nodetype == LYS_CHOICE))) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid augment of %s node which is not allowed to contain %s node \"%s\".",
+ lys_nodetype2str(target->nodetype), lys_nodetype2str(pnode->nodetype), pnode->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* compile the children */
+ if (target->nodetype == LYS_CHOICE) {
+ LY_CHECK_GOTO(rc = lys_compile_node_choice_child(ctx, pnode, target, &child_set), cleanup);
+ } else if (target->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ if (target->nodetype == LYS_INPUT) {
+ ctx->compile_opts |= LYS_COMPILE_RPC_INPUT;
+ } else {
+ ctx->compile_opts |= LYS_COMPILE_RPC_OUTPUT;
+ }
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, aug_flags, &child_set), cleanup);
+ } else {
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, aug_flags, &child_set), cleanup);
+ }
+
+ /* eval if-features again for the rest of this node processing */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ }
+
+ /* since the augment node is not present in the compiled tree, we need to pass some of its
+ * statements to all its children */
+ for (i = 0; i < child_set.count; ++i) {
+ node = child_set.snodes[i];
+ if (!allow_mand && (node->flags & LYS_CONFIG_W) && (node->flags & LYS_MAND_TRUE)) {
+ node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(target, 0);
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Invalid augment adding mandatory node \"%s\" without making it conditional via when statement.",
+ node->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (aug_when) {
+ /* pass augment's when to all the children */
+ rc = lys_compile_when(ctx, aug_when, aug_flags, target, lysc_data_node(target), node, &when_shared);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ if (child_unres_disabled) {
+ /* child is disabled by the augment if-features */
+ ly_set_add(&ctx->unres->disabled, node, 1, NULL);
+ }
+ }
+
+ /* next iter */
+ ly_set_erase(&child_set, NULL);
+ ctx->compile_opts = opt_prev;
+ }
+
+cleanup:
+ ly_set_erase(&child_set, NULL);
+ ctx->compile_opts = opt_prev;
+ return rc;
+}
+
+/**
+ * @brief Compile the parsed augment connecting it into its target.
+ *
+ * It is expected that all the data referenced in path are present - augments are ordered so that augment B
+ * targeting data from augment A is being compiled after augment A. Also the modules referenced in the path
+ * are already implemented and compiled.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] aug_p Parsed augment to compile.
+ * @param[in] target Target node of the augment.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+static LY_ERR
+lys_compile_augment(struct lysc_ctx *ctx, struct lysp_node_augment *aug_p, struct lysc_node *target)
+{
+ LY_ERR rc = LY_SUCCESS;
+ ly_bool enabled, child_unres_disabled = 0;
+ uint32_t opt_prev = ctx->compile_opts;
+
+ assert(target->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF));
+
+ /* nodetype checks */
+ if (aug_p->actions && !lysc_node_actions_p(target)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid augment of %s node which is not allowed to contain RPC/action node \"%s\".",
+ lys_nodetype2str(target->nodetype), aug_p->actions->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if (aug_p->notifs && !lysc_node_notifs_p(target)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid augment of %s node which is not allowed to contain notification node \"%s\".",
+ lys_nodetype2str(target->nodetype), aug_p->notifs->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* augment if-features */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, aug_p->iffeatures, &enabled), cleanup);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ child_unres_disabled = 1;
+ }
+
+ /* augment children */
+ rc = lys_compile_augment_children(ctx, aug_p->when, aug_p->flags, aug_p->child, target, child_unres_disabled);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* augment actions */
+ rc = lys_compile_augment_children(ctx, aug_p->when, aug_p->flags, (struct lysp_node *)aug_p->actions, target,
+ child_unres_disabled);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* augment notifications */
+ rc = lys_compile_augment_children(ctx, aug_p->when, aug_p->flags, (struct lysp_node *)aug_p->notifs, target,
+ child_unres_disabled);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* compile extensions into the target */
+ COMPILE_EXTS_GOTO(ctx, aug_p->exts, target->exts, target, rc, cleanup);
+
+cleanup:
+ ctx->compile_opts = opt_prev;
+ return rc;
+}
+
+LY_ERR
+lys_compile_node_augments(struct lysc_ctx *ctx, struct lysc_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *orig_mod = ctx->cur_mod;
+ struct lysp_module *orig_pmod = ctx->pmod;
+ uint32_t i;
+ char orig_path[LYSC_CTX_BUFSIZE];
+ struct lysc_augment *aug;
+
+ /* uses augments */
+ for (i = 0; i < ctx->uses_augs.count; ) {
+ aug = ctx->uses_augs.objs[i];
+
+ if (!lysp_schema_nodeid_match(aug->nodeid, orig_mod->parsed, aug->ext, aug->nodeid_ctx_node, node, NULL, NULL,
+ ctx->ext)) {
+ /* not our target node */
+ ++i;
+ continue;
+ }
+
+ /* use the path and modules from the augment */
+ lysc_update_path(ctx, NULL, "{augment}");
+ lysc_update_path(ctx, NULL, aug->aug_p->nodeid);
+ ctx->pmod = (struct lysp_module *)aug->aug_pmod;
+
+ /* apply augment, restore the path */
+ ret = lys_compile_augment(ctx, aug->aug_p, node);
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* augment was applied, remove it (index and the whole set may have changed because other augments
+ * could have been applied) */
+ ly_set_rm(&ctx->uses_augs, aug, NULL);
+ lysc_augment_free(ctx->ctx, aug);
+ i = 0;
+ }
+
+ /* top-level augments */
+ for (i = 0; i < ctx->augs.count; ) {
+ aug = ctx->augs.objs[i];
+
+ if (!lysp_schema_nodeid_match(aug->nodeid, aug->aug_pmod, aug->ext, NULL, node, NULL, NULL, ctx->ext)) {
+ /* not our target node */
+ ++i;
+ continue;
+ }
+
+ /* use the path and modules from the augment */
+ strcpy(orig_path, ctx->path);
+ ctx->path_len = 1;
+ ctx->cur_mod = aug->aug_pmod->mod;
+ ctx->pmod = (struct lysp_module *)aug->aug_pmod;
+ lysc_update_path(ctx, NULL, "{augment}");
+ lysc_update_path(ctx, NULL, aug->aug_p->nodeid);
+
+ /* apply augment, restore the path */
+ ret = lys_compile_augment(ctx, aug->aug_p, node);
+ strcpy(ctx->path, orig_path);
+ ctx->path_len = strlen(ctx->path);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* augment was applied, remove it */
+ ly_set_rm(&ctx->augs, aug, NULL);
+ lysc_augment_free(ctx->ctx, aug);
+ i = 0;
+ }
+
+cleanup:
+ ctx->cur_mod = orig_mod;
+ ctx->pmod = orig_pmod;
+ return ret;
+}
+
+/**
+ * @brief Prepare an absolute-nodeid augment to be applied during data nodes compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] aug_p Parsed augment to be applied.
+ * @param[in] pmod Both current and prefix module for @p aug_p.
+ * @param[in] ext Extension instance in case @p aug_p is defined in one.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_precompile_own_augment(struct lysc_ctx *ctx, struct lysp_node_augment *aug_p, const struct lysp_module *pmod,
+ const struct lysp_ext_instance *ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ struct lysc_augment *aug;
+ const struct lys_module *mod;
+
+ /* parse its target, it was already parsed and fully checked (except for the existence of the nodes) */
+ ret = lyxp_expr_parse(ctx->ctx, aug_p->nodeid, strlen(aug_p->nodeid), 0, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ mod = lys_schema_node_get_module(ctx->ctx, exp->expr + exp->tok_pos[1], exp->tok_len[1], pmod, NULL, NULL);
+ LY_CHECK_ERR_GOTO(!mod, LOGINT(ctx->ctx); ret = LY_EINT, cleanup);
+ if (mod != ctx->cur_mod) {
+ /* augment for another module, ignore */
+ goto cleanup;
+ }
+
+ /* allocate new compiled augment and store it in the set */
+ aug = calloc(1, sizeof *aug);
+ LY_CHECK_ERR_GOTO(!aug, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(&ctx->augs, aug, 1, NULL), cleanup);
+
+ aug->nodeid = exp;
+ exp = NULL;
+ aug->aug_pmod = pmod;
+ aug->ext = ext;
+ aug->aug_p = aug_p;
+
+cleanup:
+ lyxp_expr_free(ctx->ctx, exp);
+ return ret;
+}
+
+/**
+ * @brief Prepare all top-level augments and extension instance augments to be applied during data nodes compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pmod Parsed mod to use.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_precompile_own_augments_mod(struct lysc_ctx *ctx, const struct lysp_module *pmod)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysp_node_augment *aug_p;
+
+ /* module */
+ LY_LIST_FOR(pmod->augments, aug_p) {
+ LY_CHECK_RET(lys_precompile_own_augment(ctx, aug_p, pmod, NULL));
+ }
+
+ /* parsed extension instances */
+ LY_ARRAY_FOR(pmod->exts, u) {
+ aug_p = NULL;
+ LY_ARRAY_FOR(pmod->exts[u].substmts, v) {
+ if (pmod->exts[u].substmts[v].stmt == LY_STMT_AUGMENT) {
+ aug_p = *(struct lysp_node_augment **)pmod->exts[u].substmts[v].storage;
+ break;
+ }
+ }
+ if (!aug_p) {
+ continue;
+ }
+
+ LY_CHECK_RET(lys_precompile_own_augment(ctx, aug_p, pmod, &pmod->exts[u]));
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_precompile_own_augments(struct lysc_ctx *ctx)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ const struct lys_module *aug_mod;
+ const struct lysp_module *submod;
+
+ LY_ARRAY_FOR(ctx->cur_mod->augmented_by, u) {
+ aug_mod = ctx->cur_mod->augmented_by[u];
+
+ /* collect all module augments */
+ LY_CHECK_RET(lys_precompile_own_augments_mod(ctx, aug_mod->parsed));
+
+ /* collect all submodules augments */
+ LY_ARRAY_FOR(aug_mod->parsed->includes, v) {
+ submod = (struct lysp_module *)aug_mod->parsed->includes[v].submodule;
+
+ LY_CHECK_RET(lys_precompile_own_augments_mod(ctx, submod));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Prepare a deviation to be applied during data nodes compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] dev_p Parsed deviation to be applied.
+ * @param[in] pmod Both current and prefix module for @p dev_p.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_precompile_own_deviation(struct lysc_ctx *ctx, struct lysp_deviation *dev_p, const struct lysp_module *pmod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_deviation *dev = NULL;
+ struct lyxp_expr *exp = NULL;
+ struct lysp_deviation **new_dev;
+ const struct lys_module *mod;
+ const struct lysp_module **new_dev_pmod;
+ uint32_t i;
+
+ /* parse its target, it was already parsed and fully checked (except for the existence of the nodes) */
+ ret = lyxp_expr_parse(ctx->ctx, dev_p->nodeid, strlen(dev_p->nodeid), 0, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ mod = lys_schema_node_get_module(ctx->ctx, exp->expr + exp->tok_pos[1], exp->tok_len[1], pmod, NULL, NULL);
+ LY_CHECK_ERR_GOTO(!mod, LOGINT(ctx->ctx); ret = LY_EINT, cleanup);
+ if (mod != ctx->cur_mod) {
+ /* deviation for another module, ignore */
+ goto cleanup;
+ }
+
+ /* try to find the node in already compiled deviations */
+ for (i = 0; i < ctx->devs.count; ++i) {
+ if (lys_abs_schema_nodeid_match(ctx->ctx, exp, pmod, ((struct lysc_deviation *)ctx->devs.objs[i])->nodeid,
+ ((struct lysc_deviation *)ctx->devs.objs[i])->dev_pmods[0])) {
+ dev = ctx->devs.objs[i];
+ break;
+ }
+ }
+
+ if (!dev) {
+ /* allocate new compiled deviation */
+ dev = calloc(1, sizeof *dev);
+ LY_CHECK_ERR_GOTO(!dev, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(&ctx->devs, dev, 1, NULL), cleanup);
+
+ dev->nodeid = exp;
+ exp = NULL;
+ }
+
+ /* add new parsed deviation structure */
+ LY_ARRAY_NEW_GOTO(ctx->ctx, dev->devs, new_dev, ret, cleanup);
+ *new_dev = dev_p;
+ LY_ARRAY_NEW_GOTO(ctx->ctx, dev->dev_pmods, new_dev_pmod, ret, cleanup);
+ *new_dev_pmod = pmod;
+
+cleanup:
+ lyxp_expr_free(ctx->ctx, exp);
+ return ret;
+}
+
+LY_ERR
+lys_precompile_own_deviations(struct lysc_ctx *ctx)
+{
+ LY_ARRAY_COUNT_TYPE u, v, w;
+ struct lys_module *orig_cur_mod;
+ const struct lys_module *dev_mod;
+ struct lysc_deviation *dev;
+ struct lysp_deviate *d;
+ int not_supported;
+ uint32_t i;
+
+ LY_ARRAY_FOR(ctx->cur_mod->deviated_by, u) {
+ dev_mod = ctx->cur_mod->deviated_by[u];
+
+ /* compile all module deviations */
+ LY_ARRAY_FOR(dev_mod->parsed->deviations, v) {
+ LY_CHECK_RET(lys_precompile_own_deviation(ctx, &dev_mod->parsed->deviations[v], dev_mod->parsed));
+ }
+
+ /* compile all submodules deviations */
+ LY_ARRAY_FOR(dev_mod->parsed->includes, v) {
+ LY_ARRAY_FOR(dev_mod->parsed->includes[v].submodule->deviations, w) {
+ LY_CHECK_RET(lys_precompile_own_deviation(ctx, &dev_mod->parsed->includes[v].submodule->deviations[w],
+ (struct lysp_module *)dev_mod->parsed->includes[v].submodule));
+ }
+ }
+ }
+
+ /* set not-supported flags for all the deviations */
+ for (i = 0; i < ctx->devs.count; ++i) {
+ dev = ctx->devs.objs[i];
+ not_supported = 0;
+
+ LY_ARRAY_FOR(dev->devs, u) {
+ LY_LIST_FOR(dev->devs[u]->deviates, d) {
+ if (d->mod == LYS_DEV_NOT_SUPPORTED) {
+ not_supported = 1;
+ break;
+ }
+ }
+ if (not_supported) {
+ break;
+ }
+ }
+ if (not_supported && (LY_ARRAY_COUNT(dev->devs) > 1)) {
+ orig_cur_mod = ctx->cur_mod;
+ ctx->cur_mod = dev->dev_pmods[u]->mod;
+ lysc_update_path(ctx, NULL, "{deviation}");
+ lysc_update_path(ctx, NULL, dev->nodeid->expr);
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Multiple deviations of \"%s\" with one of them being \"not-supported\".", dev->nodeid->expr);
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ ctx->cur_mod = orig_cur_mod;
+ return LY_EVALID;
+ }
+
+ dev->not_supported = not_supported;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add a module reference into an array, checks for duplicities.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] mod Module reference to add.
+ * @param[in,out] mod_array Module sized array to add to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_array_add_mod_ref(struct lysc_ctx *ctx, struct lys_module *mod, struct lys_module ***mod_array)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lys_module **new_mod;
+
+ LY_ARRAY_FOR(*mod_array, u) {
+ if ((*mod_array)[u] == mod) {
+ /* already there */
+ return LY_EEXIST;
+ }
+ }
+
+ /* add the new module ref */
+ LY_ARRAY_NEW_RET(ctx->ctx, *mod_array, new_mod, LY_EMEM);
+ *new_mod = mod;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check whether all modules in a set are implemented.
+ *
+ * @param[in] mod_set Module set to check.
+ * @return Whether all modules are implemented or not.
+ */
+static ly_bool
+lys_precompile_mod_set_is_all_implemented(const struct ly_set *mod_set)
+{
+ uint32_t i;
+ const struct lys_module *mod;
+
+ for (i = 0; i < mod_set->count; ++i) {
+ mod = mod_set->objs[i];
+ if (!mod->implemented) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Add references to target modules of top-level augments, deviations, and augments in extension instances
+ * in a module and all its submodules.
+ *
+ * @param[in] pmod Module to process.
+ * @param[in,out] mod_set Module set to add referenced modules into.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lys_precompile_mod_augments_deviations(struct lysp_module *pmod, struct ly_set *mod_set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysc_ctx ctx;
+ struct lys_module *m;
+ struct lysp_node_augment *aug;
+ struct ly_set set = {0};
+
+ LYSC_CTX_INIT_PMOD(ctx, pmod, NULL);
+
+ LY_LIST_FOR(pmod->augments, aug) {
+ /* get target module */
+ lysc_update_path(&ctx, NULL, "{augment}");
+ lysc_update_path(&ctx, NULL, aug->nodeid);
+ ret = lys_nodeid_mod_check(&ctx, aug->nodeid, 1, &set, NULL, &m);
+ lysc_update_path(&ctx, NULL, NULL);
+ lysc_update_path(&ctx, NULL, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add this module into the target module augmented_by, if not there and implemented */
+ if ((lys_array_add_mod_ref(&ctx, pmod->mod, &m->augmented_by) != LY_EEXIST) ||
+ !lys_precompile_mod_set_is_all_implemented(&set)) {
+ LY_CHECK_GOTO(ret = ly_set_merge(mod_set, &set, 0, NULL), cleanup);
+ }
+ ly_set_erase(&set, NULL);
+ }
+
+ LY_ARRAY_FOR(pmod->deviations, u) {
+ /* get target module */
+ lysc_update_path(&ctx, NULL, "{deviation}");
+ lysc_update_path(&ctx, NULL, pmod->deviations[u].nodeid);
+ ret = lys_nodeid_mod_check(&ctx, pmod->deviations[u].nodeid, 1, &set, NULL, &m);
+ lysc_update_path(&ctx, NULL, NULL);
+ lysc_update_path(&ctx, NULL, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add this module into the target module deviated_by, if not there and implemented */
+ if ((lys_array_add_mod_ref(&ctx, pmod->mod, &m->deviated_by) != LY_EEXIST) ||
+ !lys_precompile_mod_set_is_all_implemented(&set)) {
+ LY_CHECK_GOTO(ret = ly_set_merge(mod_set, &set, 0, NULL), cleanup);
+ }
+ ly_set_erase(&set, NULL);
+ }
+
+ LY_ARRAY_FOR(pmod->exts, u) {
+ aug = NULL;
+ LY_ARRAY_FOR(pmod->exts[u].substmts, v) {
+ if (pmod->exts[u].substmts[v].stmt == LY_STMT_AUGMENT) {
+ aug = *(struct lysp_node_augment **)pmod->exts[u].substmts[v].storage;
+ break;
+ }
+ }
+ if (!aug) {
+ continue;
+ }
+
+ /* get target module */
+ lysc_update_path(&ctx, NULL, "{ext-augment}");
+ lysc_update_path(&ctx, NULL, aug->nodeid);
+ ret = lys_nodeid_mod_check(&ctx, aug->nodeid, 1, &set, NULL, &m);
+ lysc_update_path(&ctx, NULL, NULL);
+ lysc_update_path(&ctx, NULL, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add this module into the target module augmented_by, if not there and implemented */
+ if ((lys_array_add_mod_ref(&ctx, pmod->mod, &m->augmented_by) != LY_EEXIST) ||
+ !lys_precompile_mod_set_is_all_implemented(&set)) {
+ LY_CHECK_GOTO(ret = ly_set_merge(mod_set, &set, 0, NULL), cleanup);
+ }
+ ly_set_erase(&set, NULL);
+ }
+
+cleanup:
+ ly_set_erase(&set, NULL);
+ return ret;
+}
+
+LY_ERR
+lys_precompile_augments_deviations(struct lys_module *mod, struct lys_glob_unres *unres)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lys_module *m;
+ struct lysp_module *submod;
+ const char **imp_f, *all_f[] = {"*", NULL};
+ uint32_t i;
+ struct ly_set mod_set = {0};
+
+ /* module */
+ LY_CHECK_GOTO(ret = lys_precompile_mod_augments_deviations(mod->parsed, &mod_set), cleanup);
+
+ /* submodules */
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ submod = (struct lysp_module *)mod->parsed->includes[u].submodule;
+ LY_CHECK_GOTO(ret = lys_precompile_mod_augments_deviations(submod, &mod_set), cleanup);
+ }
+
+ for (i = 0; i < mod_set.count; ++i) {
+ m = mod_set.objs[i];
+
+ if (m == mod) {
+ /* will be applied normally later */
+ continue;
+ }
+
+ /* we do not actually need the target modules compiled with out amends, they just need to be implemented
+ * not compiled yet and marked for compilation */
+
+ if (!m->implemented) {
+ /* implement the target module */
+ imp_f = (mod->ctx->flags & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL;
+ r = lys_implement(m, imp_f, unres);
+ if (r == LY_ERECOMPILE) {
+ /* implement all the modules right away to save possible later recompilation */
+ ret = r;
+ continue;
+ } else if (r) {
+ /* error */
+ ret = r;
+ goto cleanup;
+ }
+ } else if (m->compiled) {
+ /* target module was already compiled without our amends (augment/deviation), we need to recompile it */
+ m->to_compile = 1;
+ ret = LY_ERECOMPILE;
+ continue;
+ }
+ }
+
+cleanup:
+ ly_set_erase(&mod_set, NULL);
+ return ret;
+}
+
+void
+lys_precompile_augments_deviations_revert(struct ly_ctx *ctx, const struct lys_module *mod)
+{
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u, count;
+ struct lys_module *m;
+
+ for (i = 0; i < ctx->list.count; ++i) {
+ m = ctx->list.objs[i];
+
+ if (m->augmented_by) {
+ count = LY_ARRAY_COUNT(m->augmented_by);
+ for (u = 0; u < count; ++u) {
+ if (m->augmented_by[u] == mod) {
+ /* keep the order */
+ if (u < count - 1) {
+ memmove(m->augmented_by + u, m->augmented_by + u + 1, (count - u - 1) * sizeof *m->augmented_by);
+ }
+ LY_ARRAY_DECREMENT(m->augmented_by);
+ break;
+ }
+ }
+ if (!LY_ARRAY_COUNT(m->augmented_by)) {
+ LY_ARRAY_FREE(m->augmented_by);
+ m->augmented_by = NULL;
+ }
+ }
+
+ if (m->deviated_by) {
+ count = LY_ARRAY_COUNT(m->deviated_by);
+ for (u = 0; u < count; ++u) {
+ if (m->deviated_by[u] == mod) {
+ /* keep the order */
+ if (u < count - 1) {
+ memmove(m->deviated_by + u, m->deviated_by + u + 1, (count - u - 1) * sizeof *m->deviated_by);
+ }
+ LY_ARRAY_DECREMENT(m->deviated_by);
+ break;
+ }
+ }
+ if (!LY_ARRAY_COUNT(m->deviated_by)) {
+ LY_ARRAY_FREE(m->deviated_by);
+ m->deviated_by = NULL;
+ }
+ }
+ }
+}
diff --git a/src/schema_compile_amend.h b/src/schema_compile_amend.h
new file mode 100644
index 0000000..344af65
--- /dev/null
+++ b/src/schema_compile_amend.h
@@ -0,0 +1,181 @@
+/**
+ * @file schema_compile_amend.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for schema compilation of augments, deviations, and refines.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SCHEMA_COMPILE_AMEND_H_
+#define LY_SCHEMA_COMPILE_AMEND_H_
+
+#include "log.h"
+
+struct ly_ctx;
+struct lysp_qname;
+struct lysp_node;
+struct lysc_node;
+struct lysc_ctx;
+struct lysp_node_uses;
+struct lys_glob_unres;
+struct lys_module;
+
+/**
+ * @brief Compiled parsed augment structure. Just a temporary storage for applying the augment to data.
+ */
+struct lysc_augment {
+ struct lyxp_expr *nodeid; /**< augment target */
+ const struct lysp_module *aug_pmod; /**< module where the augment is defined, for top-level augments
+ used to resolve prefixes, for uses augments used as the context pmod */
+ const struct lysc_node *nodeid_ctx_node; /**< nodeid context node for relative targets */
+ const struct lysp_ext_instance *ext; /**< parent extension instance, in case the augment is from one */
+
+ struct lysp_node_augment *aug_p; /**< pointer to the parsed augment to apply */
+};
+
+/**
+ * @brief Compiled parsed deviation structure. Just a temporary storage for applying the deviation to data.
+ */
+struct lysc_deviation {
+ struct lyxp_expr *nodeid; /**< deviation target, taken from the first deviation in
+ ::lysc_deviation.dev_pmods array, this module is used for resolving
+ prefixes used in the nodeid. */
+
+ struct lysp_deviation **devs; /**< sized array of all the parsed deviations for one target node */
+ const struct lysp_module **dev_pmods; /**< sized array of parsed modules of @p devs (where the specific deviations
+ are defined). */
+ ly_bool not_supported; /**< whether this is a not-supported deviation */
+};
+
+/**
+ * @brief Compiled parsed refine structure. Just a temporary storage for applying the refine to data.
+ */
+struct lysc_refine {
+ struct lyxp_expr *nodeid; /**< refine target */
+ const struct lysp_module *nodeid_pmod; /**< module where the nodeid is defined, used to resolve prefixes */
+ const struct lysc_node *nodeid_ctx_node; /**< nodeid context node */
+ struct lysp_node_uses *uses_p; /**< parsed uses node of the refine, for tracking recursive refines */
+
+ struct lysp_refine **rfns; /**< sized array of parsed refines to apply */
+};
+
+/**
+ * @brief Prepare any uses augments and refines in the context to be applied during uses descendant node compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] uses_p Parsed uses structure with augments and refines.
+ * @param[in] ctx_node Context node of @p uses_p meaning its first data definition parent.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_precompile_uses_augments_refines(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p,
+ const struct lysc_node *ctx_node);
+
+/**
+ * @brief Duplicate qname structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] orig_qname Structure to read from.
+ * @param[in,out] qname Structure to fill.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_qname_dup(const struct ly_ctx *ctx, const struct lysp_qname *orig_qname, struct lysp_qname *qname);
+
+/**
+ * @brief Free a compiled augment temporary structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] aug Structure to free.
+ */
+void lysc_augment_free(const struct ly_ctx *ctx, struct lysc_augment *aug);
+
+/**
+ * @brief Free a compiled deviation temporary structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] dev Structure to free.
+ */
+void lysc_deviation_free(const struct ly_ctx *ctx, struct lysc_deviation *dev);
+
+/**
+ * @brief Free a compiled refine temporary structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] rfn Structure to free.
+ */
+void lysc_refine_free(const struct ly_ctx *ctx, struct lysc_refine *rfn);
+
+/**
+ * @brief Free a duplicate of parsed node. It is returned by ::lys_compile_node_deviations_refines().
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] dev_pnode Parsed node to free.
+ */
+void lysp_dev_node_free(struct lysc_ctx *cctx, struct lysp_node *dev_pnode);
+
+/**
+ * @brief Compile and apply any precompiled deviations and refines targeting a node.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pnode Parsed node to consider.
+ * @param[in] parent First compiled parent of @p pnode.
+ * @param[out] dev_pnode Copy of parsed node @p pnode with deviations and refines, if any. NULL if there are none.
+ * @param[out] not_supported Whether a not-supported deviation is defined for the node.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_node_deviations_refines(struct lysc_ctx *ctx, const struct lysp_node *pnode,
+ const struct lysc_node *parent, struct lysp_node **dev_pnode, ly_bool *not_supported);
+
+/**
+ * @brief Compile and apply any precompiled top-level or uses augments targeting a node.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Compiled node to consider.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_node_augments(struct lysc_ctx *ctx, struct lysc_node *node);
+
+/**
+ * @brief Prepare all top-level augments for the current module to be applied during data nodes compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_precompile_own_augments(struct lysc_ctx *ctx);
+
+/**
+ * @brief Prepare all deviations for the current module to be applied during data nodes compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_precompile_own_deviations(struct lysc_ctx *ctx);
+
+/**
+ * @brief Add references to target modules of top-level augments and deviations in a module and all its submodules.
+ * Adds the module reference to the target modules and if not implemented, implement them (but not compile).
+ *
+ * @param[in] mod Module to process.
+ * @param[in,out] unres Global unres to use.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE on required recompilation of the dep set.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_precompile_augments_deviations(struct lys_module *mod, struct lys_glob_unres *unres);
+
+/**
+ * @brief Revert precompilation of module augments and deviations. Meaning remove its reference from
+ * all the target modules.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] mod Mod whose precompilation to revert.
+ */
+void lys_precompile_augments_deviations_revert(struct ly_ctx *ctx, const struct lys_module *mod);
+
+#endif /* LY_SCHEMA_COMPILE_AMEND_H_ */
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
new file mode 100644
index 0000000..0b64dcb
--- /dev/null
+++ b/src/schema_compile_node.c
@@ -0,0 +1,4218 @@
+/**
+ * @file schema_compile_node.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Schema compilation of common nodes.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "schema_compile_node.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "dict.h"
+#include "log.h"
+#include "plugins.h"
+#include "plugins_internal.h"
+#include "plugins_types.h"
+#include "schema_compile.h"
+#include "schema_compile_amend.h"
+#include "schema_features.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+static struct lysc_ext_instance *
+lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
+{
+ /* TODO - extensions, increase refcount */
+ (void) ctx;
+ (void) orig;
+ return NULL;
+}
+
+/**
+ * @brief Add a node with a when to unres.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] when Specific compiled when to check.
+ * @param[in] node Compiled node with when(s).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysc_unres_when_add(struct lysc_ctx *ctx, struct lysc_when *when, struct lysc_node *node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysc_unres_when *w = NULL;
+
+ /* do not check must(s) in a grouping */
+ if (ctx->compile_opts & LYS_COMPILE_GROUPING) {
+ goto cleanup;
+ }
+
+ /* add new unres when */
+ w = calloc(1, sizeof *w);
+ LY_CHECK_ERR_GOTO(!w, LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup);
+
+ w->node = node;
+ w->when = when;
+
+ /* add into the unres set */
+ LY_CHECK_ERR_GOTO(ly_set_add(&ctx->unres->whens, w, 1, NULL), LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup);
+ w = NULL;
+
+cleanup:
+ free(w);
+ return rc;
+}
+
+/**
+ * @brief Add a node with must(s) to unres.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Compiled node with must(s).
+ * @param[in] pnode Parsed ndoe with must(s).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysc_unres_must_add(struct lysc_ctx *ctx, struct lysc_node *node, struct lysp_node *pnode)
+{
+ struct lysc_unres_must *m = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_must *musts;
+ struct lysp_restr *pmusts;
+ LY_ERR ret;
+
+ /* do not check must(s) in a grouping */
+ if (ctx->compile_opts & LYS_COMPILE_GROUPING) {
+ return LY_SUCCESS;
+ }
+
+ musts = lysc_node_musts(node);
+ pmusts = lysp_node_musts(pnode);
+ assert(LY_ARRAY_COUNT(musts) == LY_ARRAY_COUNT(pmusts));
+
+ if (!musts) {
+ /* no must */
+ return LY_SUCCESS;
+ }
+
+ /* add new unres must */
+ m = calloc(1, sizeof *m);
+ LY_CHECK_ERR_GOTO(!m, ret = LY_EMEM, error);
+ m->node = node;
+
+ /* add must local modules */
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, m->local_mods, LY_ARRAY_COUNT(pmusts), ret, error);
+ LY_ARRAY_FOR(pmusts, u) {
+ m->local_mods[u] = pmusts[u].arg.mod;
+ LY_ARRAY_INCREMENT(m->local_mods);
+ }
+
+ /* store ext */
+ m->ext = ctx->ext;
+
+ LY_CHECK_ERR_GOTO(ly_set_add(&ctx->unres->musts, m, 1, NULL), ret = LY_EMEM, error);
+
+ return LY_SUCCESS;
+
+error:
+ if (m) {
+ LY_ARRAY_FREE(m->local_mods);
+ free(m);
+ }
+ LOGMEM(ctx->ctx);
+ return ret;
+}
+
+static LY_ERR
+lysc_unres_leafref_add(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf, const struct lysp_module *local_mod)
+{
+ struct lysc_unres_leafref *l = NULL;
+ struct ly_set *leafrefs_set;
+ LY_ARRAY_COUNT_TYPE u;
+ int is_lref = 0;
+
+ if (ctx->compile_opts & LYS_COMPILE_GROUPING) {
+ /* do not check leafrefs in groupings */
+ return LY_SUCCESS;
+ }
+
+ /* use special set for disabled leafrefs */
+ leafrefs_set = ctx->compile_opts & LYS_COMPILE_DISABLED ? &ctx->unres->disabled_leafrefs : &ctx->unres->leafrefs;
+
+ if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+ /* leafref */
+ is_lref = 1;
+ } else if (leaf->type->basetype == LY_TYPE_UNION) {
+ /* union with leafrefs */
+ LY_ARRAY_FOR(((struct lysc_type_union *)leaf->type)->types, u) {
+ if (((struct lysc_type_union *)leaf->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
+ is_lref = 1;
+ break;
+ }
+ }
+ }
+
+ if (is_lref) {
+ /* add new unresolved leafref node */
+ l = calloc(1, sizeof *l);
+ LY_CHECK_ERR_RET(!l, LOGMEM(ctx->ctx), LY_EMEM);
+
+ l->node = &leaf->node;
+ l->local_mod = local_mod;
+ l->ext = ctx->ext;
+
+ LY_CHECK_ERR_RET(ly_set_add(leafrefs_set, l, 1, NULL), free(l); LOGMEM(ctx->ctx), LY_EMEM);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add/replace a leaf default value in unres.
+ * Can also be used for a single leaf-list default value.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] leaf Leaf with the default value.
+ * @param[in] dflt Default value to use.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysc_unres_leaf_dflt_add(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf, struct lysp_qname *dflt)
+{
+ struct lysc_unres_dflt *r = NULL;
+ uint32_t i;
+
+ if (ctx->compile_opts & (LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING)) {
+ return LY_SUCCESS;
+ }
+
+ for (i = 0; i < ctx->unres->dflts.count; ++i) {
+ if (((struct lysc_unres_dflt *)ctx->unres->dflts.objs[i])->leaf == leaf) {
+ /* just replace the default */
+ r = ctx->unres->dflts.objs[i];
+ lysp_qname_free(ctx->ctx, r->dflt);
+ free(r->dflt);
+ break;
+ }
+ }
+ if (!r) {
+ /* add new unres item */
+ r = calloc(1, sizeof *r);
+ LY_CHECK_ERR_RET(!r, LOGMEM(ctx->ctx), LY_EMEM);
+ r->leaf = leaf;
+
+ LY_CHECK_RET(ly_set_add(&ctx->unres->dflts, r, 1, NULL));
+ }
+
+ r->dflt = malloc(sizeof *r->dflt);
+ LY_CHECK_GOTO(!r->dflt, error);
+ LY_CHECK_GOTO(lysp_qname_dup(ctx->ctx, dflt, r->dflt), error);
+
+ return LY_SUCCESS;
+
+error:
+ free(r->dflt);
+ LOGMEM(ctx->ctx);
+ return LY_EMEM;
+}
+
+/**
+ * @brief Add/replace a leaf-list default value(s) in unres.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] llist Leaf-list with the default value.
+ * @param[in] dflts Sized array of the default values.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysc_unres_llist_dflts_add(struct lysc_ctx *ctx, struct lysc_node_leaflist *llist, struct lysp_qname *dflts)
+{
+ struct lysc_unres_dflt *r = NULL;
+ uint32_t i;
+
+ if (ctx->compile_opts & (LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING)) {
+ return LY_SUCCESS;
+ }
+
+ for (i = 0; i < ctx->unres->dflts.count; ++i) {
+ if (((struct lysc_unres_dflt *)ctx->unres->dflts.objs[i])->llist == llist) {
+ /* just replace the defaults */
+ r = ctx->unres->dflts.objs[i];
+ lysp_qname_free(ctx->ctx, r->dflt);
+ free(r->dflt);
+ r->dflt = NULL;
+ FREE_ARRAY(ctx->ctx, r->dflts, lysp_qname_free);
+ r->dflts = NULL;
+ break;
+ }
+ }
+ if (!r) {
+ r = calloc(1, sizeof *r);
+ LY_CHECK_ERR_RET(!r, LOGMEM(ctx->ctx), LY_EMEM);
+ r->llist = llist;
+
+ LY_CHECK_RET(ly_set_add(&ctx->unres->dflts, r, 1, NULL));
+ }
+
+ DUP_ARRAY(ctx->ctx, dflts, r->dflts, lysp_qname_dup);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add a bits/enumeration type to unres.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] leaf Leaf of type bits/enumeration whose disabled items to free.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysc_unres_bitenum_add(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf)
+{
+ if (ctx->compile_opts & (LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING)) {
+ /* skip groupings and redundant for disabled nodes */
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(ly_set_add(&ctx->unres->disabled_bitenums, leaf, 1, NULL));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Duplicate the compiled pattern structure.
+ *
+ * Instead of duplicating memory, the reference counter in the @p orig is increased.
+ *
+ * @param[in] orig The pattern structure to duplicate.
+ * @return The duplicated structure to use.
+ */
+static struct lysc_pattern *
+lysc_pattern_dup(struct lysc_pattern *orig)
+{
+ ++orig->refcount;
+ return orig;
+}
+
+/**
+ * @brief Duplicate the array of compiled patterns.
+ *
+ * The sized array itself is duplicated, but the pattern structures are just shadowed by increasing their reference counter.
+ *
+ * @param[in] ctx Libyang context for logging.
+ * @param[in] orig The patterns sized array to duplicate.
+ * @return New sized array as a copy of @p orig.
+ * @return NULL in case of memory allocation error.
+ */
+static struct lysc_pattern **
+lysc_patterns_dup(struct ly_ctx *ctx, struct lysc_pattern **orig)
+{
+ struct lysc_pattern **dup = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(orig);
+
+ LY_ARRAY_CREATE_RET(ctx, dup, LY_ARRAY_COUNT(orig), NULL);
+ LY_ARRAY_FOR(orig, u) {
+ dup[u] = lysc_pattern_dup(orig[u]);
+ LY_ARRAY_INCREMENT(dup);
+ }
+ return dup;
+}
+
+/**
+ * @brief Duplicate compiled range structure.
+ *
+ * @param[in] ctx Libyang context for logging.
+ * @param[in] orig The range structure to be duplicated.
+ * @return New compiled range structure as a copy of @p orig.
+ * @return NULL in case of memory allocation error.
+ */
+static struct lysc_range *
+lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
+{
+ struct lysc_range *dup;
+ LY_ERR ret;
+
+ assert(orig);
+
+ dup = calloc(1, sizeof *dup);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
+ if (orig->parts) {
+ LY_ARRAY_CREATE_GOTO(ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, cleanup);
+ (*((LY_ARRAY_COUNT_TYPE *)(dup->parts) - 1)) = LY_ARRAY_COUNT(orig->parts);
+ memcpy(dup->parts, orig->parts, LY_ARRAY_COUNT(dup->parts) * sizeof *dup->parts);
+ }
+ DUP_STRING_GOTO(ctx, orig->eapptag, dup->eapptag, ret, cleanup);
+ DUP_STRING_GOTO(ctx, orig->emsg, dup->emsg, ret, cleanup);
+ dup->exts = lysc_ext_instance_dup(ctx, orig->exts);
+
+ return dup;
+cleanup:
+ free(dup);
+ (void) ret; /* set but not used due to the return type */
+ return NULL;
+}
+
+/**
+ * @brief Print status into a string.
+ *
+ * @param[in] flags Flags with the status to print.
+ * @return String status.
+ */
+static const char *
+lys_status2str(uint16_t flags)
+{
+ flags &= LYS_STATUS_MASK;
+
+ switch (flags) {
+ case 0:
+ case LYS_STATUS_CURR:
+ return "current";
+ case LYS_STATUS_DEPRC:
+ return "deprecated";
+ case LYS_STATUS_OBSLT:
+ return "obsolete";
+ default:
+ LOGINT(NULL);
+ return NULL;
+ }
+}
+
+/**
+ * @brief Compile status information of the given statement.
+ *
+ * To simplify getting status of the node, the flags are set following inheritance rules, so all the nodes
+ * has the status correctly set during the compilation.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] parsed_flags Parsed statement flags.
+ * @param[in] inherited_flags Parsed inherited flags from a schema-only statement (augment, uses, ext instance, ...).
+ * @param[in] parent_flags Compiled parent node flags.
+ * @param[in] parent_name Name of the parent node, for logging.
+ * @param[in] stmt_name Statement name, for logging.
+ * @param[in,out] stmt_flags Statement flags with the correct status set.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_status(struct lysc_ctx *ctx, uint16_t parsed_flags, uint16_t inherited_flags, uint16_t parent_flags,
+ const char *parent_name, const char *stmt_name, uint16_t *stmt_flags)
+{
+ /* normalize to status-only */
+ parsed_flags &= LYS_STATUS_MASK;
+ inherited_flags &= LYS_STATUS_MASK;
+ parent_flags &= LYS_STATUS_MASK;
+
+ /* check for conflicts */
+ if (parent_flags && parsed_flags && (parent_flags > parsed_flags)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Status \"%s\" of \"%s\" is in conflict with \"%s\" status of parent \"%s\".",
+ lys_status2str(parsed_flags), stmt_name, lys_status2str(parent_flags), parent_name);
+ return LY_EVALID;
+ } else if (inherited_flags && parsed_flags && (inherited_flags > parsed_flags)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Inherited schema-only status \"%s\" is in conflict with \"%s\" status of \"%s\".",
+ lys_status2str(inherited_flags), lys_status2str(parsed_flags), stmt_name);
+ return LY_EVALID;
+ } else if (parent_flags && inherited_flags && (parent_flags > inherited_flags)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Status \"%s\" of parent \"%s\" is in conflict with inherited schema-only status \"%s\".",
+ lys_status2str(parent_flags), parent_name, lys_status2str(inherited_flags));
+ return LY_EVALID;
+ }
+
+ /* clear */
+ (*stmt_flags) &= ~LYS_STATUS_MASK;
+
+ if (parsed_flags) {
+ /* explicit status */
+ (*stmt_flags) |= parsed_flags;
+ } else if (inherited_flags) {
+ /* inherited status from a schema-only statement */
+ (*stmt_flags) |= inherited_flags;
+ } else if (parent_flags) {
+ /* inherited status from a parent node */
+ (*stmt_flags) |= parent_flags;
+ } else {
+ /* default status */
+ (*stmt_flags) |= LYS_STATUS_CURR;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile information from the when statement
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] when_p Parsed when structure.
+ * @param[in] inherited_flags Inherited flags from a schema-only statement.
+ * @param[in] parent Parent node, if any.
+ * @param[in] ctx_node Context node for the when statement.
+ * @param[out] when Pointer where to store pointer to the created compiled when structure.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_when_(struct lysc_ctx *ctx, const struct lysp_when *when_p, uint16_t inherited_flags,
+ const struct lysc_node *parent, const struct lysc_node *ctx_node, struct lysc_when **when)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_VALUE_FORMAT format;
+
+ *when = calloc(1, sizeof **when);
+ LY_CHECK_ERR_RET(!(*when), LOGMEM(ctx->ctx), LY_EMEM);
+ (*when)->refcount = 1;
+ LY_CHECK_RET(lyxp_expr_parse(ctx->ctx, when_p->cond, 0, 1, &(*when)->cond));
+ LY_CHECK_RET(lyplg_type_prefix_data_new(ctx->ctx, when_p->cond, strlen(when_p->cond),
+ LY_VALUE_SCHEMA, ctx->pmod, &format, (void **)&(*when)->prefixes));
+ (*when)->context = (struct lysc_node *)ctx_node;
+ DUP_STRING_GOTO(ctx->ctx, when_p->dsc, (*when)->dsc, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, when_p->ref, (*when)->ref, ret, done);
+ COMPILE_EXTS_GOTO(ctx, when_p->exts, (*when)->exts, (*when), ret, done);
+ LY_CHECK_RET(lys_compile_status(ctx, 0, inherited_flags, parent ? parent->flags : 0, parent ? parent->name : NULL,
+ "when", &(*when)->flags));
+
+done:
+ return ret;
+}
+
+LY_ERR
+lys_compile_when(struct lysc_ctx *ctx, const struct lysp_when *when_p, uint16_t inherited_flags, const struct lysc_node *parent,
+ const struct lysc_node *ctx_node, struct lysc_node *node, struct lysc_when **when_c)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysc_when **new_when, ***node_when, *ptr;
+
+ assert(when_p && (node || when_c));
+
+ if (node) {
+ /* get the when array */
+ node_when = lysc_node_when_p(node);
+
+ /* create new when pointer */
+ LY_ARRAY_NEW_GOTO(ctx->ctx, *node_when, new_when, rc, cleanup);
+ } else {
+ /* individual when */
+ new_when = &ptr;
+ *new_when = calloc(1, sizeof **new_when);
+ LY_CHECK_ERR_GOTO(!*new_when, LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup);
+ }
+
+ if (!when_c || !(*when_c)) {
+ /* compile when */
+ LY_CHECK_GOTO(rc = lys_compile_when_(ctx, when_p, inherited_flags, parent, ctx_node, new_when), cleanup);
+
+ /* remember the compiled when for sharing */
+ if (when_c) {
+ *when_c = *new_when;
+ }
+ } else {
+ /* use the previously compiled when */
+ ++(*when_c)->refcount;
+ *new_when = *when_c;
+ }
+
+ /* add when to unres */
+ LY_CHECK_GOTO(rc = lysc_unres_when_add(ctx, *new_when, node), cleanup);
+
+cleanup:
+ return rc;
+}
+
+LY_ERR
+lys_compile_must(struct lysc_ctx *ctx, const struct lysp_restr *must_p, struct lysc_must *must)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_VALUE_FORMAT format;
+
+ LY_CHECK_RET(lyxp_expr_parse(ctx->ctx, must_p->arg.str, 0, 1, &must->cond));
+ LY_CHECK_RET(lyplg_type_prefix_data_new(ctx->ctx, must_p->arg.str, strlen(must_p->arg.str),
+ LY_VALUE_SCHEMA, must_p->arg.mod, &format, (void **)&must->prefixes));
+ DUP_STRING_GOTO(ctx->ctx, must_p->eapptag, must->eapptag, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, must_p->emsg, must->emsg, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, must_p->dsc, must->dsc, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, must_p->ref, must->ref, ret, done);
+ COMPILE_EXTS_GOTO(ctx, must_p->exts, must->exts, must, ret, done);
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Validate and normalize numeric value from a range definition.
+ * @param[in] ctx Compile context.
+ * @param[in] basetype Base YANG built-in type of the node connected with the range restriction. Actually only LY_TYPE_DEC64 is important to
+ * allow processing of the fractions. The fraction point is extracted from the value which is then normalize according to given frdigits into
+ * valcopy to allow easy parsing and storing of the value. libyang stores decimal number without the decimal point which is always recovered from
+ * the known fraction-digits value. So, with fraction-digits 2, number 3.14 is stored as 314 and number 1 is stored as 100.
+ * @param[in] frdigits The fraction-digits of the type in case of LY_TYPE_DEC64.
+ * @param[in] value String value of the range boundary.
+ * @param[out] len Number of the processed bytes from the value. Processing stops on the first character which is not part of the number boundary.
+ * @param[out] valcopy NULL-terminated string with the numeric value to parse and store.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID (no number) or LY_EINVAL (decimal64 not matching fraction-digits value).
+ */
+static LY_ERR
+range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value,
+ size_t *len, char **valcopy)
+{
+ size_t fraction = 0, size;
+
+ *len = 0;
+
+ assert(value);
+ /* parse value */
+ if (!isdigit(value[*len]) && (value[*len] != '-') && (value[*len] != '+')) {
+ return LY_EVALID;
+ }
+
+ if ((value[*len] == '-') || (value[*len] == '+')) {
+ ++(*len);
+ }
+
+ while (isdigit(value[*len])) {
+ ++(*len);
+ }
+
+ if ((basetype != LY_TYPE_DEC64) || (value[*len] != '.') || !isdigit(value[*len + 1])) {
+ if (basetype == LY_TYPE_DEC64) {
+ goto decimal;
+ } else {
+ *valcopy = strndup(value, *len);
+ return LY_SUCCESS;
+ }
+ }
+ fraction = *len;
+
+ ++(*len);
+ while (isdigit(value[*len])) {
+ ++(*len);
+ }
+
+ if (basetype == LY_TYPE_DEC64) {
+decimal:
+ assert(frdigits);
+ if (fraction && (*len - 1 - fraction > frdigits)) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Range boundary \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
+ (int)(*len), value, frdigits);
+ return LY_EINVAL;
+ }
+ if (fraction) {
+ size = (*len) + (frdigits - ((*len) - 1 - fraction));
+ } else {
+ size = (*len) + frdigits + 1;
+ }
+ *valcopy = malloc(size * sizeof **valcopy);
+ LY_CHECK_ERR_RET(!(*valcopy), LOGMEM(ctx->ctx), LY_EMEM);
+
+ (*valcopy)[size - 1] = '\0';
+ if (fraction) {
+ memcpy(&(*valcopy)[0], &value[0], fraction);
+ memcpy(&(*valcopy)[fraction], &value[fraction + 1], (*len) - 1 - (fraction));
+ memset(&(*valcopy)[(*len) - 1], '0', frdigits - ((*len) - 1 - fraction));
+ } else {
+ memcpy(&(*valcopy)[0], &value[0], *len);
+ memset(&(*valcopy)[*len], '0', frdigits);
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that values in range are in ascendant order.
+ * @param[in] unsigned_value Flag to note that we are working with unsigned values.
+ * @param[in] max Flag to distinguish if checking min or max value. min value must be strictly higher than previous,
+ * max can be also equal.
+ * @param[in] value Current value to check.
+ * @param[in] prev_value The last seen value.
+ * @return LY_SUCCESS or LY_EEXIST for invalid order.
+ */
+static LY_ERR
+range_part_check_ascendancy(ly_bool unsigned_value, ly_bool max, int64_t value, int64_t prev_value)
+{
+ if (unsigned_value) {
+ if ((max && ((uint64_t)prev_value > (uint64_t)value)) || (!max && ((uint64_t)prev_value >= (uint64_t)value))) {
+ return LY_EEXIST;
+ }
+ } else {
+ if ((max && (prev_value > value)) || (!max && (prev_value >= value))) {
+ return LY_EEXIST;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Set min/max value of the range part.
+ * @param[in] ctx Compile context.
+ * @param[in] part Range part structure to fill.
+ * @param[in] max Flag to distinguish if storing min or max value.
+ * @param[in] prev The last seen value to check that all values in range are specified in ascendant order.
+ * @param[in] basetype Type of the value to get know implicit min/max values and other checking rules.
+ * @param[in] first Flag for the first value of the range to avoid ascendancy order.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range from the type from which the current type is derived (if not built-in) to get type's min and max values.
+ * @param[in,out] value Numeric range value to be stored, if not provided the type's min/max value is set.
+ * @return LY_ERR value - LY_SUCCESS, LY_EDENIED (value brokes type's boundaries), LY_EVALID (not a number),
+ * LY_EEXIST (value is smaller than the previous one), LY_EINVAL (decimal64 value does not corresponds with the
+ * frdigits value), LY_EMEM.
+ */
+static LY_ERR
+range_part_minmax(struct lysc_ctx *ctx, struct lysc_range_part *part, ly_bool max, int64_t prev, LY_DATA_TYPE basetype,
+ ly_bool first, ly_bool length_restr, uint8_t frdigits, struct lysc_range *base_range, const char **value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *valcopy = NULL;
+ size_t len = 0;
+
+ if (value) {
+ ret = range_part_check_value_syntax(ctx, basetype, frdigits, *value, &len, &valcopy);
+ LY_CHECK_GOTO(ret, finalize);
+ }
+ if (!valcopy && base_range) {
+ if (max) {
+ part->max_64 = base_range->parts[LY_ARRAY_COUNT(base_range->parts) - 1].max_64;
+ } else {
+ part->min_64 = base_range->parts[0].min_64;
+ }
+ if (!first) {
+ ret = range_part_check_ascendancy(basetype <= LY_TYPE_STRING ? 1 : 0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ goto finalize;
+ }
+
+ switch (basetype) {
+ case LY_TYPE_INT8: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, strlen(valcopy), INT64_C(-128), INT64_C(127), LY_BASE_DEC, max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(127);
+ } else {
+ part->min_64 = INT64_C(-128);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_INT16: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, strlen(valcopy), INT64_C(-32768), INT64_C(32767), LY_BASE_DEC,
+ max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(32767);
+ } else {
+ part->min_64 = INT64_C(-32768);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_INT32: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, strlen(valcopy), INT64_C(-2147483648), INT64_C(2147483647), LY_BASE_DEC,
+ max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(2147483647);
+ } else {
+ part->min_64 = INT64_C(-2147483648);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_INT64: /* range */
+ case LY_TYPE_DEC64: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, strlen(valcopy), INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807),
+ LY_BASE_DEC, max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(9223372036854775807);
+ } else {
+ part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT8: /* range */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, strlen(valcopy), UINT64_C(255), LY_BASE_DEC, max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(255);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT16: /* range */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, strlen(valcopy), UINT64_C(65535), LY_BASE_DEC, max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(65535);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT32: /* range */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, strlen(valcopy), UINT64_C(4294967295), LY_BASE_DEC,
+ max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(4294967295);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT64: /* range */
+ case LY_TYPE_STRING: /* length */
+ case LY_TYPE_BINARY: /* length */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, strlen(valcopy), UINT64_C(18446744073709551615), LY_BASE_DEC,
+ max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(18446744073709551615);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ ret = LY_EINT;
+ }
+
+finalize:
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - value \"%s\" does not fit the type limitations.",
+ length_restr ? "length" : "range", valcopy ? valcopy : *value);
+ } else if (ret == LY_EVALID) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - invalid value \"%s\".",
+ length_restr ? "length" : "range", valcopy ? valcopy : *value);
+ } else if (ret == LY_EEXIST) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - values are not in ascending order (%s).",
+ length_restr ? "length" : "range",
+ (valcopy && basetype != LY_TYPE_DEC64) ? valcopy : value ? *value : max ? "max" : "min");
+ } else if (!ret && value) {
+ *value = *value + len;
+ }
+ free(valcopy);
+ return ret;
+}
+
+LY_ERR
+lys_compile_type_range(struct lysc_ctx *ctx, const struct lysp_restr *range_p, LY_DATA_TYPE basetype, ly_bool length_restr,
+ uint8_t frdigits, struct lysc_range *base_range, struct lysc_range **range)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *expr;
+ struct lysc_range_part *parts = NULL, *part;
+ ly_bool range_expected = 0, uns;
+ LY_ARRAY_COUNT_TYPE parts_done = 0, u, v;
+
+ assert(range);
+ assert(range_p);
+
+ expr = range_p->arg.str;
+ while (1) {
+ if (isspace(*expr)) {
+ ++expr;
+ } else if (*expr == '\0') {
+ if (range_expected) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected end of the expression after \"..\" (%s).",
+ length_restr ? "length" : "range", range_p->arg.str);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (!parts || (parts_done == LY_ARRAY_COUNT(parts))) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected end of the expression (%s).",
+ length_restr ? "length" : "range", range_p->arg.str);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ parts_done++;
+ break;
+ } else if (!strncmp(expr, "min", ly_strlen_const("min"))) {
+ if (parts) {
+ /* min cannot be used elsewhere than in the first part */
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected data before min keyword (%.*s).", length_restr ? "length" : "range",
+ (int)(expr - range_p->arg.str), range_p->arg.str);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ expr += ly_strlen_const("min");
+
+ LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+ LY_CHECK_GOTO(ret = range_part_minmax(ctx, part, 0, 0, basetype, 1, length_restr, frdigits, base_range, NULL), cleanup);
+ part->max_64 = part->min_64;
+ } else if (*expr == '|') {
+ if (!parts || range_expected) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected beginning of the expression (%s).", length_restr ? "length" : "range", expr);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ expr++;
+ parts_done++;
+ /* process next part of the expression */
+ } else if (!strncmp(expr, "..", 2)) {
+ expr += 2;
+ while (isspace(*expr)) {
+ expr++;
+ }
+ if (!parts || (LY_ARRAY_COUNT(parts) == parts_done)) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected \"..\" without a lower bound.", length_restr ? "length" : "range");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ /* continue expecting the upper boundary */
+ range_expected = 1;
+ } else if (isdigit(*expr) || (*expr == '-') || (*expr == '+')) {
+ /* number */
+ if (range_expected) {
+ part = &parts[LY_ARRAY_COUNT(parts) - 1];
+ LY_CHECK_GOTO(ret = range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, NULL, &expr), cleanup);
+ range_expected = 0;
+ } else {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+ LY_CHECK_GOTO(ret = range_part_minmax(ctx, part, 0, parts_done ? parts[LY_ARRAY_COUNT(parts) - 2].max_64 : 0,
+ basetype, parts_done ? 0 : 1, length_restr, frdigits, NULL, &expr), cleanup);
+ part->max_64 = part->min_64;
+ }
+
+ /* continue with possible another expression part */
+ } else if (!strncmp(expr, "max", ly_strlen_const("max"))) {
+ expr += ly_strlen_const("max");
+ while (isspace(*expr)) {
+ expr++;
+ }
+ if (*expr != '\0') {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data after max keyword (%s).",
+ length_restr ? "length" : "range", expr);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (range_expected) {
+ part = &parts[LY_ARRAY_COUNT(parts) - 1];
+ LY_CHECK_GOTO(ret = range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, base_range, NULL), cleanup);
+ range_expected = 0;
+ } else {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+ LY_CHECK_GOTO(ret = range_part_minmax(ctx, part, 1, parts_done ? parts[LY_ARRAY_COUNT(parts) - 2].max_64 : 0,
+ basetype, parts_done ? 0 : 1, length_restr, frdigits, base_range, NULL), cleanup);
+ part->min_64 = part->max_64;
+ }
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data (%s).",
+ length_restr ? "length" : "range", expr);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* check with the previous range/length restriction */
+ if (base_range) {
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_UINT64:
+ case LY_TYPE_STRING:
+ uns = 1;
+ break;
+ case LY_TYPE_DEC64:
+ case LY_TYPE_INT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_INT64:
+ uns = 0;
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ for (u = v = 0; u < parts_done && v < LY_ARRAY_COUNT(base_range->parts); ++u) {
+ if ((uns && (parts[u].min_u64 < base_range->parts[v].min_u64)) || (!uns && (parts[u].min_64 < base_range->parts[v].min_64))) {
+ goto baseerror;
+ }
+ /* current lower bound is not lower than the base */
+ if (base_range->parts[v].min_64 == base_range->parts[v].max_64) {
+ /* base has single value */
+ if (base_range->parts[v].min_64 == parts[u].min_64) {
+ /* both lower bounds are the same */
+ if (parts[u].min_64 != parts[u].max_64) {
+ /* current continues with a range */
+ goto baseerror;
+ } else {
+ /* equal single values, move both forward */
+ ++v;
+ continue;
+ }
+ } else {
+ /* base is single value lower than current range, so the
+ * value from base range is removed in the current,
+ * move only base and repeat checking */
+ ++v;
+ --u;
+ continue;
+ }
+ } else {
+ /* base is the range */
+ if (parts[u].min_64 == parts[u].max_64) {
+ /* current is a single value */
+ if ((uns && (parts[u].max_u64 > base_range->parts[v].max_u64)) || (!uns && (parts[u].max_64 > base_range->parts[v].max_64))) {
+ /* current is behind the base range, so base range is omitted,
+ * move the base and keep the current for further check */
+ ++v;
+ --u;
+ } /* else it is within the base range, so move the current, but keep the base */
+ continue;
+ } else {
+ /* both are ranges - check the higher bound, the lower was already checked */
+ if ((uns && (parts[u].max_u64 > base_range->parts[v].max_u64)) || (!uns && (parts[u].max_64 > base_range->parts[v].max_64))) {
+ /* higher bound is higher than the current higher bound */
+ if ((uns && (parts[u].min_u64 > base_range->parts[v].max_u64)) || (!uns && (parts[u].min_64 > base_range->parts[v].max_64))) {
+ /* but the current lower bound is also higher, so the base range is omitted,
+ * continue with the same current, but move the base */
+ --u;
+ ++v;
+ continue;
+ }
+ /* current range starts within the base range but end behind it */
+ goto baseerror;
+ } else {
+ /* current range is smaller than the base,
+ * move current, but stay with the base */
+ continue;
+ }
+ }
+ }
+ }
+ if (u != parts_done) {
+baseerror:
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - the derived restriction (%s) is not equally or more limiting.",
+ length_restr ? "length" : "range", range_p->arg.str);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ if (!(*range)) {
+ *range = calloc(1, sizeof **range);
+ LY_CHECK_ERR_RET(!(*range), LOGMEM(ctx->ctx), LY_EMEM);
+ }
+
+ /* we rewrite the following values as the types chain is being processed */
+ if (range_p->eapptag) {
+ lydict_remove(ctx->ctx, (*range)->eapptag);
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->ctx, range_p->eapptag, 0, &(*range)->eapptag), cleanup);
+ }
+ if (range_p->emsg) {
+ lydict_remove(ctx->ctx, (*range)->emsg);
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->ctx, range_p->emsg, 0, &(*range)->emsg), cleanup);
+ }
+ if (range_p->dsc) {
+ lydict_remove(ctx->ctx, (*range)->dsc);
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->ctx, range_p->dsc, 0, &(*range)->dsc), cleanup);
+ }
+ if (range_p->ref) {
+ lydict_remove(ctx->ctx, (*range)->ref);
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->ctx, range_p->ref, 0, &(*range)->ref), cleanup);
+ }
+ /* extensions are taken only from the last range by the caller */
+
+ (*range)->parts = parts;
+ parts = NULL;
+cleanup:
+ LY_ARRAY_FREE(parts);
+
+ return ret;
+}
+
+/**
+ * @brief Transform characters block in an XML Schema pattern into Perl character ranges.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] pattern Original pattern.
+ * @param[in,out] regex Pattern to modify.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_pattern_chblocks_xmlschema2perl(const struct ly_ctx *ctx, const char *pattern, char **regex)
+{
+#define URANGE_LEN 19
+ char *ublock2urange[][2] = {
+ {"BasicLatin", "[\\x{0000}-\\x{007F}]"},
+ {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"},
+ {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"},
+ {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"},
+ {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"},
+ {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"},
+ {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"},
+ {"Greek", "[\\x{0370}-\\x{03FF}]"},
+ {"Cyrillic", "[\\x{0400}-\\x{04FF}]"},
+ {"Armenian", "[\\x{0530}-\\x{058F}]"},
+ {"Hebrew", "[\\x{0590}-\\x{05FF}]"},
+ {"Arabic", "[\\x{0600}-\\x{06FF}]"},
+ {"Syriac", "[\\x{0700}-\\x{074F}]"},
+ {"Thaana", "[\\x{0780}-\\x{07BF}]"},
+ {"Devanagari", "[\\x{0900}-\\x{097F}]"},
+ {"Bengali", "[\\x{0980}-\\x{09FF}]"},
+ {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"},
+ {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"},
+ {"Oriya", "[\\x{0B00}-\\x{0B7F}]"},
+ {"Tamil", "[\\x{0B80}-\\x{0BFF}]"},
+ {"Telugu", "[\\x{0C00}-\\x{0C7F}]"},
+ {"Kannada", "[\\x{0C80}-\\x{0CFF}]"},
+ {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"},
+ {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"},
+ {"Thai", "[\\x{0E00}-\\x{0E7F}]"},
+ {"Lao", "[\\x{0E80}-\\x{0EFF}]"},
+ {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"},
+ {"Myanmar", "[\\x{1000}-\\x{109F}]"},
+ {"Georgian", "[\\x{10A0}-\\x{10FF}]"},
+ {"HangulJamo", "[\\x{1100}-\\x{11FF}]"},
+ {"Ethiopic", "[\\x{1200}-\\x{137F}]"},
+ {"Cherokee", "[\\x{13A0}-\\x{13FF}]"},
+ {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"},
+ {"Ogham", "[\\x{1680}-\\x{169F}]"},
+ {"Runic", "[\\x{16A0}-\\x{16FF}]"},
+ {"Khmer", "[\\x{1780}-\\x{17FF}]"},
+ {"Mongolian", "[\\x{1800}-\\x{18AF}]"},
+ {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"},
+ {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"},
+ {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"},
+ {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"},
+ {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"},
+ {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"},
+ {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"},
+ {"NumberForms", "[\\x{2150}-\\x{218F}]"},
+ {"Arrows", "[\\x{2190}-\\x{21FF}]"},
+ {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"},
+ {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"},
+ {"ControlPictures", "[\\x{2400}-\\x{243F}]"},
+ {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"},
+ {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"},
+ {"BoxDrawing", "[\\x{2500}-\\x{257F}]"},
+ {"BlockElements", "[\\x{2580}-\\x{259F}]"},
+ {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"},
+ {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"},
+ {"Dingbats", "[\\x{2700}-\\x{27BF}]"},
+ {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"},
+ {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"},
+ {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"},
+ {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"},
+ {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"},
+ {"Hiragana", "[\\x{3040}-\\x{309F}]"},
+ {"Katakana", "[\\x{30A0}-\\x{30FF}]"},
+ {"Bopomofo", "[\\x{3100}-\\x{312F}]"},
+ {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"},
+ {"Kanbun", "[\\x{3190}-\\x{319F}]"},
+ {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"},
+ {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"},
+ {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"},
+ {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"},
+ {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"},
+ {"YiSyllables", "[\\x{A000}-\\x{A48F}]"},
+ {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"},
+ {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"},
+ {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"},
+ {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"},
+ {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"},
+ {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"},
+ {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"},
+ {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"},
+ {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"},
+ {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"},
+ {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"},
+ {"Specials", "[\\x{FEFF}|\\x{FFF0}-\\x{FFFD}]"},
+ {NULL, NULL}
+ };
+
+ size_t idx, idx2, start, end;
+ char *perl_regex, *ptr;
+
+ perl_regex = *regex;
+
+ /* substitute Unicode Character Blocks with exact Character Ranges */
+ while ((ptr = strstr(perl_regex, "\\p{Is"))) {
+ start = ptr - perl_regex;
+
+ ptr = strchr(ptr, '}');
+ if (!ptr) {
+ LOGVAL(ctx, LY_VCODE_INREGEXP, pattern, perl_regex + start + 2, "unterminated character property");
+ return LY_EVALID;
+ }
+ end = (ptr - perl_regex) + 1;
+
+ /* need more space */
+ if (end - start < URANGE_LEN) {
+ perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (URANGE_LEN - (end - start)) + 1);
+ *regex = perl_regex;
+ LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx), LY_EMEM);
+ }
+
+ /* find our range */
+ for (idx = 0; ublock2urange[idx][0]; ++idx) {
+ if (!strncmp(perl_regex + start + ly_strlen_const("\\p{Is"),
+ ublock2urange[idx][0], strlen(ublock2urange[idx][0]))) {
+ break;
+ }
+ }
+ if (!ublock2urange[idx][0]) {
+ LOGVAL(ctx, LY_VCODE_INREGEXP, pattern, perl_regex + start + 5, "unknown block name");
+ return LY_EVALID;
+ }
+
+ /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */
+ for (idx2 = 0, idx = 0; idx2 < start; ++idx2) {
+ if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
+ ++idx;
+ }
+ if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
+ --idx;
+ }
+ }
+ if (idx) {
+ /* skip brackets */
+ memmove(perl_regex + start + (URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1);
+ memcpy(perl_regex + start, ublock2urange[idx][1] + 1, URANGE_LEN - 2);
+ } else {
+ memmove(perl_regex + start + URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1);
+ memcpy(perl_regex + start, ublock2urange[idx][1], URANGE_LEN);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_compile_type_pattern_check(struct ly_ctx *ctx, const char *pattern, pcre2_code **code)
+{
+ size_t idx, size, brack;
+ char *perl_regex;
+ int err_code, compile_opts;
+ const char *orig_ptr;
+ PCRE2_SIZE err_offset;
+ pcre2_code *code_local;
+ ly_bool escaped;
+ LY_ERR r;
+
+ /* adjust the expression to a Perl equivalent
+ * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs */
+
+ /* allocate space for the transformed pattern */
+ size = strlen(pattern) + 1;
+ compile_opts = PCRE2_UTF | PCRE2_UCP | PCRE2_ANCHORED | PCRE2_DOLLAR_ENDONLY | PCRE2_NO_AUTO_CAPTURE;
+#ifdef PCRE2_ENDANCHORED
+ compile_opts |= PCRE2_ENDANCHORED;
+#else
+ /* add space for trailing $ anchor */
+ size++;
+#endif
+ perl_regex = malloc(size);
+ LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx), LY_EMEM);
+ perl_regex[0] = '\0';
+
+ /* we need to replace all "$" and "^" (that are not in "[]") with "\$" and "\^" */
+ brack = 0;
+ idx = 0;
+ escaped = 0;
+ orig_ptr = pattern;
+ while (orig_ptr[0]) {
+ switch (orig_ptr[0]) {
+ case '$':
+ case '^':
+ if (!brack) {
+ /* make space for the extra character */
+ ++size;
+ perl_regex = ly_realloc(perl_regex, size);
+ LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx), LY_EMEM);
+
+ /* print escape slash */
+ perl_regex[idx] = '\\';
+ ++idx;
+ }
+ break;
+ case '\\':
+ /* escape character found or backslash is escaped */
+ escaped = !escaped;
+ /* copy backslash and continue with the next character */
+ perl_regex[idx] = orig_ptr[0];
+ ++idx;
+ ++orig_ptr;
+ continue;
+ case '[':
+ if (!escaped) {
+ ++brack;
+ }
+ break;
+ case ']':
+ if (!brack && !escaped) {
+ /* If ']' does not terminate a character class expression, then pcre2_compile() implicitly escapes the
+ * ']' character. But this seems to be against the regular expressions rules declared in
+ * "XML schema: Datatypes" and therefore an error is returned. So for example if pattern is '\[a]' then
+ * pcre2 match characters '[a]' literally but in YANG such pattern is not allowed.
+ */
+ LOGVAL(ctx, LY_VCODE_INREGEXP, pattern, orig_ptr, "character group doesn't begin with '['");
+ free(perl_regex);
+ return LY_EVALID;
+ } else if (!escaped) {
+ --brack;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* copy char */
+ perl_regex[idx] = orig_ptr[0];
+
+ ++idx;
+ ++orig_ptr;
+ escaped = 0;
+ }
+#ifndef PCRE2_ENDANCHORED
+ /* anchor match to end of subject */
+ perl_regex[idx++] = '$';
+#endif
+ perl_regex[idx] = '\0';
+
+ /* transform character blocks */
+ if ((r = lys_compile_pattern_chblocks_xmlschema2perl(ctx, pattern, &perl_regex))) {
+ free(perl_regex);
+ return r;
+ }
+
+ /* must return 0, already checked during parsing */
+ code_local = pcre2_compile((PCRE2_SPTR)perl_regex, PCRE2_ZERO_TERMINATED, compile_opts,
+ &err_code, &err_offset, NULL);
+ if (!code_local) {
+ PCRE2_UCHAR err_msg[LY_PCRE2_MSG_LIMIT] = {0};
+
+ pcre2_get_error_message(err_code, err_msg, LY_PCRE2_MSG_LIMIT);
+ LOGVAL(ctx, LY_VCODE_INREGEXP, pattern, perl_regex + err_offset, err_msg);
+ free(perl_regex);
+ return LY_EVALID;
+ }
+ free(perl_regex);
+
+ if (code) {
+ *code = code_local;
+ } else {
+ free(code_local);
+ }
+
+ return LY_SUCCESS;
+
+#undef URANGE_LEN
+}
+
+LY_ERR
+lys_compile_type_patterns(struct lysc_ctx *ctx, const struct lysp_restr *patterns_p, struct lysc_pattern **base_patterns,
+ struct lysc_pattern ***patterns)
+{
+ struct lysc_pattern **pattern;
+ LY_ARRAY_COUNT_TYPE u;
+ LY_ERR ret = LY_SUCCESS;
+
+ /* first, copy the patterns from the base type */
+ if (base_patterns) {
+ *patterns = lysc_patterns_dup(ctx->ctx, base_patterns);
+ LY_CHECK_ERR_RET(!(*patterns), LOGMEM(ctx->ctx), LY_EMEM);
+ }
+
+ LY_ARRAY_FOR(patterns_p, u) {
+ LY_ARRAY_NEW_RET(ctx->ctx, (*patterns), pattern, LY_EMEM);
+ *pattern = calloc(1, sizeof **pattern);
+ ++(*pattern)->refcount;
+
+ ret = lys_compile_type_pattern_check(ctx->ctx, &patterns_p[u].arg.str[1], &(*pattern)->code);
+ LY_CHECK_RET(ret);
+
+ if (patterns_p[u].arg.str[0] == LYSP_RESTR_PATTERN_NACK) {
+ (*pattern)->inverted = 1;
+ }
+ DUP_STRING_GOTO(ctx->ctx, &patterns_p[u].arg.str[1], (*pattern)->expr, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, patterns_p[u].eapptag, (*pattern)->eapptag, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, patterns_p[u].emsg, (*pattern)->emsg, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, patterns_p[u].dsc, (*pattern)->dsc, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, patterns_p[u].ref, (*pattern)->ref, ret, done);
+ COMPILE_EXTS_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts, (*pattern), ret, done);
+ }
+done:
+ return ret;
+}
+
+/**
+ * @brief map of the possible restrictions combination for the specific built-in type.
+ */
+static uint16_t type_substmt_map[LY_DATA_TYPE_COUNT] = {
+ 0 /* LY_TYPE_UNKNOWN */,
+ LYS_SET_LENGTH /* LY_TYPE_BINARY */,
+ LYS_SET_RANGE /* LY_TYPE_UINT8 */,
+ LYS_SET_RANGE /* LY_TYPE_UINT16 */,
+ LYS_SET_RANGE /* LY_TYPE_UINT32 */,
+ LYS_SET_RANGE /* LY_TYPE_UINT64 */,
+ LYS_SET_LENGTH | LYS_SET_PATTERN /* LY_TYPE_STRING */,
+ LYS_SET_BIT /* LY_TYPE_BITS */,
+ 0 /* LY_TYPE_BOOL */,
+ LYS_SET_FRDIGITS | LYS_SET_RANGE /* LY_TYPE_DEC64 */,
+ 0 /* LY_TYPE_EMPTY */,
+ LYS_SET_ENUM /* LY_TYPE_ENUM */,
+ LYS_SET_BASE /* LY_TYPE_IDENT */,
+ LYS_SET_REQINST /* LY_TYPE_INST */,
+ LYS_SET_REQINST | LYS_SET_PATH /* LY_TYPE_LEAFREF */,
+ LYS_SET_TYPE /* LY_TYPE_UNION */,
+ LYS_SET_RANGE /* LY_TYPE_INT8 */,
+ LYS_SET_RANGE /* LY_TYPE_INT16 */,
+ LYS_SET_RANGE /* LY_TYPE_INT32 */,
+ LYS_SET_RANGE /* LY_TYPE_INT64 */
+};
+
+/**
+ * @brief stringification of the YANG built-in data types
+ */
+const char *ly_data_type2str[LY_DATA_TYPE_COUNT] = {
+ LY_TYPE_UNKNOWN_STR,
+ LY_TYPE_BINARY_STR,
+ LY_TYPE_UINT8_STR,
+ LY_TYPE_UINT16_STR,
+ LY_TYPE_UINT32_STR,
+ LY_TYPE_UINT64_STR,
+ LY_TYPE_STRING_STR,
+ LY_TYPE_BITS_STR,
+ LY_TYPE_BOOL_STR,
+ LY_TYPE_DEC64_STR,
+ LY_TYPE_EMPTY_STR,
+ LY_TYPE_ENUM_STR,
+ LY_TYPE_IDENT_STR,
+ LY_TYPE_INST_STR,
+ LY_TYPE_LEAFREF_STR,
+ LY_TYPE_UNION_STR,
+ LY_TYPE_INT8_STR,
+ LY_TYPE_INT16_STR,
+ LY_TYPE_INT32_STR,
+ LY_TYPE_INT64_STR
+};
+
+LY_ERR
+lys_compile_type_enums(struct lysc_ctx *ctx, const struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype,
+ struct lysc_type_bitenum_item *base_enums, struct lysc_type_bitenum_item **bitenums)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v, match = 0;
+ int32_t highest_value = INT32_MIN, cur_val = INT32_MIN;
+ uint32_t highest_position = 0, cur_pos = 0;
+ struct lysc_type_bitenum_item *e, storage;
+ ly_bool enabled;
+
+ if (base_enums && (ctx->pmod->version < LYS_VERSION_1_1)) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "%s type can be subtyped only in YANG 1.1 modules.",
+ basetype == LY_TYPE_ENUM ? "Enumeration" : "Bits");
+ return LY_EVALID;
+ }
+
+ LY_ARRAY_FOR(enums_p, u) {
+ /* perform all checks */
+ if (base_enums) {
+ /* check the enum/bit presence in the base type - the set of enums/bits in the derived type must be a subset */
+ LY_ARRAY_FOR(base_enums, v) {
+ if (!strcmp(enums_p[u].name, base_enums[v].name)) {
+ break;
+ }
+ }
+ if (v == LY_ARRAY_COUNT(base_enums)) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid %s - derived type adds new item \"%s\".",
+ basetype == LY_TYPE_ENUM ? "enumeration" : "bits", enums_p[u].name);
+ return LY_EVALID;
+ }
+ match = v;
+ }
+
+ if (basetype == LY_TYPE_ENUM) {
+ if (enums_p[u].flags & LYS_SET_VALUE) {
+ /* value assigned by model */
+ cur_val = (int32_t)enums_p[u].value;
+ /* check collision with other values */
+ LY_ARRAY_FOR(*bitenums, v) {
+ if (cur_val == (*bitenums)[v].value) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid enumeration - value %d collide in items \"%s\" and \"%s\".",
+ cur_val, enums_p[u].name, (*bitenums)[v].name);
+ return LY_EVALID;
+ }
+ }
+ } else if (base_enums) {
+ /* inherit the assigned value */
+ cur_val = base_enums[match].value;
+ } else {
+ /* assign value automatically */
+ if (u == 0) {
+ cur_val = 0;
+ } else if (highest_value == INT32_MAX) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid enumeration - it is not possible to auto-assign enum value for "
+ "\"%s\" since the highest value is already 2147483647.", enums_p[u].name);
+ return LY_EVALID;
+ } else {
+ cur_val = highest_value + 1;
+ }
+ }
+
+ /* save highest value for auto assing */
+ if (highest_value < cur_val) {
+ highest_value = cur_val;
+ }
+ } else { /* LY_TYPE_BITS */
+ if (enums_p[u].flags & LYS_SET_VALUE) {
+ /* value assigned by model */
+ cur_pos = (uint32_t)enums_p[u].value;
+ /* check collision with other values */
+ LY_ARRAY_FOR(*bitenums, v) {
+ if (cur_pos == (*bitenums)[v].position) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid bits - position %u collide in items \"%s\" and \"%s\".",
+ cur_pos, enums_p[u].name, (*bitenums)[v].name);
+ return LY_EVALID;
+ }
+ }
+ } else if (base_enums) {
+ /* inherit the assigned value */
+ cur_pos = base_enums[match].position;
+ } else {
+ /* assign value automatically */
+ if (u == 0) {
+ cur_pos = 0;
+ } else if (highest_position == UINT32_MAX) {
+ /* counter overflow */
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid bits - it is not possible to auto-assign bit position for "
+ "\"%s\" since the highest value is already 4294967295.", enums_p[u].name);
+ return LY_EVALID;
+ } else {
+ cur_pos = highest_position + 1;
+ }
+ }
+
+ /* save highest position for auto assing */
+ if (highest_position < cur_pos) {
+ highest_position = cur_pos;
+ }
+ }
+
+ /* the assigned values must not change from the derived type */
+ if (base_enums) {
+ if (basetype == LY_TYPE_ENUM) {
+ if (cur_val != base_enums[match].value) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid enumeration - value of the item \"%s\" has changed from %d to %d in the derived type.",
+ enums_p[u].name, base_enums[match].value, cur_val);
+ return LY_EVALID;
+ }
+ } else {
+ if (cur_pos != base_enums[match].position) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid bits - position of the item \"%s\" has changed from %u to %u in the derived type.",
+ enums_p[u].name, base_enums[match].position, cur_pos);
+ return LY_EVALID;
+ }
+ }
+ }
+
+ /* add new enum/bit */
+ LY_ARRAY_NEW_RET(ctx->ctx, *bitenums, e, LY_EMEM);
+ 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);
+ if (basetype == LY_TYPE_ENUM) {
+ e->value = cur_val;
+ } else {
+ e->position = cur_pos;
+ }
+ COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, ret, done);
+
+ /* evaluate if-ffeatures */
+ LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, enums_p[u].iffeatures, &enabled));
+ if (!enabled) {
+ /* set only flag, later resolved and removed */
+ e->flags |= LYS_DISABLED;
+ }
+
+ if (basetype == LY_TYPE_BITS) {
+ /* keep bits ordered by position */
+ for (v = u; v && (*bitenums)[v - 1].position > e->position; --v) {}
+ if (v != u) {
+ memcpy(&storage, e, sizeof *e);
+ memmove(&(*bitenums)[v + 1], &(*bitenums)[v], (u - v) * sizeof **bitenums);
+ memcpy(&(*bitenums)[v], &storage, sizeof storage);
+ }
+ }
+ }
+
+done:
+ return ret;
+}
+
+static LY_ERR
+lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct lysp_node *context_pnode, uint16_t context_flags,
+ const char *context_name, struct lysc_type ***utypes_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type **utypes = *utypes_p;
+ struct lysc_type_union *un_aux = NULL;
+
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, utypes, LY_ARRAY_COUNT(ptypes), ret, error);
+ for (LY_ARRAY_COUNT_TYPE u = 0, additional = 0; u < LY_ARRAY_COUNT(ptypes); ++u) {
+ ret = lys_compile_type(ctx, context_pnode, context_flags, context_name, &ptypes[u], &utypes[u + additional],
+ NULL, NULL);
+ LY_CHECK_GOTO(ret, error);
+ if (utypes[u + additional]->basetype == LY_TYPE_UNION) {
+ /* add space for additional types from the union subtype */
+ un_aux = (struct lysc_type_union *)utypes[u + additional];
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, utypes,
+ LY_ARRAY_COUNT(ptypes) + additional + LY_ARRAY_COUNT(un_aux->types) - LY_ARRAY_COUNT(utypes), ret, error);
+
+ /* copy subtypes of the subtype union */
+ for (LY_ARRAY_COUNT_TYPE v = 0; v < LY_ARRAY_COUNT(un_aux->types); ++v) {
+ if (un_aux->types[v]->basetype == LY_TYPE_LEAFREF) {
+ struct lysc_type_leafref *lref;
+
+ /* duplicate the whole structure because of the instance-specific path resolving for realtype */
+ utypes[u + additional] = calloc(1, sizeof(struct lysc_type_leafref));
+ LY_CHECK_ERR_GOTO(!utypes[u + additional], LOGMEM(ctx->ctx); ret = LY_EMEM, error);
+ lref = (struct lysc_type_leafref *)utypes[u + additional];
+
+ lref->basetype = LY_TYPE_LEAFREF;
+ ret = lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)un_aux->types[v])->path, 0, 0, &lref->path);
+ LY_CHECK_GOTO(ret, error);
+ lref->refcount = 1;
+ lref->require_instance = ((struct lysc_type_leafref *)un_aux->types[v])->require_instance;
+ ret = lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
+ ((struct lysc_type_leafref *)un_aux->types[v])->prefixes, (void **)&lref->prefixes);
+ LY_CHECK_GOTO(ret, error);
+ /* TODO extensions */
+
+ } else {
+ utypes[u + additional] = un_aux->types[v];
+ LY_ATOMIC_INC_BARRIER(un_aux->types[v]->refcount);
+ }
+ ++additional;
+ LY_ARRAY_INCREMENT(utypes);
+ }
+ /* compensate u increment in main loop */
+ --additional;
+
+ /* free the replaced union subtype */
+ lysc_type_free(&ctx->free_ctx, (struct lysc_type *)un_aux);
+ un_aux = NULL;
+ } else {
+ LY_ARRAY_INCREMENT(utypes);
+ }
+ }
+
+ *utypes_p = utypes;
+ return LY_SUCCESS;
+
+error:
+ if (un_aux) {
+ lysc_type_free(&ctx->free_ctx, (struct lysc_type *)un_aux);
+ }
+ *utypes_p = utypes;
+ return ret;
+}
+
+/**
+ * @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
+ * @param[in] ctx Compile context.
+ * @param[in] context_pnode Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] basetype Base YANG built-in type of the type to compile.
+ * @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
+ * @param[in] base The latest base (compiled) type from which the current type is being derived.
+ * @param[out] type Newly created type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t context_flags, const char *context_name,
+ struct lysp_type *type_p, LY_DATA_TYPE basetype, const char *tpdfname, const struct lysc_type *base,
+ struct lysc_type **type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_bin *bin;
+ struct lysc_type_num *num;
+ struct lysc_type_str *str;
+ struct lysc_type_bits *bits;
+ struct lysc_type_enum *enumeration;
+ struct lysc_type_dec *dec;
+ struct lysc_type_identityref *idref;
+ struct lysc_type_leafref *lref;
+ struct lysc_type_union *un;
+
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ bin = (struct lysc_type_bin *)(*type);
+
+ /* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
+ if (type_p->length) {
+ LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_bin *)base)->length : NULL, &bin->length));
+ if (!tpdfname) {
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, ret, cleanup);
+ }
+ }
+ break;
+ case LY_TYPE_BITS:
+ /* RFC 7950 9.7 - bits */
+ bits = (struct lysc_type_bits *)(*type);
+ if (type_p->bits) {
+ LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->bits, basetype,
+ base ? (struct lysc_type_bitenum_item *)((struct lysc_type_bits *)base)->bits : NULL,
+ (struct lysc_type_bitenum_item **)&bits->bits));
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from bits built-in type must contain at least one bit */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "bit", "bits type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "bit", "bits type", "");
+ }
+ return LY_EVALID;
+ }
+ break;
+ case LY_TYPE_DEC64:
+ dec = (struct lysc_type_dec *)(*type);
+
+ /* RFC 7950 9.3.4 - fraction-digits */
+ if (!base) {
+ if (!type_p->fraction_digits) {
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type", "");
+ }
+ return LY_EVALID;
+ }
+ dec->fraction_digits = type_p->fraction_digits;
+ } else {
+ if (type_p->fraction_digits) {
+ /* fraction digits is prohibited in types not directly derived from built-in decimal64 */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid fraction-digits substatement for type \"%s\" not directly derived from decimal64 built-in type.",
+ tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.");
+ }
+ return LY_EVALID;
+ }
+ dec->fraction_digits = ((struct lysc_type_dec *)base)->fraction_digits;
+ }
+
+ /* RFC 7950 9.2.4 - range */
+ if (type_p->range) {
+ LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+ base ? ((struct lysc_type_dec *)base)->range : NULL, &dec->range));
+ if (!tpdfname) {
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, ret, cleanup);
+ }
+ }
+ break;
+ case LY_TYPE_STRING:
+ str = (struct lysc_type_str *)(*type);
+
+ /* RFC 7950 9.4.4 - length */
+ if (type_p->length) {
+ LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_str *)base)->length : NULL, &str->length));
+ if (!tpdfname) {
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, ret, cleanup);
+ }
+ } else if (base && ((struct lysc_type_str *)base)->length) {
+ str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str *)base)->length);
+ }
+
+ /* RFC 7950 9.4.5 - pattern */
+ if (type_p->patterns) {
+ LY_CHECK_RET(lys_compile_type_patterns(ctx, type_p->patterns,
+ base ? ((struct lysc_type_str *)base)->patterns : NULL, &str->patterns));
+ } else if (base && ((struct lysc_type_str *)base)->patterns) {
+ str->patterns = lysc_patterns_dup(ctx->ctx, ((struct lysc_type_str *)base)->patterns);
+ }
+ break;
+ case LY_TYPE_ENUM:
+ enumeration = (struct lysc_type_enum *)(*type);
+
+ /* RFC 7950 9.6 - enum */
+ if (type_p->enums) {
+ LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->enums, basetype,
+ base ? ((struct lysc_type_enum *)base)->enums : NULL, &enumeration->enums));
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from enumerations built-in type must contain at least one enum */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type", "");
+ }
+ return LY_EVALID;
+ }
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ num = (struct lysc_type_num *)(*type);
+
+ /* RFC 6020 9.2.4 - range */
+ if (type_p->range) {
+ LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
+ base ? ((struct lysc_type_num *)base)->range : NULL, &num->range));
+ if (!tpdfname) {
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, ret, cleanup);
+ }
+ }
+ break;
+ case LY_TYPE_IDENT:
+ idref = (struct lysc_type_identityref *)(*type);
+
+ /* RFC 7950 9.10.2 - base */
+ if (type_p->bases) {
+ if (base) {
+ /* only the directly derived identityrefs can contain base specification */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid base substatement for the type \"%s\" not directly derived from identityref built-in type.",
+ tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid base substatement for the type not directly derived from identityref built-in type.");
+ }
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases));
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from identityref built-in type must contain at least one base */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "base", "identityref type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "base", "identityref type", "");
+ }
+ return LY_EVALID;
+ }
+ break;
+ case LY_TYPE_LEAFREF:
+ lref = (struct lysc_type_leafref *)*type;
+
+ /* RFC 7950 9.9.3 - require-instance */
+ if (type_p->flags & LYS_SET_REQINST) {
+ if (type_p->pmod->version < LYS_VERSION_1_1) {
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Leafref type \"%s\" can be restricted by require-instance statement only in YANG 1.1 modules.", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
+ }
+ return LY_EVALID;
+ }
+ lref->require_instance = type_p->require_instance;
+ } else if (base) {
+ /* inherit */
+ lref->require_instance = ((struct lysc_type_leafref *)base)->require_instance;
+ } else {
+ /* default is true */
+ lref->require_instance = 1;
+ }
+ if (type_p->path) {
+ LY_VALUE_FORMAT format;
+
+ LY_CHECK_RET(lyxp_expr_dup(ctx->ctx, type_p->path, 0, 0, &lref->path));
+ LY_CHECK_RET(lyplg_type_prefix_data_new(ctx->ctx, type_p->path->expr, strlen(type_p->path->expr),
+ LY_VALUE_SCHEMA, type_p->pmod, &format, (void **)&lref->prefixes));
+ } else if (base) {
+ LY_CHECK_RET(lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)base)->path, 0, 0, &lref->path));
+ LY_CHECK_RET(lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
+ ((struct lysc_type_leafref *)base)->prefixes, (void **)&lref->prefixes));
+ } else if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+ return LY_EVALID;
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+ return LY_EVALID;
+ }
+ break;
+ case LY_TYPE_INST:
+ /* RFC 7950 9.9.3 - require-instance */
+ if (type_p->flags & LYS_SET_REQINST) {
+ ((struct lysc_type_instanceid *)(*type))->require_instance = type_p->require_instance;
+ } else {
+ /* default is true */
+ ((struct lysc_type_instanceid *)(*type))->require_instance = 1;
+ }
+ break;
+ case LY_TYPE_UNION:
+ un = (struct lysc_type_union *)(*type);
+
+ /* RFC 7950 7.4 - type */
+ if (type_p->types) {
+ if (base) {
+ /* only the directly derived union can contain types specification */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid type substatement for the type \"%s\" not directly derived from union built-in type.",
+ tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
+ "Invalid type substatement for the type not directly derived from union built-in type.");
+ }
+ return LY_EVALID;
+ }
+ /* compile the type */
+ LY_CHECK_RET(lys_compile_type_union(ctx, type_p->types, context_pnode, context_flags, context_name, &un->types));
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from union built-in type must contain at least one type */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "type", "union type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "type", "union type", "");
+ }
+ return LY_EVALID;
+ }
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ case LY_TYPE_UNKNOWN: /* just to complete switch */
+ break;
+ }
+
+ if (tpdfname) {
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_bin));
+ break;
+ case LY_TYPE_BITS:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_bits));
+ break;
+ case LY_TYPE_DEC64:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_dec));
+ break;
+ case LY_TYPE_STRING:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_str));
+ break;
+ case LY_TYPE_ENUM:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_enum));
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_num));
+ break;
+ case LY_TYPE_IDENT:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_identityref));
+ break;
+ case LY_TYPE_LEAFREF:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_leafref));
+ break;
+ case LY_TYPE_INST:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_instanceid));
+ break;
+ case LY_TYPE_UNION:
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_union));
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ case LY_TYPE_UNKNOWN: /* just to complete switch */
+ break;
+ }
+ }
+ LY_CHECK_ERR_RET(!(*type), LOGMEM(ctx->ctx), LY_EMEM);
+
+cleanup:
+ return ret;
+}
+
+LY_ERR
+lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t context_flags, const char *context_name,
+ const struct lysp_type *type_p, struct lysc_type **type, const char **units, struct lysp_qname **dflt)
+{
+ LY_ERR ret = LY_SUCCESS;
+ ly_bool dummyloops = 0;
+ struct type_context {
+ const struct lysp_tpdf *tpdf;
+ struct lysp_node *node;
+ } *tctx, *tctx_prev = NULL, *tctx_iter;
+ LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
+ struct lysc_type *base = NULL, *prev_type;
+ struct ly_set tpdf_chain = {0};
+ struct lyplg_type *plugin;
+
+ (*type) = NULL;
+ if (dflt) {
+ *dflt = NULL;
+ }
+
+ tctx = calloc(1, sizeof *tctx);
+ LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
+ for (ret = lysp_type_find(type_p->name, context_pnode, type_p->pmod, ctx->ext, &basetype, &tctx->tpdf, &tctx->node);
+ ret == LY_SUCCESS;
+ ret = lysp_type_find(tctx_prev->tpdf->type.name, tctx_prev->node, tctx_prev->tpdf->type.pmod, ctx->ext,
+ &basetype, &tctx->tpdf, &tctx->node)) {
+ if (basetype) {
+ break;
+ }
+
+ /* check status */
+ ret = lysc_check_status(ctx, context_flags, (void *)type_p->pmod, context_name, tctx->tpdf->flags,
+ (void *)tctx->tpdf->type.pmod, tctx->node ? tctx->node->name : tctx->tpdf->name);
+ LY_CHECK_ERR_GOTO(ret, free(tctx), cleanup);
+
+ if (units && !*units) {
+ /* inherit units */
+ DUP_STRING(ctx->ctx, tctx->tpdf->units, *units, ret);
+ LY_CHECK_ERR_GOTO(ret, free(tctx), cleanup);
+ }
+ if (dflt && !*dflt && tctx->tpdf->dflt.str) {
+ /* inherit default */
+ *dflt = (struct lysp_qname *)&tctx->tpdf->dflt;
+ }
+ if (dummyloops && (!units || *units) && dflt && *dflt) {
+ basetype = ((struct type_context *)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
+ break;
+ }
+
+ if (tctx->tpdf->type.compiled && (tctx->tpdf->type.compiled->refcount == 1)) {
+ /* context recompilation - everything was freed previously (the only reference is from the parsed type itself)
+ * and we need now recompile the type again in the updated context. */
+ lysc_type_free(&ctx->free_ctx, tctx->tpdf->type.compiled);
+ ((struct lysp_tpdf *)tctx->tpdf)->type.compiled = NULL;
+ }
+
+ if (tctx->tpdf->type.compiled) {
+ /* it is not necessary to continue, the rest of the chain was already compiled,
+ * but we still may need to inherit default and units values, so start dummy loops */
+ basetype = tctx->tpdf->type.compiled->basetype;
+ ret = ly_set_add(&tpdf_chain, tctx, 1, NULL);
+ LY_CHECK_ERR_GOTO(ret, free(tctx), cleanup);
+
+ if ((units && !*units) || (dflt && !*dflt)) {
+ dummyloops = 1;
+ goto preparenext;
+ } else {
+ tctx = NULL;
+ break;
+ }
+ }
+
+ /* circular typedef reference detection */
+ for (uint32_t u = 0; u < tpdf_chain.count; u++) {
+ /* local part */
+ tctx_iter = (struct type_context *)tpdf_chain.objs[u];
+ if (tctx_iter->tpdf == tctx->tpdf) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
+ free(tctx);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ for (uint32_t u = 0; u < ctx->tpdf_chain.count; u++) {
+ /* global part for unions corner case */
+ tctx_iter = (struct type_context *)ctx->tpdf_chain.objs[u];
+ if (tctx_iter->tpdf == tctx->tpdf) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
+ free(tctx);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* store information for the following processing */
+ ret = ly_set_add(&tpdf_chain, tctx, 1, NULL);
+ LY_CHECK_ERR_GOTO(ret, free(tctx), cleanup);
+
+preparenext:
+ /* prepare next loop */
+ tctx_prev = tctx;
+ tctx = calloc(1, sizeof *tctx);
+ LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
+ }
+ free(tctx);
+
+ /* allocate type according to the basetype */
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ *type = calloc(1, sizeof(struct lysc_type_bin));
+ break;
+ case LY_TYPE_BITS:
+ *type = calloc(1, sizeof(struct lysc_type_bits));
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ *type = calloc(1, sizeof(struct lysc_type));
+ break;
+ case LY_TYPE_DEC64:
+ *type = calloc(1, sizeof(struct lysc_type_dec));
+ break;
+ case LY_TYPE_ENUM:
+ *type = calloc(1, sizeof(struct lysc_type_enum));
+ break;
+ case LY_TYPE_IDENT:
+ *type = calloc(1, sizeof(struct lysc_type_identityref));
+ break;
+ case LY_TYPE_INST:
+ *type = calloc(1, sizeof(struct lysc_type_instanceid));
+ break;
+ case LY_TYPE_LEAFREF:
+ *type = calloc(1, sizeof(struct lysc_type_leafref));
+ break;
+ case LY_TYPE_STRING:
+ *type = calloc(1, sizeof(struct lysc_type_str));
+ break;
+ case LY_TYPE_UNION:
+ *type = calloc(1, sizeof(struct lysc_type_union));
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ *type = calloc(1, sizeof(struct lysc_type_num));
+ break;
+ case LY_TYPE_UNKNOWN:
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Referenced type \"%s\" not found.", tctx_prev ? tctx_prev->tpdf->type.name : type_p->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
+ if (~type_substmt_map[basetype] & type_p->flags) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid type restrictions for %s type.",
+ ly_data_type2str[basetype]);
+ free(*type);
+ (*type) = NULL;
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* get restrictions from the referred typedefs */
+ for (uint32_t u = tpdf_chain.count - 1; u + 1 > 0; --u) {
+ tctx = (struct type_context *)tpdf_chain.objs[u];
+
+ /* remember the typedef context for circular check */
+ ret = ly_set_add(&ctx->tpdf_chain, tctx, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (tctx->tpdf->type.compiled) {
+ /* already compiled */
+ base = tctx->tpdf->type.compiled;
+ continue;
+ }
+
+ /* try to find loaded user type plugins */
+ plugin = lyplg_type_plugin_find(tctx->tpdf->type.pmod->mod->name, tctx->tpdf->type.pmod->mod->revision,
+ tctx->tpdf->name);
+ if (!plugin && base) {
+ /* use the base type implementation if available */
+ plugin = base->plugin;
+ }
+ if (!plugin) {
+ /* use the internal built-in type implementation */
+ plugin = lyplg_type_plugin_find("", NULL, ly_data_type2str[basetype]);
+ }
+ assert(plugin);
+
+ if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags) &&
+ (plugin == base->plugin)) {
+ /* no change, reuse the compiled base */
+ ((struct lysp_tpdf *)tctx->tpdf)->type.compiled = base;
+ LY_ATOMIC_INC_BARRIER(base->refcount);
+ continue;
+ }
+
+ LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ if (~type_substmt_map[basetype] & tctx->tpdf->type.flags) {
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid type \"%s\" restriction(s) for %s type.",
+ tctx->tpdf->name, ly_data_type2str[basetype]);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if ((basetype == LY_TYPE_EMPTY) && tctx->tpdf->dflt.str) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Invalid type \"%s\" - \"empty\" type must not have a default value (%s).",
+ tctx->tpdf->name, tctx->tpdf->dflt.str);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ (*type)->basetype = basetype;
+ (*type)->plugin = plugin;
+
+ /* collect extensions */
+ COMPILE_EXTS_GOTO(ctx, tctx->tpdf->type.exts, (*type)->exts, (*type), ret, cleanup);
+
+ /* compile the new typedef */
+ prev_type = *type;
+ ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->tpdf->name,
+ &((struct lysp_tpdf *)tctx->tpdf)->type, basetype, tctx->tpdf->name, base, type);
+ LY_CHECK_GOTO(ret, cleanup);
+ base = prev_type;
+ }
+ /* remove the processed typedef contexts from the stack for circular check */
+ ctx->tpdf_chain.count = ctx->tpdf_chain.count - tpdf_chain.count;
+
+ /* process the type definition in leaf */
+ if (type_p->flags || !base || (basetype == LY_TYPE_LEAFREF)) {
+ /* get restrictions from the node itself */
+ (*type)->basetype = basetype;
+ (*type)->plugin = base ? base->plugin : lyplg_type_plugin_find("", NULL, ly_data_type2str[basetype]);
+ LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ ret = lys_compile_type_(ctx, context_pnode, context_flags, context_name, (struct lysp_type *)type_p, basetype,
+ NULL, base, type);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else if ((basetype != LY_TYPE_BOOL) && (basetype != LY_TYPE_EMPTY)) {
+ /* no specific restriction in leaf's type definition, copy from the base */
+ free(*type);
+ (*type) = base;
+ LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ }
+
+ COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, (*type), ret, cleanup);
+
+cleanup:
+ ly_set_erase(&tpdf_chain, free);
+ return ret;
+}
+
+/**
+ * @brief Check uniqness of the node/action/notification name.
+ *
+ * Data nodes, actions/RPCs and Notifications are stored separately (in distinguish lists) in the schema
+ * structures, but they share the namespace so we need to check their name collisions.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] parent Parent of the nodes to check, can be NULL.
+ * @param[in] name Name of the item to find in the given lists.
+ * @param[in] exclude Node that was just added that should be excluded from the name checking.
+ * @return LY_SUCCESS in case of unique name, LY_EEXIST otherwise.
+ */
+static LY_ERR
+lys_compile_node_uniqness(struct lysc_ctx *ctx, const struct lysc_node *parent, const char *name,
+ const struct lysc_node *exclude)
+{
+ const struct lysc_node *iter, *iter2;
+ const struct lysc_node_action *actions;
+ const struct lysc_node_notif *notifs;
+ uint32_t getnext_flags;
+ struct ly_set parent_choices = {0};
+
+#define CHECK_NODE(iter, exclude, name) (iter != (void *)exclude && (iter)->module == exclude->module && !strcmp(name, (iter)->name))
+
+ if (exclude->nodetype == LYS_CASE) {
+ /* check restricted only to all the cases */
+ assert(parent->nodetype == LYS_CHOICE);
+ LY_LIST_FOR(lysc_node_child(parent), iter) {
+ if (CHECK_NODE(iter, exclude, name)) {
+ LOGVAL(ctx->ctx, LY_VCODE_DUPIDENT, name, "case");
+ return LY_EEXIST;
+ }
+ }
+
+ return LY_SUCCESS;
+ }
+
+ /* no reason for our parent to be choice anymore */
+ assert(!parent || (parent->nodetype != LYS_CHOICE));
+
+ if (parent && (parent->nodetype == LYS_CASE)) {
+ /* move to the first data definition parent */
+
+ /* but remember the choice nodes on the parents path to avoid believe they collide with our node */
+ iter = lysc_data_parent(parent);
+ do {
+ parent = parent->parent;
+ if (parent && (parent->nodetype == LYS_CHOICE)) {
+ ly_set_add(&parent_choices, (void *)parent, 1, NULL);
+ }
+ } while (parent != iter);
+ }
+
+ getnext_flags = LYS_GETNEXT_WITHCHOICE;
+ if (parent && (parent->nodetype & (LYS_RPC | LYS_ACTION))) {
+ /* move to the inout to avoid traversing a not-filled-yet (the other) node */
+ if (exclude->flags & LYS_IS_OUTPUT) {
+ getnext_flags |= LYS_GETNEXT_OUTPUT;
+ parent = lysc_node_child(parent)->next;
+ } else {
+ parent = lysc_node_child(parent);
+ }
+ }
+
+ iter = NULL;
+ if (!parent && ctx->ext) {
+ while ((iter = lys_getnext_ext(iter, parent, ctx->ext, getnext_flags))) {
+ if (!ly_set_contains(&parent_choices, (void *)iter, NULL) && CHECK_NODE(iter, exclude, name)) {
+ goto error;
+ }
+
+ /* we must compare with both the choice and all its nested data-definiition nodes (but not recursively) */
+ if (iter->nodetype == LYS_CHOICE) {
+ iter2 = NULL;
+ while ((iter2 = lys_getnext_ext(iter2, iter, NULL, 0))) {
+ if (CHECK_NODE(iter2, exclude, name)) {
+ goto error;
+ }
+ }
+ }
+ }
+ } else {
+ while ((iter = lys_getnext(iter, parent, ctx->cur_mod->compiled, getnext_flags))) {
+ if (!ly_set_contains(&parent_choices, (void *)iter, NULL) && CHECK_NODE(iter, exclude, name)) {
+ goto error;
+ }
+
+ /* we must compare with both the choice and all its nested data-definiition nodes (but not recursively) */
+ if (iter->nodetype == LYS_CHOICE) {
+ iter2 = NULL;
+ while ((iter2 = lys_getnext(iter2, iter, NULL, 0))) {
+ if (CHECK_NODE(iter2, exclude, name)) {
+ goto error;
+ }
+ }
+ }
+ }
+
+ actions = parent ? lysc_node_actions(parent) : ctx->cur_mod->compiled->rpcs;
+ LY_LIST_FOR((struct lysc_node *)actions, iter) {
+ if (CHECK_NODE(iter, exclude, name)) {
+ goto error;
+ }
+ }
+
+ notifs = parent ? lysc_node_notifs(parent) : ctx->cur_mod->compiled->notifs;
+ LY_LIST_FOR((struct lysc_node *)notifs, iter) {
+ if (CHECK_NODE(iter, exclude, name)) {
+ goto error;
+ }
+ }
+ }
+ ly_set_erase(&parent_choices, NULL);
+ return LY_SUCCESS;
+
+error:
+ ly_set_erase(&parent_choices, NULL);
+ LOGVAL(ctx->ctx, LY_VCODE_DUPIDENT, name, "data definition/RPC/action/notification");
+ return LY_EEXIST;
+
+#undef CHECK_NODE
+}
+
+LY_ERR
+lys_compile_node_connect(struct lysc_ctx *ctx, struct lysc_node *parent, struct lysc_node *node)
+{
+ struct lysc_node **children, *anchor = NULL;
+ int insert_after = 0;
+
+ node->parent = parent;
+
+ if (parent) {
+ if (node->nodetype == LYS_INPUT) {
+ assert(parent->nodetype & (LYS_ACTION | LYS_RPC));
+ /* input node is part of the action but link it with output */
+ node->next = &((struct lysc_node_action *)parent)->output.node;
+ node->prev = node->next;
+ return LY_SUCCESS;
+ } else if (node->nodetype == LYS_OUTPUT) {
+ /* output node is part of the action but link it with input */
+ node->next = NULL;
+ node->prev = &((struct lysc_node_action *)parent)->input.node;
+ return LY_SUCCESS;
+ } else if (node->nodetype == LYS_ACTION) {
+ children = (struct lysc_node **)lysc_node_actions_p(parent);
+ } else if (node->nodetype == LYS_NOTIF) {
+ children = (struct lysc_node **)lysc_node_notifs_p(parent);
+ } else {
+ children = lysc_node_child_p(parent);
+ }
+ assert(children);
+
+ if (!(*children)) {
+ /* first child */
+ *children = node;
+ } else if (node->flags & LYS_KEY) {
+ /* special handling of adding keys */
+ assert(node->module == parent->module);
+ anchor = *children;
+ if (anchor->flags & LYS_KEY) {
+ while ((anchor->flags & LYS_KEY) && anchor->next) {
+ anchor = anchor->next;
+ }
+ /* insert after the last key */
+ insert_after = 1;
+ } /* else insert before anchor (at the beginning) */
+ } else if ((*children)->prev->module == node->module) {
+ /* last child is from the same module, keep the order and insert at the end */
+ anchor = (*children)->prev;
+ insert_after = 1;
+ } else if (parent->module == node->module) {
+ /* adding module child after some augments were connected */
+ for (anchor = *children; anchor->module == node->module; anchor = anchor->next) {}
+ } else {
+ /* some augments are already connected and we are connecting new ones,
+ * keep module name order and insert the node into the children list */
+ anchor = *children;
+ do {
+ anchor = anchor->prev;
+
+ /* check that we have not found the last augment node from our module or
+ * the first augment node from a "smaller" module or
+ * the first node from a local module */
+ if ((anchor->module == node->module) || (strcmp(anchor->module->name, node->module->name) < 0) ||
+ (anchor->module == parent->module)) {
+ /* insert after */
+ insert_after = 1;
+ break;
+ }
+
+ /* we have traversed all the nodes, insert before anchor (as the first node) */
+ } while (anchor->prev->next);
+ }
+
+ /* insert */
+ if (anchor) {
+ if (insert_after) {
+ node->next = anchor->next;
+ node->prev = anchor;
+ anchor->next = node;
+ if (node->next) {
+ /* middle node */
+ node->next->prev = node;
+ } else {
+ /* last node */
+ (*children)->prev = node;
+ }
+ } else {
+ node->next = anchor;
+ node->prev = anchor->prev;
+ anchor->prev = node;
+ if (anchor == *children) {
+ /* first node */
+ *children = node;
+ } else {
+ /* middle node */
+ node->prev->next = node;
+ }
+ }
+ }
+
+ /* check the name uniqueness (even for an only child, it may be in case) */
+ if (lys_compile_node_uniqness(ctx, parent, node->name, node)) {
+ return LY_EEXIST;
+ }
+ } else {
+ /* top-level element */
+ struct lysc_node **list;
+
+ if (ctx->ext) {
+ lyplg_ext_get_storage_p(ctx->ext, LY_STMT_DATA_NODE_MASK, (const void ***)&list);
+ } else if (node->nodetype == LYS_RPC) {
+ list = (struct lysc_node **)&ctx->cur_mod->compiled->rpcs;
+ } else if (node->nodetype == LYS_NOTIF) {
+ list = (struct lysc_node **)&ctx->cur_mod->compiled->notifs;
+ } else {
+ list = &ctx->cur_mod->compiled->data;
+ }
+ if (!(*list)) {
+ *list = node;
+ } else {
+ /* insert at the end of the module's top-level nodes list */
+ (*list)->prev->next = node;
+ node->prev = (*list)->prev;
+ (*list)->prev = node;
+ }
+
+ /* check the name uniqueness on top-level */
+ if (lys_compile_node_uniqness(ctx, NULL, node->name, node)) {
+ return LY_EEXIST;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Set config and operation flags for a node.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Compiled node flags to set.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_config(struct lysc_ctx *ctx, struct lysc_node *node)
+{
+ /* case never has any explicit config */
+ assert((node->nodetype != LYS_CASE) || !(node->flags & LYS_CONFIG_MASK));
+
+ if (ctx->compile_opts & LYS_COMPILE_NO_CONFIG) {
+ /* ignore config statements inside Notification/RPC/action/... data */
+ node->flags &= ~LYS_CONFIG_MASK;
+ } else if (!(node->flags & LYS_CONFIG_MASK)) {
+ /* config not explicitly set, inherit it from parent */
+ assert(!node->parent || (node->parent->flags & LYS_CONFIG_MASK) || (node->parent->nodetype & LYS_AUGMENT));
+ if (node->parent && (node->parent->flags & LYS_CONFIG_MASK)) {
+ node->flags |= node->parent->flags & LYS_CONFIG_MASK;
+ } else {
+ /* default is config true */
+ node->flags |= LYS_CONFIG_W;
+ }
+ } else {
+ /* config set explicitly */
+ node->flags |= LYS_SET_CONFIG;
+ }
+
+ if (node->parent && (node->parent->flags & LYS_CONFIG_R) && (node->flags & LYS_CONFIG_W)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Configuration node cannot be child of any state data node.");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Set various flags of the compiled nodes
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] parsed_flags Parsed node flags.
+ * @param[in] inherited_flags Inherited flags from a schema-only statement.
+ * @param[in,out] node Compiled node where the flags will be set.
+ */
+static LY_ERR
+lys_compile_node_flags(struct lysc_ctx *ctx, uint16_t parsed_flags, uint16_t inherited_flags, struct lysc_node *node)
+{
+ uint16_t parent_flags;
+ const char *parent_name;
+
+ /* copy flags except for status */
+ node->flags = (parsed_flags & LYS_FLAGS_COMPILED_MASK) & ~LYS_STATUS_MASK;
+
+ /* inherit config flags */
+ LY_CHECK_RET(lys_compile_config(ctx, node));
+
+ /* compile status */
+ parent_flags = node->parent ? node->parent->flags : 0;
+ parent_name = node->parent ? node->parent->name : NULL;
+ LY_CHECK_RET(lys_compile_status(ctx, parsed_flags, inherited_flags, parent_flags, parent_name, node->name, &node->flags));
+
+ /* other flags */
+ if ((ctx->compile_opts & LYS_IS_INPUT) && (node->nodetype != LYS_INPUT)) {
+ node->flags |= LYS_IS_INPUT;
+ } else if ((ctx->compile_opts & LYS_IS_OUTPUT) && (node->nodetype != LYS_OUTPUT)) {
+ node->flags |= LYS_IS_OUTPUT;
+ } else if ((ctx->compile_opts & LYS_IS_NOTIF) && (node->nodetype != LYS_NOTIF)) {
+ node->flags |= LYS_IS_NOTIF;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lys_compile_node_(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *parent, uint16_t inherited_flags,
+ LY_ERR (*node_compile_spec)(struct lysc_ctx *, struct lysp_node *, struct lysc_node *),
+ struct lysc_node *node, struct ly_set *child_set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ ly_bool not_supported, enabled;
+ struct lysp_node *dev_pnode = NULL;
+ struct lysp_when *pwhen = NULL;
+ uint32_t prev_opts = ctx->compile_opts;
+
+ node->nodetype = pnode->nodetype;
+ node->module = ctx->cur_mod;
+ node->parent = parent;
+ node->prev = node;
+ node->priv = ctx->ctx->flags & LY_CTX_SET_PRIV_PARSED ? pnode : NULL;
+
+ /* compile any deviations for this node */
+ LY_CHECK_GOTO(ret = lys_compile_node_deviations_refines(ctx, pnode, parent, &dev_pnode, &not_supported), error);
+ if (not_supported && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ /* if not supported, keep it just like disabled nodes by if-feature */
+ ly_set_add(&ctx->unres->disabled, node, 1, NULL);
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ }
+ if (dev_pnode) {
+ pnode = dev_pnode;
+ }
+
+ DUP_STRING_GOTO(ctx->ctx, pnode->name, node->name, ret, error);
+ DUP_STRING_GOTO(ctx->ctx, pnode->dsc, node->dsc, ret, error);
+ DUP_STRING_GOTO(ctx->ctx, pnode->ref, node->ref, ret, error);
+
+ /* if-features */
+ LY_CHECK_GOTO(ret = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), error);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ ly_set_add(&ctx->unres->disabled, node, 1, NULL);
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ }
+
+ /* config, status and other flags */
+ LY_CHECK_GOTO(ret = lys_compile_node_flags(ctx, pnode->flags, inherited_flags, node), error);
+
+ /* list ordering */
+ if (node->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ if ((node->flags & (LYS_CONFIG_R | LYS_IS_OUTPUT | LYS_IS_NOTIF)) && (node->flags & LYS_ORDBY_MASK)) {
+ LOGVRB("The ordered-by statement is ignored in lists representing %s (%s).",
+ (node->flags & LYS_IS_OUTPUT) ? "RPC/action output parameters" :
+ (ctx->compile_opts & LYS_IS_NOTIF) ? "notification content" : "state data", ctx->path);
+ node->flags &= ~LYS_ORDBY_MASK;
+ node->flags |= LYS_ORDBY_SYSTEM;
+ } else if (!(node->flags & LYS_ORDBY_MASK)) {
+ /* default ordering is system */
+ node->flags |= LYS_ORDBY_SYSTEM;
+ }
+ }
+
+ /* insert into parent's children/compiled module (we can no longer free the node separately on error) */
+ LY_CHECK_GOTO(ret = lys_compile_node_connect(ctx, parent, node), cleanup);
+
+ if ((pwhen = lysp_node_when(pnode))) {
+ /* compile when */
+ ret = lys_compile_when(ctx, pwhen, pnode->flags, node, lysc_data_node(node), node, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* nodetype-specific part */
+ LY_CHECK_GOTO(ret = node_compile_spec(ctx, pnode, node), cleanup);
+
+ /* final compilation tasks that require the node to be connected */
+ COMPILE_EXTS_GOTO(ctx, pnode->exts, node->exts, node, ret, cleanup);
+ if (node->flags & LYS_MAND_TRUE) {
+ /* inherit LYS_MAND_TRUE in parent containers */
+ lys_compile_mandatory_parents(parent, 1);
+ }
+
+ if (child_set) {
+ /* add the new node into set */
+ LY_CHECK_GOTO(ret = ly_set_add(child_set, node, 1, NULL), cleanup);
+ }
+
+ goto cleanup;
+
+error:
+ lysc_node_free(&ctx->free_ctx, node, 0);
+
+cleanup:
+ if (ret && dev_pnode) {
+ LOGVAL(ctx->ctx, LYVE_OTHER, "Compilation of a deviated and/or refined node failed.");
+ }
+ ctx->compile_opts = prev_opts;
+ lysp_dev_node_free(ctx, dev_pnode);
+ return ret;
+}
+
+LY_ERR
+lys_compile_node_action_inout(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node *child_p;
+ uint32_t prev_options = ctx->compile_opts;
+
+ struct lysp_node_action_inout *inout_p = (struct lysp_node_action_inout *)pnode;
+ struct lysc_node_action_inout *inout = (struct lysc_node_action_inout *)node;
+
+ COMPILE_ARRAY_GOTO(ctx, inout_p->musts, inout->musts, lys_compile_must, ret, done);
+ COMPILE_EXTS_GOTO(ctx, inout_p->exts, inout->exts, inout, ret, done);
+ ctx->compile_opts |= (inout_p->nodetype == LYS_INPUT) ? LYS_COMPILE_RPC_INPUT : LYS_COMPILE_RPC_OUTPUT;
+
+ LY_LIST_FOR(inout_p->child, child_p) {
+ LY_CHECK_GOTO(ret = lys_compile_node(ctx, child_p, node, 0, NULL), done);
+ }
+
+ /* connect any augments */
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, node), done);
+
+ ctx->compile_opts = prev_options;
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed action node information.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed action node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the action-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_action(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ LY_ERR ret;
+ struct lysp_node_action *action_p = (struct lysp_node_action *)pnode;
+ struct lysc_node_action *action = (struct lysc_node_action *)node;
+ struct lysp_node_action_inout *input, implicit_input = {
+ .nodetype = LYS_INPUT,
+ .name = "input",
+ .parent = pnode,
+ };
+ struct lysp_node_action_inout *output, implicit_output = {
+ .nodetype = LYS_OUTPUT,
+ .name = "output",
+ .parent = pnode,
+ };
+
+ /* input */
+ lysc_update_path(ctx, action->module, "input");
+ if (action_p->input.nodetype == LYS_UNKNOWN) {
+ input = &implicit_input;
+ } else {
+ input = &action_p->input;
+ }
+ ret = lys_compile_node_(ctx, &input->node, &action->node, 0, lys_compile_node_action_inout, &action->input.node, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ LY_CHECK_GOTO(ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, &action->input.node, &input->node);
+ LY_CHECK_GOTO(ret, done);
+
+ /* output */
+ lysc_update_path(ctx, action->module, "output");
+ if (action_p->output.nodetype == LYS_UNKNOWN) {
+ output = &implicit_output;
+ } else {
+ output = &action_p->output;
+ }
+ ret = lys_compile_node_(ctx, &output->node, &action->node, 0, lys_compile_node_action_inout, &action->output.node, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ LY_CHECK_GOTO(ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, &action->output.node, &output->node);
+ LY_CHECK_GOTO(ret, done);
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed action node information.
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed action node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the action-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_notif(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node_notif *notif_p = (struct lysp_node_notif *)pnode;
+ struct lysc_node_notif *notif = (struct lysc_node_notif *)node;
+ struct lysp_node *child_p;
+
+ COMPILE_ARRAY_GOTO(ctx, notif_p->musts, notif->musts, lys_compile_must, ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, node, pnode);
+ LY_CHECK_GOTO(ret, done);
+
+ LY_LIST_FOR(notif_p->child, child_p) {
+ ret = lys_compile_node(ctx, child_p, node, 0, NULL);
+ LY_CHECK_GOTO(ret, done);
+ }
+
+ /* connect any augments */
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, node), done);
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed container node information.
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed container node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the container-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_container(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ struct lysp_node_container *cont_p = (struct lysp_node_container *)pnode;
+ struct lysc_node_container *cont = (struct lysc_node_container *)node;
+ struct lysp_node *child_p;
+ LY_ERR ret = LY_SUCCESS;
+
+ if (cont_p->presence) {
+ /* presence container */
+ cont->flags |= LYS_PRESENCE;
+ }
+
+ /* more cases when the container has meaning but is kept NP for convenience:
+ * - when condition
+ * - direct child action/notification
+ */
+
+ LY_LIST_FOR(cont_p->child, child_p) {
+ ret = lys_compile_node(ctx, child_p, node, 0, NULL);
+ LY_CHECK_GOTO(ret, done);
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, lys_compile_must, ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, node, pnode);
+ LY_CHECK_GOTO(ret, done);
+
+ /* connect any augments */
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, node), done);
+
+ LY_LIST_FOR((struct lysp_node *)cont_p->actions, child_p) {
+ ret = lys_compile_node(ctx, child_p, node, 0, NULL);
+ LY_CHECK_GOTO(ret, done);
+ }
+ LY_LIST_FOR((struct lysp_node *)cont_p->notifs, child_p) {
+ ret = lys_compile_node(ctx, child_p, node, 0, NULL);
+ LY_CHECK_GOTO(ret, done);
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile type in leaf/leaf-list node and do all the necessary checks.
+ * @param[in] ctx Compile context.
+ * @param[in] context_node Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in,out] leaf Compiled leaf structure (possibly cast leaf-list) to provide node information and to store the compiled type information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_node_type(struct lysc_ctx *ctx, struct lysp_node *context_node, struct lysp_type *type_p,
+ struct lysc_node_leaf *leaf)
+{
+ struct lysp_qname *dflt;
+ struct lysc_type **t;
+ LY_ARRAY_COUNT_TYPE u, count;
+ ly_bool in_unres = 0;
+
+ LY_CHECK_RET(lys_compile_type(ctx, context_node, leaf->flags, leaf->name, type_p, &leaf->type,
+ leaf->units ? NULL : &leaf->units, &dflt));
+
+ /* store default value, if any */
+ if (dflt && !(leaf->flags & LYS_SET_DFLT)) {
+ LY_CHECK_RET(lysc_unres_leaf_dflt_add(ctx, leaf, dflt));
+ }
+
+ /* store leafref(s) to be resolved */
+ LY_CHECK_RET(lysc_unres_leafref_add(ctx, leaf, type_p->pmod));
+
+ /* type-specific checks */
+ if (leaf->type->basetype == LY_TYPE_UNION) {
+ t = ((struct lysc_type_union *)leaf->type)->types;
+ count = LY_ARRAY_COUNT(t);
+ } else {
+ t = &leaf->type;
+ count = 1;
+ }
+ for (u = 0; u < count; ++u) {
+ if (t[u]->basetype == LY_TYPE_EMPTY) {
+ if ((leaf->nodetype == LYS_LEAFLIST) && (ctx->pmod->version < LYS_VERSION_1_1)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
+ return LY_EVALID;
+ }
+ } else if (!in_unres && ((t[u]->basetype == LY_TYPE_BITS) || (t[u]->basetype == LY_TYPE_ENUM))) {
+ /* store in unres for all disabled bits/enums to be removed */
+ LY_CHECK_RET(lysc_unres_bitenum_add(ctx, leaf));
+ in_unres = 1;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile parsed leaf node information.
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed leaf node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the leaf-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ struct lysp_node_leaf *leaf_p = (struct lysp_node_leaf *)pnode;
+ struct lysc_node_leaf *leaf = (struct lysc_node_leaf *)node;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, lys_compile_must, ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, node, pnode);
+ LY_CHECK_GOTO(ret, done);
+
+ if (leaf_p->units) {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->ctx, leaf_p->units, 0, &leaf->units), done);
+ leaf->flags |= LYS_SET_UNITS;
+ }
+
+ /* compile type */
+ ret = lys_compile_node_type(ctx, pnode, &leaf_p->type, leaf);
+ LY_CHECK_GOTO(ret, done);
+
+ /* store/update default value */
+ if (leaf_p->dflt.str) {
+ LY_CHECK_RET(lysc_unres_leaf_dflt_add(ctx, leaf, &leaf_p->dflt));
+ leaf->flags |= LYS_SET_DFLT;
+ }
+
+ /* checks */
+ if ((leaf->flags & LYS_SET_DFLT) && (leaf->flags & LYS_MAND_TRUE)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid mandatory leaf with a default value.");
+ return LY_EVALID;
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed leaf-list node information.
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed leaf-list node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the leaf-list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_leaflist(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ struct lysp_node_leaflist *llist_p = (struct lysp_node_leaflist *)pnode;
+ struct lysc_node_leaflist *llist = (struct lysc_node_leaflist *)node;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, lys_compile_must, ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, node, pnode);
+ LY_CHECK_GOTO(ret, done);
+
+ if (llist_p->units) {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx->ctx, llist_p->units, 0, &llist->units), done);
+ llist->flags |= LYS_SET_UNITS;
+ }
+
+ /* compile type */
+ ret = lys_compile_node_type(ctx, pnode, &llist_p->type, (struct lysc_node_leaf *)llist);
+ LY_CHECK_GOTO(ret, done);
+
+ /* store/update default values */
+ if (llist_p->dflts) {
+ if (ctx->pmod->version < LYS_VERSION_1_1) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Leaf-list default values are allowed only in YANG 1.1 modules.");
+ return LY_EVALID;
+ }
+
+ LY_CHECK_GOTO(lysc_unres_llist_dflts_add(ctx, llist, llist_p->dflts), done);
+ llist->flags |= LYS_SET_DFLT;
+ }
+
+ llist->min = llist_p->min;
+ if (llist->min) {
+ llist->flags |= LYS_MAND_TRUE;
+ }
+ llist->max = llist_p->max ? llist_p->max : UINT32_MAX;
+
+ if (llist->flags & LYS_CONFIG_R) {
+ /* state leaf-list is always ordered-by user */
+ llist->flags &= ~LYS_ORDBY_SYSTEM;
+ llist->flags |= LYS_ORDBY_USER;
+ }
+
+ /* checks */
+ if ((llist->flags & LYS_SET_DFLT) && (llist->flags & LYS_MAND_TRUE)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "The default statement is present on leaf-list with a nonzero min-elements.");
+ return LY_EVALID;
+ }
+
+ if (llist->min > llist->max) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Leaf-list min-elements %u is bigger than max-elements %u.",
+ llist->min, llist->max);
+ return LY_EVALID;
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Find the node according to the given descendant/absolute schema nodeid.
+ * Used in unique, refine and augment statements.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] nodeid Descendant-schema-nodeid (according to the YANG grammar)
+ * @param[in] nodeid_len Length of the given nodeid, if it is not NULL-terminated string.
+ * @param[in] ctx_node Context node for a relative nodeid.
+ * @param[in] format Format of any prefixes.
+ * @param[in] prefix_data Format-specific prefix data (see ::ly_resolve_prefix).
+ * @param[in] nodetype Optional (can be 0) restriction for target's nodetype. If target exists, but does not match
+ * the given nodetype, LY_EDENIED is returned (and target is provided), but no error message is printed.
+ * The value can be even an ORed value to allow multiple nodetypes.
+ * @param[out] target Found target node if any.
+ * @param[out] result_flag Output parameter to announce if the schema nodeid goes through the action's input/output or a Notification.
+ * The LYSC_OPT_RPC_INPUT, LYSC_OPT_RPC_OUTPUT and LYSC_OPT_NOTIFICATION are used as flags.
+ * @return LY_ERR values - LY_ENOTFOUND, LY_EVALID, LY_EDENIED or LY_SUCCESS.
+ */
+static LY_ERR
+lysc_resolve_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *ctx_node,
+ LY_VALUE_FORMAT format, void *prefix_data, uint16_t nodetype, const struct lysc_node **target, uint16_t *result_flag)
+{
+ LY_ERR ret = LY_EVALID;
+ const char *name, *prefix, *id;
+ size_t name_len, prefix_len;
+ const struct lys_module *mod = NULL;
+ const char *nodeid_type;
+ uint32_t getnext_extra_flag = 0;
+ uint16_t current_nodetype = 0;
+
+ assert(nodeid);
+ assert(target);
+ assert(result_flag);
+ *target = NULL;
+ *result_flag = 0;
+
+ id = nodeid;
+
+ if (ctx_node) {
+ /* descendant-schema-nodeid */
+ nodeid_type = "descendant";
+
+ if (*id == '/') {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid descendant-schema-nodeid value \"%.*s\" - absolute-schema-nodeid used.",
+ (int)(nodeid_len ? nodeid_len : strlen(nodeid)), nodeid);
+ return LY_EVALID;
+ }
+ } else {
+ /* absolute-schema-nodeid */
+ nodeid_type = "absolute";
+
+ if (*id != '/') {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid absolute-schema-nodeid value \"%.*s\" - missing starting \"/\".",
+ (int)(nodeid_len ? nodeid_len : strlen(nodeid)), nodeid);
+ return LY_EVALID;
+ }
+ ++id;
+ }
+
+ while (*id && (ret = ly_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len)) == LY_SUCCESS) {
+ if (prefix) {
+ mod = ly_resolve_prefix(ctx->ctx, prefix, prefix_len, format, prefix_data);
+ if (!mod) {
+ /* module must always be found */
+ assert(prefix);
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid %s-schema-nodeid value \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+ nodeid_type, (int)(id - nodeid), nodeid, (int)prefix_len, prefix, LYSP_MODULE_NAME(ctx->pmod));
+ return LY_ENOTFOUND;
+ }
+ } else {
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* use the current module */
+ mod = ctx->cur_mod;
+ break;
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ if (!ctx_node) {
+ LOGINT_RET(ctx->ctx);
+ }
+ /* inherit the module of the previous context node */
+ mod = ctx_node->module;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ /* not really defined */
+ LOGINT_RET(ctx->ctx);
+ }
+ }
+
+ if (ctx_node && (ctx_node->nodetype & (LYS_RPC | LYS_ACTION))) {
+ /* move through input/output manually */
+ if (mod != ctx_node->module) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid %s-schema-nodeid value \"%.*s\" - target node not found.",
+ nodeid_type, (int)(id - nodeid), nodeid);
+ return LY_ENOTFOUND;
+ }
+ if (!ly_strncmp("input", name, name_len)) {
+ ctx_node = &((struct lysc_node_action *)ctx_node)->input.node;
+ } else if (!ly_strncmp("output", name, name_len)) {
+ ctx_node = &((struct lysc_node_action *)ctx_node)->output.node;
+ getnext_extra_flag = LYS_GETNEXT_OUTPUT;
+ } else {
+ /* only input or output is valid */
+ ctx_node = NULL;
+ }
+ } else if (ctx->ext && !ctx_node) {
+ /* top-level extension nodes */
+ ctx_node = lysc_ext_find_node(ctx->ext, mod, name, name_len, 0, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
+ } else {
+ ctx_node = lys_find_child(ctx_node, mod, name, name_len, 0,
+ getnext_extra_flag | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
+ getnext_extra_flag = 0;
+ }
+ if (!ctx_node) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid %s-schema-nodeid value \"%.*s\" - target node not found.",
+ nodeid_type, (int)(id - nodeid), nodeid);
+ return LY_ENOTFOUND;
+ }
+ current_nodetype = ctx_node->nodetype;
+
+ if (current_nodetype == LYS_NOTIF) {
+ (*result_flag) |= LYS_COMPILE_NOTIFICATION;
+ } else if (current_nodetype == LYS_INPUT) {
+ (*result_flag) |= LYS_COMPILE_RPC_INPUT;
+ } else if (current_nodetype == LYS_OUTPUT) {
+ (*result_flag) |= LYS_COMPILE_RPC_OUTPUT;
+ }
+
+ if (!*id || (nodeid_len && ((size_t)(id - nodeid) >= nodeid_len))) {
+ break;
+ }
+ if (*id != '/') {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid %s-schema-nodeid value \"%.*s\" - missing \"/\" as node-identifier separator.",
+ nodeid_type, (int)(id - nodeid + 1), nodeid);
+ return LY_EVALID;
+ }
+ ++id;
+ }
+
+ if (ret == LY_SUCCESS) {
+ *target = ctx_node;
+ if (nodetype && !(current_nodetype & nodetype)) {
+ return LY_EDENIED;
+ }
+ } else {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid %s-schema-nodeid value \"%.*s\" - unexpected end of expression.",
+ nodeid_type, (int)(nodeid_len ? nodeid_len : strlen(nodeid)), nodeid);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Compile information about list's uniques.
+ * @param[in] ctx Compile context.
+ * @param[in] uniques Sized array list of unique statements.
+ * @param[in] list Compiled list where the uniques are supposed to be resolved and stored.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_node_list_unique(struct lysc_ctx *ctx, struct lysp_qname *uniques, struct lysc_node_list *list)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_node_leaf **key, ***unique;
+ struct lysc_node *parent;
+ const char *keystr, *delim;
+ size_t len;
+ LY_ARRAY_COUNT_TYPE v;
+ int8_t config; /* -1 - not yet seen; 0 - LYS_CONFIG_R; 1 - LYS_CONFIG_W */
+ uint16_t flags;
+
+ LY_ARRAY_FOR(uniques, v) {
+ config = -1;
+ LY_ARRAY_NEW_RET(ctx->ctx, list->uniques, unique, LY_EMEM);
+ keystr = uniques[v].str;
+ while (keystr) {
+ delim = strpbrk(keystr, " \t\n");
+ if (delim) {
+ len = delim - keystr;
+ while (isspace(*delim)) {
+ ++delim;
+ }
+ } else {
+ len = strlen(keystr);
+ }
+
+ /* unique node must be present */
+ LY_ARRAY_NEW_RET(ctx->ctx, *unique, key, LY_EMEM);
+ ret = lysc_resolve_schema_nodeid(ctx, keystr, len, &list->node, LY_VALUE_SCHEMA, (void *)uniques[v].mod,
+ LYS_LEAF, (const struct lysc_node **)key, &flags);
+ if (ret != LY_SUCCESS) {
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Unique's descendant-schema-nodeid \"%.*s\" refers to %s node instead of a leaf.",
+ (int)len, keystr, lys_nodetype2str((*key)->nodetype));
+ }
+ return LY_EVALID;
+ } else if (flags) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Unique's descendant-schema-nodeid \"%.*s\" refers into %s node.",
+ (int)len, keystr, flags & LYS_IS_NOTIF ? "notification" : "RPC/action");
+ return LY_EVALID;
+ }
+
+ /* all referenced leafs must be of the same config type */
+ if ((config != -1) && ((((*key)->flags & LYS_CONFIG_W) && (config == 0)) ||
+ (((*key)->flags & LYS_CONFIG_R) && (config == 1)))) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Unique statement \"%s\" refers to leaves with different config type.", uniques[v].str);
+ return LY_EVALID;
+ } else if ((*key)->flags & LYS_CONFIG_W) {
+ config = 1;
+ } else { /* LYS_CONFIG_R */
+ config = 0;
+ }
+
+ /* we forbid referencing nested lists because it is unspecified what instance of such a list to use */
+ for (parent = (*key)->parent; parent != (struct lysc_node *)list; parent = parent->parent) {
+ if (parent->nodetype == LYS_LIST) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Unique statement \"%s\" refers to a leaf in nested list \"%s\".", uniques[v].str, parent->name);
+ return LY_EVALID;
+ }
+ }
+
+ /* check status */
+ LY_CHECK_RET(lysc_check_status(ctx, list->flags, uniques[v].mod->mod, list->name,
+ (*key)->flags, (*key)->module, (*key)->name));
+
+ /* mark leaf as unique */
+ (*key)->flags |= LYS_UNIQUE;
+
+ /* next unique value in line */
+ keystr = delim;
+ }
+ /* next unique definition */
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile parsed list node information.
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed list node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ struct lysp_node_list *list_p = (struct lysp_node_list *)pnode;
+ struct lysc_node_list *list = (struct lysc_node_list *)node;
+ struct lysp_node *child_p;
+ struct lysc_node *parent;
+ struct lysc_node_leaf *key, *prev_key = NULL;
+ size_t len;
+ const char *keystr, *delim;
+ LY_ERR ret = LY_SUCCESS;
+
+ list->min = list_p->min;
+ if (list->min) {
+ list->flags |= LYS_MAND_TRUE;
+ }
+ list->max = list_p->max ? list_p->max : (uint32_t)-1;
+
+ LY_LIST_FOR(list_p->child, child_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, child_p, node, 0, NULL));
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, lys_compile_must, ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, node, pnode);
+ LY_CHECK_GOTO(ret, done);
+
+ /* keys */
+ if (list->flags & LYS_CONFIG_W) {
+ parent = node;
+ if (ctx->compile_opts & LYS_COMPILE_GROUPING) {
+ /* compiling individual grouping, we can check this only if there is an explicit config set */
+ while (parent) {
+ if (parent->flags & LYS_SET_CONFIG) {
+ break;
+ }
+ parent = parent->parent;
+ }
+ }
+
+ if (parent && (!list_p->key || !list_p->key[0])) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Missing key in list representing configuration data.");
+ return LY_EVALID;
+ }
+ }
+
+ /* find all the keys (must be direct children) */
+ keystr = list_p->key;
+ if (!keystr) {
+ /* keyless list */
+ list->flags &= ~LYS_ORDBY_SYSTEM;
+ list->flags |= LYS_KEYLESS | LYS_ORDBY_USER;
+ }
+ while (keystr) {
+ delim = strpbrk(keystr, " \t\n");
+ if (delim) {
+ len = delim - keystr;
+ while (isspace(*delim)) {
+ ++delim;
+ }
+ } else {
+ len = strlen(keystr);
+ }
+
+ /* key node must be present */
+ key = (struct lysc_node_leaf *)lys_find_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE);
+ if (!key) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "The list's key \"%.*s\" not found.", (int)len, keystr);
+ return LY_EVALID;
+ }
+ /* keys must be unique */
+ if (key->flags & LYS_KEY) {
+ /* the node was already marked as a key */
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Duplicated key identifier \"%.*s\".", (int)len, keystr);
+ return LY_EVALID;
+ }
+
+ lysc_update_path(ctx, list->module, key->name);
+ /* key must have the same config flag as the list itself */
+ if ((list->flags & LYS_CONFIG_MASK) != (key->flags & LYS_CONFIG_MASK)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Key of a configuration list must not be a state leaf.");
+ return LY_EVALID;
+ }
+ if (ctx->pmod->version < LYS_VERSION_1_1) {
+ /* YANG 1.0 denies key to be of empty type */
+ if (key->type->basetype == LY_TYPE_EMPTY) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "List's key cannot be of \"empty\" type until it is in YANG 1.1 module.");
+ return LY_EVALID;
+ }
+ } else {
+ /* when and if-feature are illegal on list keys */
+ if (key->when) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "List's key must not have any \"when\" statement.");
+ return LY_EVALID;
+ }
+ /* unable to check if-features but compilation would fail if disabled */
+ }
+
+ /* check status */
+ LY_CHECK_RET(lysc_check_status(ctx, list->flags, list->module, list->name, key->flags, key->module, key->name));
+
+ /* ignore default values of the key */
+ if (key->dflt) {
+ key->dflt->realtype->plugin->free(ctx->ctx, key->dflt);
+ lysc_type_free(&ctx->free_ctx, (struct lysc_type *)key->dflt->realtype);
+ free(key->dflt);
+ key->dflt = NULL;
+ }
+ /* mark leaf as key */
+ key->flags |= LYS_KEY;
+
+ /* move it to the correct position */
+ if ((prev_key && ((struct lysc_node *)prev_key != key->prev)) || (!prev_key && key->prev->next)) {
+ /* fix links in closest previous siblings of the key */
+ if (key->next) {
+ key->next->prev = key->prev;
+ } else {
+ /* last child */
+ list->child->prev = key->prev;
+ }
+ if (key->prev->next) {
+ key->prev->next = key->next;
+ }
+ /* fix links in the key */
+ if (prev_key) {
+ key->prev = &prev_key->node;
+ key->next = prev_key->next;
+ } else {
+ key->prev = list->child->prev;
+ key->next = list->child;
+ }
+ /* fix links in closes future siblings of the key */
+ if (prev_key) {
+ if (prev_key->next) {
+ prev_key->next->prev = &key->node;
+ } else {
+ list->child->prev = &key->node;
+ }
+ prev_key->next = &key->node;
+ } else {
+ list->child->prev = &key->node;
+ }
+ /* fix links in parent */
+ if (!key->prev->next) {
+ list->child = &key->node;
+ }
+ }
+
+ /* next key value */
+ prev_key = key;
+ keystr = delim;
+ lysc_update_path(ctx, NULL, NULL);
+ }
+
+ /* connect any augments */
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, node), done);
+
+ /* uniques */
+ if (list_p->uniques) {
+ LY_CHECK_RET(lys_compile_node_list_unique(ctx, list_p->uniques, list));
+ }
+
+ LY_LIST_FOR((struct lysp_node *)list_p->actions, child_p) {
+ ret = lys_compile_node(ctx, child_p, node, 0, NULL);
+ LY_CHECK_GOTO(ret, done);
+ }
+ LY_LIST_FOR((struct lysp_node *)list_p->notifs, child_p) {
+ ret = lys_compile_node(ctx, child_p, node, 0, NULL);
+ LY_CHECK_GOTO(ret, done);
+ }
+
+ /* checks */
+ if (list->min > list->max) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "List min-elements %u is bigger than max-elements %u.", list->min, list->max);
+ return LY_EVALID;
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Do some checks and set the default choice's case.
+ *
+ * Selects (and stores into ::lysc_node_choice#dflt) the default case and set LYS_SET_DFLT flag on it.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] dflt Name of the default branch. Can even contain a prefix.
+ * @param[in,out] ch The compiled choice node, its dflt member is filled to point to the default case node of the choice.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_node_choice_dflt(struct lysc_ctx *ctx, struct lysp_qname *dflt, struct lysc_node_choice *ch)
+{
+ struct lysc_node *iter;
+ const struct lys_module *mod;
+ const char *prefix = NULL, *name;
+ size_t prefix_len = 0;
+
+ /* could use lys_parse_nodeid(), but it checks syntax which is already done in this case by the parsers */
+ name = strchr(dflt->str, ':');
+ if (name) {
+ prefix = dflt->str;
+ prefix_len = name - prefix;
+ ++name;
+ } else {
+ name = dflt->str;
+ }
+ if (prefix) {
+ mod = ly_resolve_prefix(ctx->ctx, prefix, prefix_len, LY_VALUE_SCHEMA, (void *)dflt->mod);
+ if (!mod) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Default case prefix \"%.*s\" not found "
+ "in imports of \"%s\".", (int)prefix_len, prefix, LYSP_MODULE_NAME(dflt->mod));
+ return LY_EVALID;
+ }
+ } else {
+ mod = ch->module;
+ }
+
+ ch->dflt = (struct lysc_node_case *)lys_find_child(&ch->node, mod, name, 0, LYS_CASE, LYS_GETNEXT_WITHCASE);
+ if (!ch->dflt) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Default case \"%s\" not found.", dflt->str);
+ return LY_EVALID;
+ }
+
+ /* no mandatory nodes directly under the default case */
+ LY_LIST_FOR(ch->dflt->child, iter) {
+ if (iter->parent != (struct lysc_node *)ch->dflt) {
+ break;
+ }
+ if (iter->flags & LYS_MAND_TRUE) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Mandatory node \"%s\" under the default case \"%s\".", iter->name, dflt->str);
+ return LY_EVALID;
+ }
+ }
+
+ if (ch->flags & LYS_MAND_TRUE) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Invalid mandatory choice with a default case.");
+ return LY_EVALID;
+ }
+
+ ch->dflt->flags |= LYS_SET_DFLT;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_compile_node_choice_child(struct lysc_ctx *ctx, struct lysp_node *child_p, struct lysc_node *node,
+ struct ly_set *child_set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node *child_p_next = child_p->next;
+ struct lysp_node_case *cs_p;
+ struct lysc_node_case *cs_c;
+
+ if (child_p->nodetype == LYS_CASE) {
+ /* standard case under choice */
+ ret = lys_compile_node(ctx, child_p, node, 0, child_set);
+ } else {
+ /* we need the implicit case first, so create a fake parsed (shorthand) case */
+ cs_p = calloc(1, sizeof *cs_p);
+ LY_CHECK_ERR_RET(!cs_p, LOGMEM(ctx->ctx), LY_EMEM);
+ cs_p->nodetype = LYS_CASE;
+ DUP_STRING_GOTO(ctx->ctx, child_p->name, cs_p->name, ret, revert_sh_case);
+ cs_p->child = child_p;
+
+ /* make the child the only case child */
+ child_p->next = NULL;
+
+ /* compile it normally */
+ LY_CHECK_GOTO(ret = lys_compile_node(ctx, (struct lysp_node *)cs_p, node, 0, child_set), revert_sh_case);
+
+ if (((struct lysc_node_choice *)node)->cases) {
+ /* find our case node */
+ cs_c = (struct lysc_node_case *)((struct lysc_node_choice *)node)->cases;
+ while (cs_c->name != cs_p->name) {
+ cs_c = (struct lysc_node_case *)cs_c->next;
+ assert(cs_c);
+ }
+
+ if (ctx->ctx->flags & LY_CTX_SET_PRIV_PARSED) {
+ /* compiled case node cannot point to his corresponding parsed node
+ * because it exists temporarily so it must be set to NULL
+ */
+ assert(cs_c->priv == cs_p);
+ cs_c->priv = NULL;
+ }
+
+ /* status is copied from his child and not from his parent as usual. */
+ if (cs_c->child) {
+ cs_c->flags &= ~LYS_STATUS_MASK;
+ cs_c->flags |= (LYS_STATUS_MASK & cs_c->child->flags);
+ }
+ } /* else it was removed by a deviation */
+
+revert_sh_case:
+ /* free the parsed shorthand case and correct pointers back */
+ cs_p->child = NULL;
+ lysp_node_free(&ctx->free_ctx, (struct lysp_node *)cs_p);
+ child_p->next = child_p_next;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Compile parsed choice node information.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed choice node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the choice-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_choice(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ struct lysp_node_choice *ch_p = (struct lysp_node_choice *)pnode;
+ struct lysc_node_choice *ch = (struct lysc_node_choice *)node;
+ struct lysp_node *child_p;
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(node->nodetype == LYS_CHOICE);
+
+ LY_LIST_FOR(ch_p->child, child_p) {
+ LY_CHECK_GOTO(ret = lys_compile_node_choice_child(ctx, child_p, node, NULL), done);
+ }
+
+ /* connect any augments */
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, node), done);
+
+ /* default branch */
+ if (ch_p->dflt.str) {
+ LY_CHECK_GOTO(ret = lys_compile_node_choice_dflt(ctx, &ch_p->dflt, ch), done);
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed anydata or anyxml node information.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed anydata or anyxml node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the any-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_any(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ struct lysp_node_anydata *any_p = (struct lysp_node_anydata *)pnode;
+ struct lysc_node_anydata *any = (struct lysc_node_anydata *)node;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, lys_compile_must, ret, done);
+
+ /* add must(s) to unres */
+ ret = lysc_unres_must_add(ctx, node, pnode);
+ LY_CHECK_GOTO(ret, done);
+
+ if (any->flags & LYS_CONFIG_W) {
+ LOGVRB("Use of %s to define configuration data is not recommended. %s",
+ lyplg_ext_stmt2str(any->nodetype == LYS_ANYDATA ? LY_STMT_ANYDATA : LY_STMT_ANYXML), ctx->path);
+ }
+done:
+ return ret;
+}
+
+/**
+ * @brief Prepare the case structure in choice node for the new data node.
+ *
+ * It is able to handle implicit as well as explicit cases and the situation when the case has multiple data nodes and the case was already
+ * created in the choice when the first child was processed.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pnode Node image from the parsed tree. If the case is explicit, it is the LYS_CASE node, but in case of implicit case,
+ * it is the LYS_CHOICE, LYS_AUGMENT or LYS_GROUPING node.
+ * @param[in] ch The compiled choice structure where the new case structures are created (if needed).
+ * @param[in] child The new data node being part of a case (no matter if explicit or implicit).
+ * @return The case structure where the child node belongs to, NULL in case of error. Note that the child is not connected into the siblings list,
+ * it is linked from the case structure only in case it is its first child.
+ */
+static LY_ERR
+lys_compile_node_case(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node *child_p;
+ struct lysp_node_case *cs_p = (struct lysp_node_case *)pnode;
+
+ if (pnode->nodetype & (LYS_CHOICE | LYS_AUGMENT | LYS_GROUPING)) {
+ /* we have to add an implicit case node into the parent choice */
+ } else if (pnode->nodetype == LYS_CASE) {
+ /* explicit parent case */
+ LY_LIST_FOR(cs_p->child, child_p) {
+ LY_CHECK_GOTO(ret = lys_compile_node(ctx, child_p, node, 0, NULL), done);
+ }
+ } else {
+ LOGINT_RET(ctx->ctx);
+ }
+
+ /* connect any augments */
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, node), done);
+
+done:
+ return ret;
+}
+
+void
+lys_compile_mandatory_parents(struct lysc_node *parent, ly_bool add)
+{
+ const struct lysc_node *iter;
+
+ if (add) { /* set flag */
+ for ( ; parent && parent->nodetype == LYS_CONTAINER && !(parent->flags & LYS_MAND_TRUE) && !(parent->flags & LYS_PRESENCE);
+ parent = parent->parent) {
+ parent->flags |= LYS_MAND_TRUE;
+ }
+ } else { /* unset flag */
+ for ( ; parent && parent->nodetype == LYS_CONTAINER && (parent->flags & LYS_MAND_TRUE); parent = parent->parent) {
+ for (iter = lysc_node_child(parent); iter; iter = iter->next) {
+ if (iter->flags & LYS_MAND_TRUE) {
+ /* there is another mandatory node */
+ return;
+ }
+ }
+ /* unset mandatory flag - there is no mandatory children in the non-presence container */
+ parent->flags &= ~LYS_MAND_TRUE;
+ }
+ }
+}
+
+/**
+ * @brief Get the grouping with the specified name from given groupings sized array.
+ *
+ * @param[in] node Linked list of nodes with groupings.
+ * @param[in] name Name of the grouping to find,
+ * @return NULL when there is no grouping with the specified name
+ * @return Pointer to the grouping of the specified @p name.
+ */
+static struct lysp_node_grp *
+match_grouping(const struct lysp_node_grp *node, const char *name)
+{
+ LY_LIST_FOR(node, node) {
+ if ((node->nodetype == LYS_GROUPING) && !strcmp(node->name, name)) {
+ return (struct lysp_node_grp *)node;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find grouping for a uses.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] uses_p Parsed uses node.
+ * @param[out] gpr_p Found grouping on success.
+ * @param[out] grp_pmod Module of @p grp_p on success.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_uses_find_grouping(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, struct lysp_node_grp **grp_p,
+ struct lysp_module **grp_pmod)
+{
+ struct lysp_node *pnode;
+ struct lysp_node_grp *grp;
+ const struct lysp_node_grp *ext_grp;
+ LY_ARRAY_COUNT_TYPE u;
+ const char *id, *name, *prefix, *local_pref;
+ size_t prefix_len, name_len;
+ struct lysp_module *pmod, *found = NULL;
+ const struct lys_module *mod;
+
+ *grp_p = NULL;
+ *grp_pmod = NULL;
+
+ /* search for the grouping definition */
+ id = uses_p->name;
+ LY_CHECK_RET(ly_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len), LY_EVALID);
+ local_pref = ctx->pmod->is_submod ? ((struct lysp_submodule *)ctx->pmod)->prefix : ctx->pmod->mod->prefix;
+ if (!prefix || !ly_strncmp(local_pref, prefix, prefix_len)) {
+ /* current module, search local groupings first */
+ pmod = ctx->pmod->mod->parsed; /* make sure that we will start in main_module, not submodule */
+ for (pnode = uses_p->parent; !found && pnode; pnode = pnode->parent) {
+ if ((grp = match_grouping(lysp_node_groupings(pnode), name))) {
+ found = ctx->pmod;
+ break;
+ }
+ }
+
+ /* if in an extension, search possible groupings in it */
+ if (!found && ctx->ext) {
+ lyplg_ext_parsed_get_storage(ctx->ext, LY_STMT_GROUPING, sizeof ext_grp, (const void **)&ext_grp);
+ if ((grp = match_grouping(ext_grp, name))) {
+ found = ctx->pmod;
+ }
+ }
+ } else {
+ /* foreign module, find it first */
+ mod = ly_resolve_prefix(ctx->ctx, prefix, prefix_len, LY_VALUE_SCHEMA, ctx->pmod);
+ if (!mod) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid prefix used for grouping \"%s\" reference.", uses_p->name);
+ return LY_EVALID;
+ }
+ pmod = mod->parsed;
+ }
+
+ if (!found) {
+ /* search in top-level groupings of the main module ... */
+ if ((grp = match_grouping(pmod->groupings, name))) {
+ found = pmod;
+ } else {
+ /* ... and all the submodules */
+ LY_ARRAY_FOR(pmod->includes, u) {
+ if ((grp = match_grouping(pmod->includes[u].submodule->groupings, name))) {
+ found = (struct lysp_module *)pmod->includes[u].submodule;
+ break;
+ }
+ }
+ }
+ }
+ if (!found) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Grouping \"%s\" referenced by a uses statement not found.", uses_p->name);
+ return LY_EVALID;
+ }
+
+ if (!(ctx->compile_opts & LYS_COMPILE_GROUPING)) {
+ /* remember that the grouping is instantiated to avoid its standalone validation */
+ grp->flags |= LYS_USED_GRP;
+ }
+
+ *grp_p = grp;
+ *grp_pmod = found;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile uses grouping children.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] uses_p Parsed uses.
+ * @param[in] inherited_flags Inherited flags from the uses.
+ * @param[in] child First grouping child to compile.
+ * @param[in] grp_mod Grouping parsed module.
+ * @param[in] parent Uses compiled parent, may be NULL if top-level.
+ * @param[in,out] child_set Set of all compiled child nodes.
+ * @param[in] child_unres_disabled Whether the children are to be put into unres disabled set or not.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+static LY_ERR
+lys_compile_uses_children(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, uint16_t inherited_flags,
+ struct lysp_node *child, struct lysp_module *grp_mod, struct lysc_node *parent, struct ly_set *child_set,
+ ly_bool child_unres_disabled)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysp_module *mod_old = ctx->pmod;
+ uint32_t child_i, opt_prev = ctx->compile_opts;
+ ly_bool enabled;
+ struct lysp_node *pnode;
+ struct lysc_node *node;
+ struct lysc_when *when_shared = NULL;
+
+ assert(child_set);
+
+ child_i = child_set->count;
+ LY_LIST_FOR(child, pnode) {
+ /* compile the nodes with their parsed (grouping) module */
+ ctx->pmod = grp_mod;
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, parent, inherited_flags, child_set), cleanup);
+
+ /* eval if-features again for the rest of this node processing */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ }
+
+ /* restore the parsed module */
+ ctx->pmod = mod_old;
+
+ /* since the uses node is not present in the compiled tree, we need to pass some of its
+ * statements to all its children */
+ while (child_i < child_set->count) {
+ node = child_set->snodes[child_i];
+
+ if (uses_p->when) {
+ /* pass uses when to all the children */
+ rc = lys_compile_when(ctx, uses_p->when, inherited_flags, parent, lysc_data_node(parent), node, &when_shared);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ if (child_unres_disabled) {
+ /* child is disabled by the uses if-features */
+ ly_set_add(&ctx->unres->disabled, node, 1, NULL);
+ }
+
+ /* child processed */
+ ++child_i;
+ }
+
+ /* next iter */
+ ctx->compile_opts = opt_prev;
+ }
+
+cleanup:
+ ctx->compile_opts = opt_prev;
+ return rc;
+}
+
+/**
+ * @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
+ * If present, also apply uses's modificators.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] uses_p Parsed uses schema node.
+ * @param[in] parent Compiled parent node where the content of the referenced grouping is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @param[in] inherited_flags Inherited flags from a schema-only statement.
+ * @param[in] child_set Optional set of all the compiled children.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_uses(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, struct lysc_node *parent, uint16_t inherited_flags,
+ struct ly_set *child_set)
+{
+ LY_ERR rc = LY_SUCCESS;
+ ly_bool enabled, child_unres_disabled = 0;
+ uint32_t i, grp_stack_count, opt_prev = ctx->compile_opts;
+ struct lysp_node_grp *grp = NULL;
+ uint16_t uses_flags = 0;
+ struct lysp_module *grp_mod;
+ struct ly_set uses_child_set = {0};
+
+ /* find the referenced grouping */
+ LY_CHECK_RET(lys_compile_uses_find_grouping(ctx, uses_p, &grp, &grp_mod));
+
+ /* grouping must not reference themselves - stack in ctx maintains list of groupings currently being applied */
+ grp_stack_count = ctx->groupings.count;
+ LY_CHECK_RET(ly_set_add(&ctx->groupings, (void *)grp, 0, NULL));
+ if (grp_stack_count == ctx->groupings.count) {
+ /* the target grouping is already in the stack, so we are already inside it -> circular dependency */
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Grouping \"%s\" references itself through a uses statement.", grp->name);
+ return LY_EVALID;
+ }
+
+ /* nodetype checks */
+ if (grp->actions && (parent && !lysc_node_actions_p(parent))) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid child %s \"%s\" of uses parent %s \"%s\" node.",
+ grp->actions->name, lys_nodetype2str(grp->actions->nodetype),
+ parent->name, lys_nodetype2str(parent->nodetype));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if (grp->notifs && (parent && !lysc_node_notifs_p(parent))) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid child %s \"%s\" of uses parent %s \"%s\" node.",
+ grp->notifs->name, lys_nodetype2str(grp->notifs->nodetype),
+ parent->name, lys_nodetype2str(parent->nodetype));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* check status */
+ rc = lysc_check_status(ctx, uses_p->flags, ctx->pmod, uses_p->name, grp->flags, grp_mod, grp->name);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* compile any augments and refines so they can be applied during the grouping nodes compilation */
+ rc = lys_precompile_uses_augments_refines(ctx, uses_p, parent);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* compile special uses status flags */
+ rc = lys_compile_status(ctx, uses_p->flags, inherited_flags, parent ? parent->flags : 0,
+ parent ? parent->name : NULL, "<uses>", &uses_flags);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* uses if-features */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, uses_p->iffeatures, &enabled), cleanup);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ child_unres_disabled = 1;
+ }
+
+ /* uses grouping children */
+ rc = lys_compile_uses_children(ctx, uses_p, uses_flags, grp->child, grp_mod, parent,
+ child_set ? child_set : &uses_child_set, child_unres_disabled);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* uses grouping RPCs/actions */
+ rc = lys_compile_uses_children(ctx, uses_p, uses_flags, (struct lysp_node *)grp->actions, grp_mod, parent,
+ child_set ? child_set : &uses_child_set, child_unres_disabled);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* uses grouping notifications */
+ rc = lys_compile_uses_children(ctx, uses_p, uses_flags, (struct lysp_node *)grp->notifs, grp_mod, parent,
+ child_set ? child_set : &uses_child_set, child_unres_disabled);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* check that all augments were applied */
+ for (i = 0; i < ctx->uses_augs.count; ++i) {
+ if (((struct lysc_augment *)ctx->uses_augs.objs[i])->aug_p->parent != (struct lysp_node *)uses_p) {
+ /* augment of some parent uses, irrelevant now */
+ continue;
+ }
+
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Augment target node \"%s\" in grouping \"%s\" was not found.",
+ ((struct lysc_augment *)ctx->uses_augs.objs[i])->nodeid->expr, grp->name);
+ rc = LY_ENOTFOUND;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* check that all refines were applied */
+ for (i = 0; i < ctx->uses_rfns.count; ++i) {
+ if (((struct lysc_refine *)ctx->uses_rfns.objs[i])->uses_p != uses_p) {
+ /* refine of some parent uses, irrelevant now */
+ continue;
+ }
+
+ LOGVAL(ctx->ctx, LYVE_REFERENCE, "Refine(s) target node \"%s\" in grouping \"%s\" was not found.",
+ ((struct lysc_refine *)ctx->uses_rfns.objs[i])->nodeid->expr, grp->name);
+ rc = LY_ENOTFOUND;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* compile uses and grouping extensions into the parent */
+ COMPILE_EXTS_GOTO(ctx, uses_p->exts, parent->exts, parent, rc, cleanup);
+ COMPILE_EXTS_GOTO(ctx, grp->exts, parent->exts, parent, rc, cleanup);
+
+cleanup:
+ /* restore previous context */
+ ctx->compile_opts = opt_prev;
+
+ /* remove the grouping from the stack for circular groupings dependency check */
+ ly_set_rm_index(&ctx->groupings, ctx->groupings.count - 1, NULL);
+ assert(ctx->groupings.count == grp_stack_count);
+
+ ly_set_erase(&uses_child_set, NULL);
+ return rc;
+}
+
+static int
+lys_compile_grouping_pathlog(struct lysc_ctx *ctx, struct lysp_node *node, char **path)
+{
+ struct lysp_node *iter;
+ int len = 0;
+
+ *path = NULL;
+ for (iter = node; iter && len >= 0; iter = iter->parent) {
+ char *s = *path;
+ char *id;
+
+ switch (iter->nodetype) {
+ case LYS_USES:
+ LY_CHECK_RET(asprintf(&id, "{uses='%s'}", iter->name) == -1, -1);
+ break;
+ case LYS_GROUPING:
+ LY_CHECK_RET(asprintf(&id, "{grouping='%s'}", iter->name) == -1, -1);
+ break;
+ case LYS_AUGMENT:
+ LY_CHECK_RET(asprintf(&id, "{augment='%s'}", iter->name) == -1, -1);
+ break;
+ default:
+ id = strdup(iter->name);
+ break;
+ }
+
+ if (!iter->parent) {
+ /* print prefix */
+ len = 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 : "");
+ }
+ free(s);
+ free(id);
+ }
+
+ if (len < 0) {
+ free(*path);
+ *path = NULL;
+ } else if (len == 0) {
+ *path = strdup("/");
+ len = 1;
+ }
+ return len;
+}
+
+LY_ERR
+lys_compile_grouping(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysp_node_grp *grp)
+{
+ LY_ERR rc = LY_SUCCESS;
+ char *path;
+ int len;
+
+ /* use grouping status to avoid errors */
+ struct lysp_node_uses fake_uses = {
+ .parent = pnode,
+ .nodetype = LYS_USES,
+ .flags = grp->flags & LYS_STATUS_MASK, .next = NULL,
+ .name = grp->name,
+ .dsc = NULL, .ref = NULL, .when = NULL, .iffeatures = NULL, .exts = NULL,
+ .refines = NULL, .augments = NULL
+ };
+ struct lysc_node_container fake_container = {
+ .nodetype = LYS_CONTAINER,
+ .flags = 0,
+ .module = ctx->cur_mod,
+ .parent = NULL, .next = NULL,
+ .prev = &fake_container.node,
+ .name = "fake",
+ .dsc = NULL, .ref = NULL, .exts = NULL, .when = NULL,
+ .child = NULL, .musts = NULL, .actions = NULL, .notifs = NULL
+ };
+
+ /* compile fake container flags */
+ LY_CHECK_GOTO(rc = lys_compile_node_flags(ctx, pnode ? pnode->flags : 0, 0, &fake_container.node), cleanup);
+
+ if (grp->parent) {
+ LOGWRN(ctx->ctx, "Locally scoped grouping \"%s\" not used.", grp->name);
+ }
+
+ len = lys_compile_grouping_pathlog(ctx, grp->parent, &path);
+ if (len < 0) {
+ LOGMEM(ctx->ctx);
+ return LY_EMEM;
+ }
+ strncpy(ctx->path, path, LYSC_CTX_BUFSIZE - 1);
+ ctx->path_len = (uint32_t)len;
+ free(path);
+
+ lysc_update_path(ctx, NULL, "{grouping}");
+ lysc_update_path(ctx, NULL, grp->name);
+ rc = lys_compile_uses(ctx, &fake_uses, &fake_container.node, 0, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+
+ ctx->path_len = 1;
+ ctx->path[1] = '\0';
+
+cleanup:
+ lysc_node_container_free(&ctx->free_ctx, &fake_container);
+ FREE_ARRAY(&ctx->free_ctx, fake_container.exts, lysc_ext_instance_free);
+ return rc;
+}
+
+LY_ERR
+lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *parent, uint16_t inherited_flags,
+ struct ly_set *child_set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_node *node = NULL;
+ uint32_t prev_opts = ctx->compile_opts;
+
+ LY_ERR (*node_compile_spec)(struct lysc_ctx *, struct lysp_node *, struct lysc_node *);
+
+ if (pnode->nodetype != LYS_USES) {
+ lysc_update_path(ctx, parent ? parent->module : NULL, pnode->name);
+ } else {
+ lysc_update_path(ctx, NULL, "{uses}");
+ lysc_update_path(ctx, NULL, pnode->name);
+ }
+
+ switch (pnode->nodetype) {
+ case LYS_CONTAINER:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_container));
+ node_compile_spec = lys_compile_node_container;
+ break;
+ case LYS_LEAF:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_leaf));
+ node_compile_spec = lys_compile_node_leaf;
+ break;
+ case LYS_LIST:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_list));
+ node_compile_spec = lys_compile_node_list;
+ break;
+ case LYS_LEAFLIST:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_leaflist));
+ node_compile_spec = lys_compile_node_leaflist;
+ break;
+ case LYS_CHOICE:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_choice));
+ node_compile_spec = lys_compile_node_choice;
+ break;
+ case LYS_CASE:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_case));
+ node_compile_spec = lys_compile_node_case;
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_anydata));
+ node_compile_spec = lys_compile_node_any;
+ break;
+ case LYS_RPC:
+ case LYS_ACTION:
+ if (ctx->compile_opts & (LYS_IS_INPUT | LYS_IS_OUTPUT | LYS_IS_NOTIF)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Action \"%s\" is placed inside %s.", pnode->name,
+ (ctx->compile_opts & LYS_IS_NOTIF) ? "notification" : "another RPC/action");
+ return LY_EVALID;
+ }
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_action));
+ node_compile_spec = lys_compile_node_action;
+ ctx->compile_opts |= LYS_COMPILE_NO_CONFIG;
+ break;
+ case LYS_NOTIF:
+ if (ctx->compile_opts & (LYS_IS_INPUT | LYS_IS_OUTPUT | LYS_IS_NOTIF)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Notification \"%s\" is placed inside %s.", pnode->name,
+ (ctx->compile_opts & LYS_IS_NOTIF) ? "another notification" : "RPC/action");
+ return LY_EVALID;
+ }
+ node = (struct lysc_node *)calloc(1, sizeof(struct lysc_node_notif));
+ node_compile_spec = lys_compile_node_notif;
+ ctx->compile_opts |= LYS_COMPILE_NOTIFICATION;
+ break;
+ case LYS_USES:
+ ret = lys_compile_uses(ctx, (struct lysp_node_uses *)pnode, parent, inherited_flags, child_set);
+ lysc_update_path(ctx, NULL, NULL);
+ lysc_update_path(ctx, NULL, NULL);
+ return ret;
+ default:
+ LOGINT(ctx->ctx);
+ return LY_EINT;
+ }
+ LY_CHECK_ERR_RET(!node, LOGMEM(ctx->ctx), LY_EMEM);
+
+ ret = lys_compile_node_(ctx, pnode, parent, inherited_flags, node_compile_spec, node, child_set);
+
+ ctx->compile_opts = prev_opts;
+ lysc_update_path(ctx, NULL, NULL);
+ return ret;
+}
diff --git a/src/schema_compile_node.h b/src/schema_compile_node.h
new file mode 100644
index 0000000..e463de6
--- /dev/null
+++ b/src/schema_compile_node.h
@@ -0,0 +1,202 @@
+/**
+ * @file schema_compile_node.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for schema compilation of common nodes.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SCHEMA_COMPILE_NODE_H_
+#define LY_SCHEMA_COMPILE_NODE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "tree.h"
+#include "tree_schema.h"
+
+struct ly_ctx;
+struct ly_set;
+struct lysc_ctx;
+
+/**
+ * @brief Compile information from the when statement by either standard compilation or by reusing
+ * another compiled when structure.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] when_p Parsed when structure.
+ * @param[in] inherited_flags Inherited flags from a schema-only statement.
+ * @param[in] parent Parent node, if any.
+ * @param[in] ctx_node Context node for the when statement.
+ * @param[in] node Compiled node to which to add the compiled when.
+ * @param[in,out] when_c Optional, pointer to the previously compiled @p when_p to be reused. Set to NULL
+ * for the first call.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_when(struct lysc_ctx *ctx, const struct lysp_when *when_p, uint16_t inherited_flags,
+ const struct lysc_node *parent, const struct lysc_node *ctx_node, struct lysc_node *node, struct lysc_when **when_c);
+
+/**
+ * @brief Compile information from the must statement
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] must_p The parsed must statement structure.
+ * @param[in,out] must Prepared (empty) compiled must structure to fill.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_must(struct lysc_ctx *ctx, const struct lysp_restr *must_p, struct lysc_must *must);
+
+/**
+ * @brief Compile the parsed range restriction.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] range_p Parsed range structure to compile.
+ * @param[in] basetype Base YANG built-in type of the node with the range restriction.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range restriction of the type from which the current type is derived. The current
+ * range restriction must be more restrictive than the base_range.
+ * @param[in,out] range Pointer to the created current range structure.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_type_range(struct lysc_ctx *ctx, const struct lysp_restr *range_p, LY_DATA_TYPE basetype,
+ ly_bool length_restr, uint8_t frdigits, struct lysc_range *base_range, struct lysc_range **range);
+
+/**
+ * @brief Checks pattern syntax.
+ *
+ * @param[in] ctx Context.
+ * @param[in] pattern Pattern to check.
+ * @param[in,out] code Compiled PCRE2 pattern. If NULL, the compiled information used to validate pattern are freed.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
+LY_ERR lys_compile_type_pattern_check(struct ly_ctx *ctx, const char *pattern, pcre2_code **code);
+
+/**
+ * @brief Compile parsed pattern restriction in conjunction with the patterns from base type.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] patterns_p Array of parsed patterns from the current type to compile.
+ * @param[in] base_patterns Compiled patterns from the type from which the current type is derived.
+ * Patterns from the base type are inherited to have all the patterns that have to match at one place.
+ * @param[out] patterns Pointer to the storage for the patterns of the current type.
+ * @return LY_ERR LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
+LY_ERR lys_compile_type_patterns(struct lysc_ctx *ctx, const struct lysp_restr *patterns_p,
+ struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns);
+
+/**
+ * @brief Compile parsed type's enum structures (for enumeration and bits types).
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] enums_p Array of the parsed enum structures to compile.
+ * @param[in] basetype Base YANG built-in type from which the current type is derived. Only LY_TYPE_ENUM and LY_TYPE_BITS are expected.
+ * @param[in] base_enums Array of the compiled enums information from the (latest) base type to check if the current enums are compatible.
+ * @param[out] bitenums Newly created array of the compiled bitenums information for the current type.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR lys_compile_type_enums(struct lysc_ctx *ctx, const struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype,
+ struct lysc_type_bitenum_item *base_enums, struct lysc_type_bitenum_item **bitenums);
+
+/**
+ * @brief Compile information about the leaf/leaf-list's type.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] context_pnode Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[in] type_p Parsed type to compile.
+ * @param[out] type Newly created (or reused with increased refcount) type structure with the filled information about the type.
+ * @param[out] units Storage for inheriting units value from the typedefs the current type derives from.
+ * @param[out] dflt Default value for the type.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t context_flags,
+ const char *context_name, const struct lysp_type *type_p, struct lysc_type **type, const char **units,
+ struct lysp_qname **dflt);
+
+/**
+ * @brief Connect the node into the siblings list and check its name uniqueness. Also,
+ * keep specific order of augments targetting the same node.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] parent Parent node holding the children list, in case of node from a choice's case,
+ * the choice itself is expected instead of a specific case node.
+ * @param[in] node Schema node to connect into the list.
+ * @return LY_ERR value - LY_SUCCESS or LY_EEXIST.
+ * In case of LY_EEXIST, the node is actually kept in the tree, so do not free it directly.
+ */
+LY_ERR lys_compile_node_connect(struct lysc_ctx *ctx, struct lysc_node *parent, struct lysc_node *node);
+
+/**
+ * @brief Compile parsed action's input/output node information.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed inout node.
+ * @param[in,out] node Pre-prepared structure from lys_compile_node_() with filled generic node information
+ * is enriched with the inout-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR lys_compile_node_action_inout(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *node);
+
+/**
+ * @brief Compile choice children.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] child_p Parsed choice children nodes.
+ * @param[in] node Compiled choice node to compile and add children to.
+ * @param[in,out] child_set Optional set to add all the compiled nodes into (can be more in case of uses).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR lys_compile_node_choice_child(struct lysc_ctx *ctx, struct lysp_node *child_p, struct lysc_node *node,
+ struct ly_set *child_set);
+
+/**
+ * @brief Set LYS_MAND_TRUE flag for the non-presence container parents.
+ *
+ * A non-presence container is mandatory in case it has at least one mandatory children. This function propagate
+ * the flag to such parents from a mandatory children.
+ *
+ * @param[in] parent A schema node to be examined if the mandatory child make it also mandatory.
+ * @param[in] add Flag to distinguish adding the mandatory flag (new mandatory children appeared) or removing the flag
+ * (mandatory children was removed).
+ */
+void lys_compile_mandatory_parents(struct lysc_node *parent, ly_bool add);
+
+/**
+ * @brief Validate grouping that was defined but not used in the schema itself.
+ *
+ * The grouping does not need to be compiled (and it is compiled here, but the result is forgotten immediately),
+ * but to have the complete result of the schema validity, even such groupings are supposed to be checked.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pnode Parsed parent node of the grouping, NULL for top-level.
+ * @param[in] grp Parsed grouping node to check.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_grouping(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysp_node_grp *grp);
+
+/**
+ * @brief Compile parsed schema node information.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] pnode Parsed schema node.
+ * @param[in] parent Compiled parent node where the current node is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @param[in] inherited_flags Inherited flags from a schema-only statement.
+ * @param[in,out] child_set Optional set to add all the compiled nodes into (can be more in case of uses).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc_node *parent, uint16_t inherited_flags,
+ struct ly_set *child_set);
+
+#endif /* LY_SCHEMA_COMPILE_NODE_H_ */
diff --git a/src/schema_features.c b/src/schema_features.c
new file mode 100644
index 0000000..dca998e
--- /dev/null
+++ b/src/schema_features.c
@@ -0,0 +1,714 @@
+/**
+ * @file schema_features.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema feature handling
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "schema_features.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "log.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+#define IFF_RECORDS_IN_BYTE 4
+#define IFF_RECORD_BITS 2
+#define IFF_RECORD_MASK 0x3
+
+uint8_t
+lysc_iff_getop(uint8_t *list, size_t pos)
+{
+ uint8_t *item;
+ uint8_t mask = IFF_RECORD_MASK, result;
+
+ item = &list[pos / IFF_RECORDS_IN_BYTE];
+ result = (*item) & (mask << IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE));
+ return result >> IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE);
+}
+
+static LY_ERR
+lysc_iffeature_value_(const struct lysc_iffeature *iff, size_t *index_e, size_t *index_f)
+{
+ uint8_t op;
+ LY_ERR a, b;
+
+ op = lysc_iff_getop(iff->expr, *index_e);
+ (*index_e)++;
+
+ switch (op) {
+ case LYS_IFF_F:
+ /* resolve feature */
+ return (iff->features[(*index_f)++]->flags & LYS_FENABLED) ? LY_SUCCESS : LY_ENOT;
+ case LYS_IFF_NOT:
+ /* invert result */
+ return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
+ case LYS_IFF_AND:
+ case LYS_IFF_OR:
+ a = lysc_iffeature_value_(iff, index_e, index_f);
+ b = lysc_iffeature_value_(iff, index_e, index_f);
+ if (op == LYS_IFF_AND) {
+ if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
+ return LY_SUCCESS;
+ } else {
+ return LY_ENOT;
+ }
+ } else { /* LYS_IFF_OR */
+ if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
+ return LY_SUCCESS;
+ } else {
+ return LY_ENOT;
+ }
+ }
+ }
+
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF LY_ERR
+lysc_iffeature_value(const struct lysc_iffeature *iff)
+{
+ size_t index_e = 0, index_f = 0;
+
+ LY_CHECK_ARG_RET(NULL, iff, LY_EINVAL);
+
+ if (iff->expr) {
+ return lysc_iffeature_value_(iff, &index_e, &index_f);
+ }
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_identity_iffeature_value(const struct lysc_ident *ident)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ ly_bool enabled;
+ const struct lysp_ident *idents_p, *found_ident = NULL;
+ struct lysp_include *includes;
+
+ assert(ident);
+
+ /* Search parsed identity in the module. */
+ idents_p = ident->module->parsed->identities;
+ LY_ARRAY_FOR(idents_p, u) {
+ if (idents_p[u].name == ident->name) {
+ found_ident = &idents_p[u];
+ break;
+ }
+ }
+
+ if (!found_ident) {
+ /* It is not in the module, so it must be in some submodule. */
+ includes = ident->module->parsed->includes;
+ LY_ARRAY_FOR(includes, u) {
+ idents_p = includes[u].submodule->identities;
+ LY_ARRAY_FOR(idents_p, v) {
+ if (idents_p[v].name == ident->name) {
+ found_ident = &idents_p[v];
+ break;
+ }
+ }
+ }
+ }
+
+ assert(found_ident);
+
+ /* Evaluate its if-feature. */
+ LY_CHECK_RET(lys_eval_iffeatures(ident->module->ctx, found_ident->iffeatures, &enabled));
+ if (!enabled) {
+ return LY_ENOT;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF struct lysp_feature *
+lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx)
+{
+ struct lysp_feature *features;
+
+ if (!*idx) {
+ /* module features */
+ features = pmod->features;
+ } else if ((*idx - 1) < LY_ARRAY_COUNT(pmod->includes)) {
+ /* submodule features */
+ features = pmod->includes[*idx - 1].submodule->features;
+ } else {
+ /* no more features */
+ return NULL;
+ }
+
+ /* get the next feature */
+ if (features && (!last || (&features[LY_ARRAY_COUNT(features) - 1] != last))) {
+ return !last ? &features[0] : (struct lysp_feature *)last + 1;
+ }
+
+ /* no more features in current (sub)module */
+ ++(*idx);
+ return lysp_feature_next(NULL, pmod, idx);
+}
+
+/**
+ * @brief Find a feature of the given name and referenced in the given module.
+ *
+ * @param[in] pmod Module where the feature was referenced (used to resolve prefix of the feature).
+ * @param[in] name Name of the feature including possible prefix.
+ * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
+ * @param[in] prefixed Whether the feature name can be prefixed.
+ * @return Pointer to the feature structure if found, NULL otherwise.
+ */
+static struct lysp_feature *
+lysp_feature_find(const struct lysp_module *pmod, const char *name, size_t len, ly_bool prefixed)
+{
+ const struct lys_module *mod;
+ const char *ptr;
+ struct lysp_feature *f = NULL;
+ uint32_t idx = 0;
+
+ assert(pmod);
+
+ if (prefixed && (ptr = ly_strnchr(name, ':', len))) {
+ /* we have a prefixed feature */
+ mod = ly_resolve_prefix(pmod->mod->ctx, name, ptr - name, LY_VALUE_SCHEMA, (void *)pmod);
+ LY_CHECK_RET(!mod, NULL);
+
+ pmod = mod->parsed;
+ len = len - (ptr - name) - 1;
+ name = ptr + 1;
+ }
+
+ /* feature without prefix, look in main module and all submodules */
+ if (pmod->is_submod) {
+ pmod = pmod->mod->parsed;
+ }
+
+ /* we have the correct module, get the feature */
+ while ((f = lysp_feature_next(f, pmod, &idx))) {
+ if (!ly_strncmp(f->name, name, len)) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_feature_value(const struct lys_module *module, const char *feature)
+{
+ const struct lysp_feature *f;
+
+ LY_CHECK_ARG_RET(NULL, module, module->parsed, feature, LY_EINVAL);
+
+ /* search for the specified feature */
+ f = lysp_feature_find(module->parsed, feature, strlen(feature), 0);
+ LY_CHECK_RET(!f, LY_ENOTFOUND);
+
+ /* feature disabled */
+ if (!(f->flags & LYS_FENABLED)) {
+ return LY_ENOT;
+ }
+
+ /* feature enabled */
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Stack for processing if-feature expressions.
+ */
+struct iff_stack {
+ size_t size; /**< number of items in the stack */
+ size_t index; /**< first empty item */
+ uint8_t *stack; /**< stack - array of @ref ifftokens to create the if-feature expression in prefix format */
+};
+#define IFF_STACK_SIZE_STEP 4
+
+/**
+ * @brief Add @ref ifftokens into the stack.
+ * @param[in] stack The if-feature stack to use.
+ * @param[in] value One of the @ref ifftokens to store in the stack.
+ * @return LY_EMEM in case of memory allocation error
+ * @return LY_ESUCCESS if the value successfully stored.
+ */
+static LY_ERR
+iff_stack_push(struct iff_stack *stack, uint8_t value)
+{
+ if (stack->index == stack->size) {
+ stack->size += IFF_STACK_SIZE_STEP;
+ stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
+ LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+ }
+ stack->stack[stack->index++] = value;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get (and remove) the last item form the stack.
+ * @param[in] stack The if-feature stack to use.
+ * @return The value from the top of the stack.
+ */
+static uint8_t
+iff_stack_pop(struct iff_stack *stack)
+{
+ assert(stack && stack->index);
+
+ stack->index--;
+ return stack->stack[stack->index];
+}
+
+/**
+ * @brief Clean up the stack.
+ * @param[in] stack The if-feature stack to use.
+ */
+static void
+iff_stack_clean(struct iff_stack *stack)
+{
+ stack->size = 0;
+ free(stack->stack);
+}
+
+/**
+ * @brief Store the @ref ifftokens (@p op) on the given position in the 2bits array
+ * (libyang format of the if-feature expression).
+ * @param[in,out] list The 2bits array to modify.
+ * @param[in] op The operand (@ref ifftokens) to store.
+ * @param[in] pos Position (0-based) where to store the given @p op.
+ */
+static void
+iff_setop(uint8_t *list, uint8_t op, size_t pos)
+{
+ uint8_t *item;
+ uint8_t mask = IFF_RECORD_MASK;
+
+ assert(op <= IFF_RECORD_MASK); /* max 2 bits */
+
+ item = &list[pos / IFF_RECORDS_IN_BYTE];
+ mask = mask << IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE);
+ *item = (*item) & ~mask;
+ *item = (*item) | (op << IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE));
+}
+
+#define LYS_IFF_LP 0x04 /**< Additional, temporary, value of @ref ifftokens: ( */
+#define LYS_IFF_RP 0x08 /**< Additional, temporary, value of @ref ifftokens: ) */
+
+static LY_ERR
+lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname, struct lysc_iffeature *iff)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const char *c = qname->str;
+ int64_t i, j;
+ int8_t op_len, last_not = 0, checkversion = 0;
+ LY_ARRAY_COUNT_TYPE f_size = 0, expr_size = 0, f_exp = 1;
+ uint8_t op;
+ struct iff_stack stack = {0, 0, NULL};
+ struct lysp_feature *f;
+
+ assert(c);
+
+ /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
+ for (i = j = 0; c[i]; i++) {
+ if (c[i] == '(') {
+ j++;
+ checkversion = 1;
+ continue;
+ } else if (c[i] == ')') {
+ j--;
+ continue;
+ } else if (isspace(c[i])) {
+ checkversion = 1;
+ continue;
+ }
+
+ if (!strncmp(&c[i], "not", op_len = ly_strlen_const("not")) ||
+ !strncmp(&c[i], "and", op_len = ly_strlen_const("and")) ||
+ !strncmp(&c[i], "or", op_len = ly_strlen_const("or"))) {
+ uint64_t spaces;
+
+ for (spaces = 0; c[i + op_len + spaces] && isspace(c[i + op_len + spaces]); spaces++) {}
+ if (c[i + op_len + spaces] == '\0') {
+ LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - unexpected end of expression.", qname->str);
+ return LY_EVALID;
+ } else if (!isspace(c[i + op_len])) {
+ /* feature name starting with the not/and/or */
+ last_not = 0;
+ f_size++;
+ } else if (c[i] == 'n') { /* not operation */
+ if (last_not) {
+ /* double not */
+ expr_size = expr_size - 2;
+ last_not = 0;
+ } else {
+ last_not = 1;
+ }
+ } else { /* and, or */
+ if (f_exp != f_size) {
+ LOGVAL(ctx, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - missing feature/expression before \"%.*s\" operation.",
+ qname->str, op_len, &c[i]);
+ return LY_EVALID;
+ }
+ f_exp++;
+
+ /* not a not operation */
+ last_not = 0;
+ }
+ i += op_len;
+ } else {
+ f_size++;
+ last_not = 0;
+ }
+ expr_size++;
+
+ while (!isspace(c[i])) {
+ if (!c[i] || (c[i] == ')') || (c[i] == '(')) {
+ i--;
+ break;
+ }
+ i++;
+ }
+ }
+ if (j) {
+ /* not matching count of ( and ) */
+ LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.",
+ qname->str);
+ return LY_EVALID;
+ }
+ if (f_exp != f_size) {
+ /* features do not match the needed arguments for the logical operations */
+ LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - number of features in expression does not match "
+ "the required number of operands for the operations.", qname->str);
+ return LY_EVALID;
+ }
+
+ if (checkversion || (expr_size > 1)) {
+ /* check that we have 1.1 module */
+ if (qname->mod->version != LYS_VERSION_1_1) {
+ LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.",
+ qname->str);
+ return LY_EVALID;
+ }
+ }
+
+ /* allocate the memory */
+ LY_ARRAY_CREATE_RET(ctx, iff->features, f_size, LY_EMEM);
+ iff->expr = calloc((j = (expr_size / IFF_RECORDS_IN_BYTE) + ((expr_size % IFF_RECORDS_IN_BYTE) ? 1 : 0)), sizeof *iff->expr);
+ stack.stack = malloc(expr_size * sizeof *stack.stack);
+ LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, error);
+
+ stack.size = expr_size;
+ f_size--; expr_size--; /* used as indexes from now */
+
+ for (i--; i >= 0; i--) {
+ if (c[i] == ')') {
+ /* push it on stack */
+ iff_stack_push(&stack, LYS_IFF_RP);
+ continue;
+ } else if (c[i] == '(') {
+ /* pop from the stack into result all operators until ) */
+ while ((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ continue;
+ } else if (isspace(c[i])) {
+ continue;
+ }
+
+ /* end of operator or operand -> find beginning and get what is it */
+ j = i + 1;
+ while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
+ i--;
+ }
+ i++; /* go back by one step */
+
+ if (!strncmp(&c[i], "not", ly_strlen_const("not")) && isspace(c[i + ly_strlen_const("not")])) {
+ if (stack.index && (stack.stack[stack.index - 1] == LYS_IFF_NOT)) {
+ /* double not */
+ iff_stack_pop(&stack);
+ } else {
+ /* not has the highest priority, so do not pop from the stack
+ * as in case of AND and OR */
+ iff_stack_push(&stack, LYS_IFF_NOT);
+ }
+ } else if (!strncmp(&c[i], "and", ly_strlen_const("and")) && isspace(c[i + ly_strlen_const("and")])) {
+ /* as for OR - pop from the stack all operators with the same or higher
+ * priority and store them to the result, then push the AND to the stack */
+ while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ iff_stack_push(&stack, LYS_IFF_AND);
+ } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
+ while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ iff_stack_push(&stack, LYS_IFF_OR);
+ } else {
+ /* feature name, length is j - i */
+
+ /* add it to the expression */
+ iff_setop(iff->expr, LYS_IFF_F, expr_size--);
+
+ /* now get the link to the feature definition */
+ f = lysp_feature_find(qname->mod, &c[i], j - i, 1);
+ if (!f) {
+ LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".",
+ qname->str, (int)(j - i), &c[i]);
+ rc = LY_EVALID;
+ goto error;
+ }
+ iff->features[f_size] = f;
+ LY_ARRAY_INCREMENT(iff->features);
+ f_size--;
+ }
+ }
+ while (stack.index) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+
+ if (++expr_size || ++f_size) {
+ /* not all expected operators and operands found */
+ LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - processing error.", qname->str);
+ rc = LY_EINT;
+ } else {
+ rc = LY_SUCCESS;
+ }
+
+error:
+ /* cleanup */
+ iff_stack_clean(&stack);
+
+ return rc;
+}
+
+LY_ERR
+lys_eval_iffeatures(const struct ly_ctx *ctx, const struct lysp_qname *iffeatures, ly_bool *enabled)
+{
+ LY_ERR ret;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_iffeature iff;
+ struct lysf_ctx fctx = {.ctx = (struct ly_ctx *)ctx};
+
+ /* enabled by default */
+ *enabled = 1;
+
+ if (!iffeatures) {
+ return LY_SUCCESS;
+ }
+
+ /* evaluate all if-feature conditions or until an unsatisfied one is found */
+ LY_ARRAY_FOR(iffeatures, u) {
+ memset(&iff, 0, sizeof iff);
+ LY_CHECK_RET(lys_compile_iffeature(ctx, &iffeatures[u], &iff));
+
+ ret = lysc_iffeature_value(&iff);
+ lysc_iffeature_free(&fctx, &iff);
+ if (ret == LY_ENOT) {
+ *enabled = 0;
+ break;
+ } else if (ret) {
+ return ret;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_check_features(const struct lysp_module *pmod)
+{
+ LY_ERR r;
+ uint32_t i = 0;
+ struct lysp_feature *f = NULL;
+
+ while ((f = lysp_feature_next(f, pmod, &i))) {
+ if (!(f->flags & LYS_FENABLED) || !f->iffeatures) {
+ /* disabled feature or no if-features to check */
+ continue;
+ }
+
+ assert(f->iffeatures_c);
+ r = lysc_iffeature_value(f->iffeatures_c);
+ if (r == LY_ENOT) {
+ LOGERR(pmod->mod->ctx, LY_EDENIED, "Feature \"%s\" cannot be enabled because its \"if-feature\" is not satisfied.",
+ f->name);
+ return LY_EDENIED;
+ } else if (r) {
+ return r;
+ } /* else if-feature satisfied */
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_set_features(struct lysp_module *pmod, const char **features)
+{
+ uint32_t i = 0, j;
+ struct lysp_feature *f = 0;
+ ly_bool change = 0;
+
+ if (!features) {
+ /* do not touch the features */
+
+ } else if (!features[0]) {
+ /* disable all the features */
+ while ((f = lysp_feature_next(f, pmod, &i))) {
+ if (f->flags & LYS_FENABLED) {
+ f->flags &= ~LYS_FENABLED;
+ change = 1;
+ }
+ }
+ } else if (!strcmp(features[0], "*")) {
+ /* enable all the features */
+ while ((f = lysp_feature_next(f, pmod, &i))) {
+ if (!(f->flags & LYS_FENABLED)) {
+ f->flags |= LYS_FENABLED;
+ change = 1;
+ }
+ }
+ } else {
+ /* check that all the features exist */
+ for (j = 0; features[j]; ++j) {
+ if (!lysp_feature_find(pmod, features[j], strlen(features[j]), 0)) {
+ LOGERR(pmod->mod->ctx, LY_EINVAL, "Feature \"%s\" not found in module \"%s\".", features[j], pmod->mod->name);
+ return LY_EINVAL;
+ }
+ }
+
+ /* enable specific features, disable the rest */
+ while ((f = lysp_feature_next(f, pmod, &i))) {
+ for (j = 0; features[j]; ++j) {
+ if (!strcmp(f->name, features[j])) {
+ break;
+ }
+ }
+
+ if (features[j] && !(f->flags & LYS_FENABLED)) {
+ /* enable */
+ f->flags |= LYS_FENABLED;
+ change = 1;
+ } else if (!features[j] && (f->flags & LYS_FENABLED)) {
+ /* disable */
+ f->flags &= ~LYS_FENABLED;
+ change = 1;
+ }
+ }
+ }
+
+ if (!change) {
+ /* features already set correctly */
+ return LY_EEXIST;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check circular dependency of features - feature MUST NOT reference itself (via their if-feature statement).
+ *
+ * The function works in the same way as lys_compile_identity_circular_check() with different structures and error messages.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] feature The feature referenced in if-feature statement (its depfeatures list is being extended by the feature
+ * being currently processed).
+ * @param[in] depfeatures The list of depending features of the feature being currently processed (not the one provided as @p feature)
+ * @return LY_SUCCESS if everything is ok.
+ * @return LY_EVALID if the feature references indirectly itself.
+ */
+static LY_ERR
+lys_compile_feature_circular_check(const struct ly_ctx *ctx, struct lysp_feature *feature, struct lysp_feature **depfeatures)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct ly_set recursion = {0};
+ struct lysp_feature *drv;
+
+ if (!depfeatures) {
+ return LY_SUCCESS;
+ }
+
+ for (u = 0; u < LY_ARRAY_COUNT(depfeatures); ++u) {
+ if (feature == depfeatures[u]) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.", feature->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ret = ly_set_add(&recursion, depfeatures[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ for (v = 0; v < recursion.count; ++v) {
+ drv = recursion.objs[v];
+ for (u = 0; u < LY_ARRAY_COUNT(drv->depfeatures); ++u) {
+ if (feature == drv->depfeatures[u]) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.", feature->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ly_set_add(&recursion, drv->depfeatures[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ ly_set_erase(&recursion, NULL);
+ return ret;
+}
+
+LY_ERR
+lys_compile_feature_iffeatures(struct lysp_module *pmod)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysp_feature *f = NULL, **df;
+ uint32_t idx = 0;
+
+ while ((f = lysp_feature_next(f, pmod, &idx))) {
+ if (!f->iffeatures) {
+ continue;
+ }
+
+ /* compile if-features */
+ LY_ARRAY_CREATE_RET(pmod->mod->ctx, f->iffeatures_c, LY_ARRAY_COUNT(f->iffeatures), LY_EMEM);
+ LY_ARRAY_FOR(f->iffeatures, u) {
+ LY_ARRAY_INCREMENT(f->iffeatures_c);
+ LY_CHECK_RET(lys_compile_iffeature(pmod->mod->ctx, &(f->iffeatures)[u], &(f->iffeatures_c)[u]));
+ }
+ LY_ARRAY_FOR(f->iffeatures_c, u) {
+ LY_ARRAY_FOR(f->iffeatures_c[u].features, v) {
+ /* check for circular dependency - direct reference first,... */
+ if (f == f->iffeatures_c[u].features[v]) {
+ LOGVAL(pmod->mod->ctx, LYVE_REFERENCE, "Feature \"%s\" is referenced from itself.", f->name);
+ return LY_EVALID;
+ }
+ /* ... and indirect circular reference */
+ LY_CHECK_RET(lys_compile_feature_circular_check(pmod->mod->ctx, f->iffeatures_c[u].features[v], f->depfeatures));
+
+ /* add itself into the dependants list */
+ LY_ARRAY_NEW_RET(pmod->mod->ctx, f->iffeatures_c[u].features[v]->depfeatures, df, LY_EMEM);
+ *df = f;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
diff --git a/src/schema_features.h b/src/schema_features.h
new file mode 100644
index 0000000..c73561f
--- /dev/null
+++ b/src/schema_features.h
@@ -0,0 +1,67 @@
+/**
+ * @file schema_features.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for schema features.
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SCHEMA_FEATURES_H_
+#define LY_SCHEMA_FEATURES_H_
+
+#include "log.h"
+
+struct ly_ctx;
+struct lysp_module;
+struct lysp_qname;
+
+/**
+ * @brief Evaluate if-features array.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] iffeatures Sized array of if-features to evaluate.
+ * @param[out] enabled Whether if-features evaluated to true or false.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_eval_iffeatures(const struct ly_ctx *ctx, const struct lysp_qname *iffeatures, ly_bool *enabled);
+
+/**
+ * @brief Check whether all enabled features have their if-features satisfied.
+ *
+ * @param[in] pmod Parsed module features to check.
+ * @return LY_SUCCESS on success.
+ * @return LY_EDENIED if there was an enabled feature with disabled if-feature.
+ */
+LY_ERR lys_check_features(const struct lysp_module *pmod);
+
+/**
+ * @brief Set the specified features of a parsed module ignoring their own if-features. These are all checked before
+ * compiling the module(s).
+ *
+ * @param[in] pmod Parsed module to modify.
+ * @param[in] features Array of features ended with NULL to be enabled if the module is being implemented.
+ * The feature string '*' enables all and array of length 1 with only the terminating NULL explicitly disables all
+ * the features. In case the parameter is NULL, the features are untouched - left disabled in newly loaded module or
+ * with the current features settings in case the module is already present in the context.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the specified features were already set.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_set_features(struct lysp_module *pmod, const char **features);
+
+/**
+ * @brief Compile if-features of features in the provided module and all its submodules.
+ *
+ * @param[in] pmod Parsed module to process.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_feature_iffeatures(struct lysp_module *pmod);
+
+#endif /* LY_SCHEMA_FEATURES_H_ */
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 0000000..1b8bfa5
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,247 @@
+/**
+ * @file set.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic set routines implementations
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "set.h"
+
+LIBYANG_API_DEF LY_ERR
+ly_set_new(struct ly_set **set_p)
+{
+ LY_CHECK_ARG_RET(NULL, set_p, LY_EINVAL);
+
+ *set_p = calloc(1, sizeof **set_p);
+ LY_CHECK_ERR_RET(!(*set_p), LOGMEM(NULL), LY_EMEM);
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF void
+ly_set_clean(struct ly_set *set, void (*destructor)(void *obj))
+{
+ uint32_t u;
+
+ if (!set) {
+ return;
+ }
+
+ if (destructor) {
+ for (u = 0; u < set->count; ++u) {
+ destructor(set->objs[u]);
+ }
+ }
+ set->count = 0;
+}
+
+LIBYANG_API_DEF void
+ly_set_erase(struct ly_set *set, void (*destructor)(void *obj))
+{
+ if (!set) {
+ return;
+ }
+
+ ly_set_clean(set, destructor);
+
+ free(set->objs);
+ set->size = 0;
+ set->objs = NULL;
+}
+
+LIBYANG_API_DEF void
+ly_set_free(struct ly_set *set, void (*destructor)(void *obj))
+{
+ if (!set) {
+ return;
+ }
+
+ ly_set_erase(set, destructor);
+
+ free(set);
+}
+
+LIBYANG_API_DEF ly_bool
+ly_set_contains(const struct ly_set *set, const void *object, uint32_t *index_p)
+{
+ LY_CHECK_ARG_RET(NULL, set, 0);
+
+ for (uint32_t i = 0; i < set->count; i++) {
+ if (set->objs[i] == object) {
+ /* object found */
+ if (index_p) {
+ *index_p = i;
+ }
+ return 1;
+ }
+ }
+
+ /* object not found */
+ return 0;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_set_dup(const struct ly_set *set, void *(*duplicator)(const void *obj), struct ly_set **newset_p)
+{
+ struct ly_set *newset;
+ uint32_t u;
+
+ LY_CHECK_ARG_RET(NULL, set, newset_p, LY_EINVAL);
+
+ newset = calloc(1, sizeof *newset);
+ LY_CHECK_ERR_RET(!newset, LOGMEM(NULL), LY_EMEM);
+ if (!set->count) {
+ *newset_p = newset;
+ return LY_SUCCESS;
+ }
+
+ newset->count = set->count;
+ newset->size = set->count; /* optimize the size */
+ newset->objs = malloc(newset->size * sizeof *(newset->objs));
+ LY_CHECK_ERR_RET(!newset->objs, LOGMEM(NULL); free(newset), LY_EMEM);
+ if (duplicator) {
+ for (u = 0; u < set->count; ++u) {
+ newset->objs[u] = duplicator(set->objs[u]);
+ }
+ } else {
+ memcpy(newset->objs, set->objs, newset->size * sizeof *(newset->objs));
+ }
+
+ *newset_p = newset;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_set_add(struct ly_set *set, const void *object, ly_bool list, uint32_t *index_p)
+{
+ void **new;
+
+ LY_CHECK_ARG_RET(NULL, set, LY_EINVAL);
+
+ if (!list) {
+ /* search for duplication */
+ for (uint32_t i = 0; i < set->count; i++) {
+ if (set->objs[i] == object) {
+ /* already in set */
+ if (index_p) {
+ *index_p = i;
+ }
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ if (set->size == set->count) {
+#define SET_SIZE_STEP 8
+ new = realloc(set->objs, (set->size + SET_SIZE_STEP) * sizeof *(set->objs));
+ LY_CHECK_ERR_RET(!new, LOGMEM(NULL), LY_EMEM);
+ set->size += SET_SIZE_STEP;
+ set->objs = new;
+#undef SET_SIZE_STEP
+ }
+
+ if (index_p) {
+ *index_p = set->count;
+ }
+ set->objs[set->count++] = (void *)object;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_set_merge(struct ly_set *trg, const struct ly_set *src, ly_bool list, void *(*duplicator)(const void *obj))
+{
+ uint32_t u;
+ void *obj;
+
+ LY_CHECK_ARG_RET(NULL, trg, LY_EINVAL);
+
+ if (!src) {
+ /* nothing to do */
+ return LY_SUCCESS;
+ }
+
+ for (u = 0; u < src->count; ++u) {
+ if (duplicator) {
+ obj = duplicator(src->objs[u]);
+ } else {
+ obj = src->objs[u];
+ }
+ LY_CHECK_RET(ly_set_add(trg, obj, list, NULL));
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj))
+{
+ LY_CHECK_ARG_RET(NULL, set, LY_EINVAL);
+ LY_CHECK_ERR_RET(index >= set->count, LOGARG(NULL, index), LY_EINVAL);
+
+ if (destructor) {
+ destructor(set->objs[index]);
+ }
+ if (index == set->count - 1) {
+ /* removing last item in set */
+ set->objs[index] = NULL;
+ } else {
+ /* removing item somewhere in a middle, so put there the last item */
+ set->objs[index] = set->objs[set->count - 1];
+ set->objs[set->count - 1] = NULL;
+ }
+ set->count--;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_set_rm(struct ly_set *set, void *object, void (*destructor)(void *obj))
+{
+ uint32_t i;
+
+ LY_CHECK_ARG_RET(NULL, set, object, LY_EINVAL);
+
+ /* get index */
+ for (i = 0; i < set->count; i++) {
+ if (set->objs[i] == object) {
+ break;
+ }
+ }
+ LY_CHECK_ERR_RET((i == set->count), LOGARG(NULL, object), LY_EINVAL); /* object is not in set */
+
+ return ly_set_rm_index(set, i, destructor);
+}
+
+LY_ERR
+ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj))
+{
+ if (destructor) {
+ destructor(set->objs[index]);
+ }
+ set->count--;
+ if (index == set->count) {
+ /* removing last item in set */
+ set->objs[index] = NULL;
+ } else {
+ /* removing item somewhere in a middle, move following items */
+ memmove(set->objs + index, set->objs + index + 1, (set->count - index) * sizeof *set->objs);
+ set->objs[set->count] = NULL;
+ }
+
+ return LY_SUCCESS;
+}
diff --git a/src/set.h b/src/set.h
new file mode 100644
index 0000000..3f79916
--- /dev/null
+++ b/src/set.h
@@ -0,0 +1,181 @@
+/**
+ * @file set.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic set structure and manipulation routines.
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SET_H_
+#define LY_SET_H_
+
+#include <stdint.h>
+
+#include "log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup lyset Generic sets
+ *
+ * Structure and functions to hold and manipulate with sets of nodes from schema or data trees.
+ *
+ * @{
+ */
+
+/**
+ * @brief Structure to hold a set of (not necessary somehow connected) objects. Usually used for lyd_node,
+ * ::lysp_node or ::lysc_node objects, but it is not limited to them. Caller is supposed to not mix the type of objects
+ * added to the set and according to its knowledge about the set content, it can access objects via the members
+ * of the set union.
+ *
+ * Until ::ly_set_rm() or ::ly_set_rm_index() is used, the set keeps the order of the inserted items as they
+ * were added into the set, so the first added item is on array index 0.
+ *
+ * To free the structure, use ::ly_set_free() function, to manipulate with the structure, use other
+ * ly_set_* functions.
+ */
+struct ly_set {
+ uint32_t size; /**< allocated size of the set array */
+ uint32_t count; /**< number of elements in (used size of) the set array */
+
+ union {
+ struct lyd_node **dnodes; /**< set array of data nodes */
+ struct lysc_node **snodes; /**< set array of schema nodes */
+ void **objs; /**< set array of generic object pointers */
+ };
+};
+
+/**
+ * @brief Create and initiate new ::ly_set structure.
+ *
+ * @param[out] set_p Pointer to store the created ::ly_set structure.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINVAL in case of NULL @p set parameter.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_new(struct ly_set **set_p);
+
+/**
+ * @brief Duplicate the existing set.
+ *
+ * @param[in] set Original set to duplicate
+ * @param[in] duplicator Optional pointer to function that duplicates the objects stored
+ * in the original set. If not provided, the new set points to the exact same objects as
+ * the original set.
+ * @param[out] newset_p Pointer to return the duplication of the original set.
+ * @return LY_SUCCESS in case the data were successfully duplicated.
+ * @return LY_EMEM in case of memory allocation failure.
+ * @return LY_EINVAL in case of invalid parameters.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_dup(const struct ly_set *set, void *(*duplicator)(const void *obj), struct ly_set **newset_p);
+
+/**
+ * @brief Add an object into the set
+ *
+ * @param[in] set Set where the \p object will be added.
+ * @param[in] object Object to be added into the \p set;
+ * @param[in] list flag to handle set as a list (without checking for (ignoring) duplicit items)
+ * @param[out] index_p Optional pointer to return index of the added @p object. Usually it is the last index (::ly_set::count - 1),
+ * but in case the duplicities are checked and the object is already in the set, the @p object is not added and index of the
+ * already present object is returned.
+ * @return LY_SUCCESS in case of success
+ * @return LY_EINVAL in case of invalid input parameters.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_add(struct ly_set *set, const void *object, ly_bool list, uint32_t *index_p);
+
+/**
+ * @brief Add all objects from \p src to \p trg.
+ *
+ * Since it is a set, the function checks for duplicities.
+ *
+ * @param[in] trg Target (result) set.
+ * @param[in] src Source set.
+ * @param[in] list flag to handle set as a list (without checking for (ignoring) duplicit items)
+ * @param[in] duplicator Optional pointer to function that duplicates the objects being added
+ * from \p src into \p trg set. If not provided, the \p trg set will point to the exact same
+ * objects as the \p src set.
+ * @return LY_SUCCESS in case of success
+ * @return LY_EINVAL in case of invalid input parameters.
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_merge(struct ly_set *trg, const struct ly_set *src, ly_bool list, void *(*duplicator)(const void *obj));
+
+/**
+ * @brief Learn whether the set contains the specified object.
+ *
+ * @param[in] set Set to explore.
+ * @param[in] object Object to be found in the set.
+ * @param[out] index_p Optional pointer to return index of the searched @p object.
+ * @return Boolean value whether the @p object was found in the @p set.
+ */
+LIBYANG_API_DECL ly_bool ly_set_contains(const struct ly_set *set, const void *object, uint32_t *index_p);
+
+/**
+ * @brief Remove all objects from the set, but keep the set container for further use.
+ *
+ * @param[in] set Set to clean.
+ * @param[in] destructor Optional function to free the objects in the set.
+ */
+LIBYANG_API_DECL void ly_set_clean(struct ly_set *set, void (*destructor)(void *obj));
+
+/**
+ * @brief Remove an object from the set.
+ *
+ * Note that after removing the object from a set, indexes of other objects in the set can change
+ * (the last object is placed instead of the removed object).
+ *
+ * @param[in] set Set from which the \p node will be removed.
+ * @param[in] object The object to be removed from the \p set.
+ * @param[in] destructor Optional function to free the objects being removed.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_rm(struct ly_set *set, void *object, void (*destructor)(void *obj));
+
+/**
+ * @brief Remove an object on the specific set index.
+ *
+ * Note that after removing the object from a set, indexes of other nodes in the set can change
+ * (the last object is placed instead of the removed object).
+ *
+ * @param[in] set Set from which a node will be removed.
+ * @param[in] index Index of the object to remove in the \p set.
+ * @param[in] destructor Optional function to free the objects being removed.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
+
+/**
+ * @brief Free the ::ly_set data. If the destructor is not provided, it frees only the set structure
+ * content, not the referred data.
+ *
+ * @param[in] set The set to be freed.
+ * @param[in] destructor Optional function to free the objects in the set.
+ */
+LIBYANG_API_DECL void ly_set_free(struct ly_set *set, void (*destructor)(void *obj));
+
+/**
+ * @brief Alternative to the ::ly_set_free() for static ::ly_set objects - in contrast to ::ly_set_free()
+ * it does not free the provided ::ly_set object.
+ *
+ * @param[in] set The set to be erased.
+ * @param[in] destructor Optional function to free the objects in the set.
+ */
+LIBYANG_API_DECL void ly_set_erase(struct ly_set *set, void (*destructor)(void *obj));
+
+/** @} lyset */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_SET_H_ */
diff --git a/src/tree.h b/src/tree.h
new file mode 100644
index 0000000..b02b1e1
--- /dev/null
+++ b/src/tree.h
@@ -0,0 +1,250 @@
+/**
+ * @file tree.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang generic macros and functions to work with YANG schema or data trees.
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_H_
+#define LY_TREE_H_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page howtoXPath XPath Addressing
+ *
+ * Internally, XPath evaluation is performed on __when__ and __must__ conditions in the schema. For that almost
+ * a full [XPath 1.0](http://www.w3.org/TR/1999/REC-xpath-19991116/) evaluator was implemented.
+ * In YANG models you can also find paths identifying __augment__ targets, __leafref__ targets, and trivial paths in
+ * __choice default__ and __unique__ statements argument. The exact format of all those paths can be found in the
+ * relevant RFCs. Further will only be discussed paths that are used directly in libyang API functions.
+ *
+ * XPath
+ * =====
+ *
+ * Generally, any xpath argument expects an expression similar to _when_ or _must_ as the same evaluator is used. As for
+ * the format of any prefixes, the standardized JSON ([RFC 7951](https://tools.ietf.org/html/rfc7951#section-6.11))
+ * was used. Summarized, xpath follows these conventions:
+ * - full XPath can be used, but only data nodes (node sets) will always be returned,
+ * - as per the specification, prefixes are actually __module names__,
+ * - also in the specification, for _absolute_ paths, the first (leftmost) node _MUST_ have a prefix,
+ * - for _relative_ paths, you specify the __context node__, which then acts as a parent for the first node in the path,
+ * - nodes always inherit their module (prefix) from their __parent node__ so whenever a node is from a different
+ * module than its parent, it _MUST_ have a prefix,
+ * - nodes from the same module as their __parent__ _MUST NOT_ have a prefix,
+ * - note that non-data nodes/schema-only node (choice, case, uses, input, output) are skipped and _MUST_ not be
+ * included in the path.
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_find_xpath()
+ * - ::lys_find_xpath()
+ *
+ * Path
+ * ====
+ *
+ * The term path is used when a simplified (subset of) XPath is expected. Path is always a valid XPath but not
+ * the other way around. In short, paths only identify a specific (set of) nodes based on their ancestors in the
+ * schema. Predicates are allowed the same as for an [instance-identifier](https://tools.ietf.org/html/rfc7950#section-9.13).
+ * Specifically, key values of a list, leaf-list value, or position of lists without keys can be used.
+ *
+ * Examples
+ * --------
+ *
+ * - get __list__ instance with __key1__ of value __1__ and __key2__ of value __2__ (this can return more __list__ instances if there are more keys than __key1__ and __key2__)
+ *
+ * /module-name:container/list[key1='1'][key2='2']
+ *
+ * - get __leaf-list__ instance with the value __val__
+ *
+ * /module-name:container/leaf-list[.='val']
+ *
+ * - get __3rd list-without-keys__ instance with no keys defined
+ *
+ * /module-name:container/list-without-keys[3]
+ *
+ * - get __aug-list__ with __aug-list-key__, which was added to __module-name__ from an augment module __augment-module__
+ *
+ * /module-name:container/container2/augment-module:aug-cont/aug-list[aug-list-key='value']
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_new_path()
+ * - ::lyd_new_path2()
+ * - ::lyd_path()
+ * - ::lyd_find_path()
+ * - ::lys_find_path()
+ *
+ */
+
+/**
+ * @defgroup trees Trees
+ *
+ * Generic macros, functions, etc. to work with both [schema](@ref schematree) and [data](@ref datatree) trees.
+ *
+ * @{
+ */
+
+/**
+ * @brief Type (i.e. size) of the [sized array](@ref sizedarrays)'s size counter.
+ *
+ * To print the value via a print format, use LY_PRI_ARRAY_COUNT_TYPE specifier.
+ */
+#define LY_ARRAY_COUNT_TYPE uint64_t
+
+/**
+ * @brief Printing format specifier macro for LY_ARRAY_SIZE_TYPE values.
+ */
+#define LY_PRI_ARRAY_COUNT_TYPE PRIu64
+
+/**
+ * @brief Macro selector for other LY_ARRAY_* macros, do not use directly!
+ */
+#define LY_ARRAY_SELECT(_1, _2, NAME, ...) NAME
+
+/**
+ * @brief Helper macro to go through sized-arrays with a pointer iterator.
+ *
+ * Use with opening curly bracket (`{`).
+ *
+ * @param[in] ARRAY Array to go through
+ * @param[in] TYPE Type of the records in the ARRAY
+ * @param[out] ITER Iterating pointer to the item being processed in each loop
+ */
+#define LY_ARRAY_FOR_ITER(ARRAY, TYPE, ITER) \
+ for (ITER = ARRAY; \
+ (ARRAY) && ((char *)ITER - (char *)ARRAY)/(sizeof(TYPE)) < (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
+ ITER = (TYPE*)ITER + 1)
+
+/**
+ * @brief Helper macro to go through sized-arrays with a numeric iterator.
+ *
+ * Use with opening curly bracket (`{`).
+ *
+ * The item on the current INDEX in the ARRAY can be accessed in a standard C way as ARRAY[INDEX].
+ *
+ * @param[in] ARRAY Array to go through
+ * @param[out] INDEX Variable for the iterating index of the item being processed in each loop
+ */
+#define LY_ARRAY_FOR_INDEX(ARRAY, INDEX) \
+ for (INDEX = 0; \
+ INDEX < LY_ARRAY_COUNT(ARRAY); \
+ ++INDEX)
+
+/**
+ * @brief Get the number of records in the ARRAY.
+ */
+#define LY_ARRAY_COUNT(ARRAY) (ARRAY ? (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)) : 0)
+
+/**
+ * @brief Sized-array iterator (for-loop).
+ *
+ * Use with opening curly bracket (`{`).
+ *
+ * There are 2 variants:
+ *
+ * LY_ARRAY_FOR(ARRAY, TYPE, ITER)
+ *
+ * Where ARRAY is a sized-array to go through, TYPE is the type of the items in the ARRAY and ITER is a pointer variable
+ * providing the items of the ARRAY in the loops. This functionality is provided by LY_ARRAY_FOR_ITER macro
+ *
+ * LY_ARRAY_FOR(ARRAY, INDEX)
+ *
+ * The ARRAY is again a sized-array to go through, the INDEX is a variable (LY_ARRAY_COUNT_TYPE) for storing iterating ARRAY's index
+ * to access the items of ARRAY in the loops. This functionality is provided by LY_ARRAY_FOR_INDEX macro.
+ */
+#define LY_ARRAY_FOR(ARRAY, ...) LY_ARRAY_SELECT(__VA_ARGS__, LY_ARRAY_FOR_ITER, LY_ARRAY_FOR_INDEX, LY_UNDEF)(ARRAY, __VA_ARGS__)
+
+/**
+ * @brief Macro to iterate via all sibling elements without affecting the list itself
+ *
+ * Works for all types of nodes despite it is data or schema tree, but all the
+ * parameters must be pointers to the same type.
+ *
+ * Use with opening curly bracket (`{`). All parameters must be of the same type.
+ *
+ * @param START Pointer to the starting element.
+ * @param ELEM Iterator.
+ */
+#define LY_LIST_FOR(START, ELEM) \
+ for ((ELEM) = (START); \
+ (ELEM); \
+ (ELEM) = (ELEM)->next)
+
+/**
+ * @brief Macro to iterate via all sibling elements allowing to modify the list itself (e.g. removing elements)
+ *
+ * Use with opening curly bracket (`{`). All parameters must be of the same type.
+ *
+ * @param START Pointer to the starting element.
+ * @param NEXT Temporary storage to allow removing of the current iterator content.
+ * @param ELEM Iterator.
+ */
+#define LY_LIST_FOR_SAFE(START, NEXT, ELEM) \
+ for ((ELEM) = (START); \
+ (ELEM) ? (NEXT = (ELEM)->next, 1) : 0; \
+ (ELEM) = (NEXT))
+
+/**
+ * @brief YANG built-in types
+ */
+typedef enum {
+ LY_TYPE_UNKNOWN = 0, /**< Unknown type */
+ LY_TYPE_BINARY, /**< Any binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
+ LY_TYPE_UINT8, /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_UINT16, /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_UINT32, /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_UINT64, /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_STRING, /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
+ LY_TYPE_BITS, /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
+ LY_TYPE_BOOL, /**< "true" or "false" ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
+ LY_TYPE_DEC64, /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3))*/
+ LY_TYPE_EMPTY, /**< A leaf that does not have any value ([RFC 6020 sec 9.11](http://tools.ietf.org/html/rfc6020#section-9.11)) */
+ LY_TYPE_ENUM, /**< Enumerated strings ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
+ LY_TYPE_IDENT, /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
+ LY_TYPE_INST, /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
+ LY_TYPE_LEAFREF, /**< A reference to a leaf instance ([RFC 6020 sec 9.9](http://tools.ietf.org/html/rfc6020#section-9.9))*/
+ LY_TYPE_UNION, /**< Choice of member types ([RFC 6020 sec 9.12](http://tools.ietf.org/html/rfc6020#section-9.12)) */
+ LY_TYPE_INT8, /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_INT16, /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_INT32, /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_INT64 /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+} LY_DATA_TYPE;
+#define LY_DATA_TYPE_COUNT 20 /**< Number of different types */
+
+/**
+ * @brief Stringfield YANG built-in data types
+ */
+extern const char *ly_data_type2str[LY_DATA_TYPE_COUNT];
+
+/**
+ * @brief All kinds of supported value formats and prefix mappings to modules.
+ */
+typedef enum {
+ LY_VALUE_CANON, /**< canonical value, prefix mapping is type-specific */
+ LY_VALUE_SCHEMA, /**< YANG schema value, prefixes map to YANG import prefixes */
+ LY_VALUE_SCHEMA_RESOLVED, /**< resolved YANG schema value, prefixes map to module structures directly */
+ LY_VALUE_XML, /**< XML data value, prefixes map to XML namespace prefixes */
+ LY_VALUE_JSON, /**< JSON data value, prefixes map to module names */
+ LY_VALUE_LYB, /**< LYB data binary value, prefix mapping is type-specific (but usually like JSON) */
+ LY_VALUE_STR_NS /**< any data format value, prefixes map to XML namespace prefixes */
+} LY_VALUE_FORMAT;
+
+/** @} trees */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_H_ */
diff --git a/src/tree_data.c b/src/tree_data.c
new file mode 100644
index 0000000..d6a04ff
--- /dev/null
+++ b/src/tree_data.c
@@ -0,0 +1,2943 @@
+/**
+ * @file tree_data.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Data tree functions
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "tree_data.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "diff.h"
+#include "hash_table.h"
+#include "in.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "path.h"
+#include "plugins.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_internal.h"
+#include "plugins_types.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "validation.h"
+#include "xml.h"
+#include "xpath.h"
+
+static LYD_FORMAT
+lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format)
+{
+ if (!format && (in->type == LY_IN_FILEPATH)) {
+ /* unknown format - try to detect it from filename's suffix */
+ const char *path = in->method.fpath.filepath;
+ size_t len = strlen(path);
+
+ /* ignore trailing whitespaces */
+ for ( ; len > 0 && isspace(path[len - 1]); len--) {}
+
+ if ((len >= LY_XML_SUFFIX_LEN + 1) &&
+ !strncmp(&path[len - LY_XML_SUFFIX_LEN], LY_XML_SUFFIX, LY_XML_SUFFIX_LEN)) {
+ format = LYD_XML;
+ } else if ((len >= LY_JSON_SUFFIX_LEN + 1) &&
+ !strncmp(&path[len - LY_JSON_SUFFIX_LEN], LY_JSON_SUFFIX, LY_JSON_SUFFIX_LEN)) {
+ format = LYD_JSON;
+ } else if ((len >= LY_LYB_SUFFIX_LEN + 1) &&
+ !strncmp(&path[len - LY_LYB_SUFFIX_LEN], LY_LYB_SUFFIX, LY_LYB_SUFFIX_LEN)) {
+ format = LYD_LYB;
+ } /* else still unknown */
+ }
+
+ return format;
+}
+
+/**
+ * @brief Parse YANG data into a data tree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extenion instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] parse_opts Options for parser.
+ * @param[in] val_opts Options for validation.
+ * @param[out] op Optional pointer to the parsed operation, if any.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_in *in, LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_ctx *lydctx = NULL;
+ struct ly_set parsed = {0};
+ struct lyd_node *first;
+ uint32_t i, int_opts = 0;
+ ly_bool subtree_sibling = 0;
+
+ assert(ctx && (parent || first_p));
+
+ format = lyd_parse_get_format(in, format);
+ if (first_p) {
+ *first_p = NULL;
+ }
+
+ /* remember input position */
+ in->func_start = in->current;
+
+ /* set internal options */
+ if (!(parse_opts & LYD_PARSE_SUBTREE)) {
+ int_opts = LYD_INTOPT_WITH_SIBLINGS;
+ }
+
+ /* parse the data */
+ switch (format) {
+ case LYD_XML:
+ rc = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ &subtree_sibling, &lydctx);
+ break;
+ case LYD_JSON:
+ rc = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ &subtree_sibling, &lydctx);
+ break;
+ case LYD_LYB:
+ rc = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ &subtree_sibling, &lydctx);
+ break;
+ case LYD_UNKNOWN:
+ LOGARG(ctx, format);
+ rc = LY_EINVAL;
+ break;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (parent) {
+ /* get first top-level sibling */
+ for (first = parent; first->parent; first = lyd_parent(first)) {}
+ first = lyd_first_sibling(first);
+ first_p = &first;
+ }
+
+ if (!(parse_opts & LYD_PARSE_ONLY)) {
+ /* validate data */
+ rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
+ &lydctx->ext_node, &lydctx->ext_val, NULL);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ /* set the operation node */
+ if (op) {
+ *op = lydctx->op_node;
+ }
+
+cleanup:
+ if (lydctx) {
+ lydctx->free(lydctx);
+ }
+ if (rc) {
+ if (parent) {
+ /* free all the parsed subtrees */
+ for (i = 0; i < parsed.count; ++i) {
+ lyd_free_tree(parsed.dnodes[i]);
+ }
+ } else {
+ /* free everything */
+ lyd_free_all(*first_p);
+ *first_p = NULL;
+ }
+ } else if (subtree_sibling) {
+ rc = LY_ENOT;
+ }
+ ly_set_erase(&parsed, NULL);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_ext_data(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree)
+{
+ const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, 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);
+
+ return lyd_parse(ctx, ext, parent, tree, in, format, parse_options, validate_options, NULL);
+}
+
+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, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
+
+ return lyd_parse(ctx, NULL, parent, tree, in, format, parse_options, validate_options, NULL);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options, struct lyd_node **tree)
+{
+ LY_ERR ret;
+ struct ly_in *in;
+
+ LY_CHECK_RET(ly_in_new_memory(data, &in));
+ ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
+
+ ly_in_free(in, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_data_fd(const struct ly_ctx *ctx, int fd, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options,
+ struct lyd_node **tree)
+{
+ LY_ERR ret;
+ struct ly_in *in;
+
+ LY_CHECK_RET(ly_in_new_fd(fd, &in));
+ ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
+
+ ly_in_free(in, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options, struct lyd_node **tree)
+{
+ LY_ERR ret;
+ struct ly_in *in;
+
+ LY_CHECK_RET(ly_in_new_filepath(path, 0, &in));
+ ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
+
+ ly_in_free(in, 0);
+ return ret;
+}
+
+/**
+ * @brief Parse YANG data into an operation data tree, in case the extension instance is specified, keep the searching
+ * for schema nodes locked inside the extension instance.
+ *
+ * At least one of @p parent, @p tree, or @p op must always be set.
+ *
+ * Specific @p data_type values have different parameter meaning as follows:
+ * - ::LYD_TYPE_RPC_NETCONF:
+ * - @p parent - must be NULL, the whole RPC is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_NETCONF:
+ * - @p parent - must be NULL, the whole notification is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_NETCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, the reply is appended to the RPC;
+ * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] data_type Expected operation to parse (@ref datatype).
+ * @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL.
+ * @param[out] op Optional parsed operation node.
+ * @return LY_ERR value.
+ * @return LY_ENOT if @p data_type is a NETCONF message and the root XML element is not the expected one.
+ */
+static LY_ERR
+lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct ly_in *in, LYD_FORMAT format, enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_ctx *lydctx = NULL;
+ struct ly_set parsed = {0};
+ struct lyd_node *first = NULL, *envp = NULL;
+ uint32_t i, parse_opts, val_opts, int_opts = 0;
+
+ if (!ctx) {
+ ctx = LYD_CTX(parent);
+ }
+ if (tree) {
+ *tree = NULL;
+ }
+ if (op) {
+ *op = NULL;
+ }
+
+ format = lyd_parse_get_format(in, format);
+
+ /* remember input position */
+ in->func_start = in->current;
+
+ /* set parse and validation opts */
+ parse_opts = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
+ val_opts = 0;
+
+ switch (data_type) {
+
+ /* special XML NETCONF data types */
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
+ /* fallthrough */
+ case LYD_TYPE_REPLY_NETCONF:
+ if (data_type == LYD_TYPE_REPLY_NETCONF) {
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op,
+ LY_EINVAL);
+ }
+
+ /* parse the NETCONF message */
+ rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ if (rc) {
+ if (envp) {
+ /* special situation when the envelopes were parsed successfully */
+ *tree = envp;
+ }
+ goto cleanup;
+ }
+
+ /* set out params correctly */
+ if (envp) {
+ /* special out param meaning */
+ *tree = envp;
+ } else {
+ *tree = parent ? NULL : first;
+ }
+ if (op) {
+ *op = lydctx->op_node;
+ }
+ goto cleanup;
+
+ /* set internal opts */
+ case LYD_TYPE_RPC_YANG:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_DATA_YANG:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ /* parse the data */
+ switch (format) {
+ case LYD_XML:
+ rc = lyd_parse_xml(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);
+ break;
+ case LYD_JSON:
+ rc = lyd_parse_json(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);
+ break;
+ case LYD_LYB:
+ rc = lyd_parse_lyb(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);
+ break;
+ case LYD_UNKNOWN:
+ LOGARG(ctx, format);
+ rc = LY_EINVAL;
+ break;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* set out params correctly */
+ if (tree) {
+ *tree = parent ? NULL : first;
+ }
+ if (op) {
+ *op = lydctx->op_node;
+ }
+
+cleanup:
+ if (lydctx) {
+ lydctx->free(lydctx);
+ }
+ if (rc) {
+ /* free all the parsed nodes */
+ if (parsed.count) {
+ i = parsed.count;
+ do {
+ --i;
+ lyd_free_tree(parsed.dnodes[i]);
+ } while (i);
+ }
+ if (tree && ((format != LYD_XML) || !envp)) {
+ *tree = NULL;
+ }
+ if (op) {
+ *op = NULL;
+ }
+ }
+ ly_set_erase(&parsed, NULL);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op)
+{
+ LY_CHECK_ARG_RET(ctx, ctx || parent, in, data_type, parent || tree || op, LY_EINVAL);
+
+ return lyd_parse_op_(ctx, NULL, parent, in, format, data_type, tree, op);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_ext_op(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op)
+{
+ const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, in, data_type, parent || tree || op, LY_EINVAL);
+
+ return lyd_parse_op_(ctx, ext, parent, in, format, data_type, tree, op);
+}
+
+struct lyd_node *
+lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct lyd_node *new_node)
+{
+ const struct lysc_node *schema, *sparent;
+ struct lyd_node *match = NULL;
+ ly_bool found;
+ uint32_t getnext_opts;
+
+ assert(new_node);
+
+ if (!first_sibling || !new_node->schema || (LYD_CTX(first_sibling) != LYD_CTX(new_node))) {
+ /* insert at the end, no next anchor */
+ return NULL;
+ }
+
+ getnext_opts = 0;
+ if (new_node->schema->flags & LYS_IS_OUTPUT) {
+ getnext_opts = LYS_GETNEXT_OUTPUT;
+ }
+
+ if (first_sibling->parent && first_sibling->parent->schema && first_sibling->parent->children_ht) {
+ /* find the anchor using hashes */
+ sparent = first_sibling->parent->schema;
+ schema = lys_getnext(new_node->schema, sparent, NULL, getnext_opts);
+ while (schema) {
+ /* keep trying to find the first existing instance of the closest following schema sibling,
+ * otherwise return NULL - inserting at the end */
+ if (!lyd_find_sibling_schema(first_sibling, schema, &match)) {
+ break;
+ }
+
+ schema = lys_getnext(schema, sparent, NULL, getnext_opts);
+ }
+ } else {
+ /* find the anchor without hashes */
+ match = (struct lyd_node *)first_sibling;
+ sparent = lysc_data_parent(new_node->schema);
+ if (!sparent) {
+ /* we are in top-level, skip all the data from preceding modules */
+ LY_LIST_FOR(match, match) {
+ if (!match->schema || (strcmp(lyd_owner_module(match)->name, lyd_owner_module(new_node)->name) >= 0)) {
+ break;
+ }
+ }
+ }
+
+ /* get the first schema sibling */
+ schema = lys_getnext(NULL, sparent, new_node->schema->module->compiled, getnext_opts);
+
+ found = 0;
+ LY_LIST_FOR(match, match) {
+ if (!match->schema || (lyd_owner_module(match) != lyd_owner_module(new_node))) {
+ /* we have found an opaque node, which must be at the end, so use it OR
+ * modules do not match, so we must have traversed all the data from new_node module (if any),
+ * we have found the first node of the next module, that is what we want */
+ break;
+ }
+
+ /* skip schema nodes until we find the instantiated one */
+ while (!found) {
+ if (new_node->schema == schema) {
+ /* we have found the schema of the new node, continue search to find the first
+ * data node with a different schema (after our schema) */
+ found = 1;
+ break;
+ }
+ if (match->schema == schema) {
+ /* current node (match) is a data node still before the new node, continue search in data */
+ break;
+ }
+ schema = lys_getnext(schema, sparent, new_node->schema->module->compiled, getnext_opts);
+ assert(schema);
+ }
+
+ if (found && (match->schema != new_node->schema)) {
+ /* find the next node after we have found our node schema data instance */
+ break;
+ }
+ }
+ }
+
+ return match;
+}
+
+void
+lyd_insert_after_node(struct lyd_node *sibling, struct lyd_node *node)
+{
+ struct lyd_node_inner *par;
+
+ assert(!node->next && (node->prev == node));
+
+ node->next = sibling->next;
+ node->prev = sibling;
+ sibling->next = node;
+ if (node->next) {
+ /* sibling had a succeeding node */
+ node->next->prev = node;
+ } else {
+ /* sibling was last, find first sibling and change its prev */
+ if (sibling->parent) {
+ sibling = sibling->parent->child;
+ } else {
+ for ( ; sibling->prev->next != node; sibling = sibling->prev) {}
+ }
+ sibling->prev = node;
+ }
+ node->parent = sibling->parent;
+
+ for (par = node->parent; par; par = par->parent) {
+ if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
+ /* remove default flags from NP containers */
+ par->flags &= ~LYD_DEFAULT;
+ }
+ }
+}
+
+void
+lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node)
+{
+ struct lyd_node_inner *par;
+
+ assert(!node->next && (node->prev == node));
+
+ node->next = sibling;
+ /* covers situation of sibling being first */
+ node->prev = sibling->prev;
+ sibling->prev = node;
+ if (node->prev->next) {
+ /* sibling had a preceding node */
+ node->prev->next = node;
+ } else if (sibling->parent) {
+ /* sibling was first and we must also change parent child pointer */
+ sibling->parent->child = node;
+ }
+ node->parent = sibling->parent;
+
+ for (par = node->parent; par; par = par->parent) {
+ if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
+ /* remove default flags from NP containers */
+ par->flags &= ~LYD_DEFAULT;
+ }
+ }
+}
+
+/**
+ * @brief Insert node as the first and only child of a parent.
+ *
+ * Handles inserting into NP containers and key-less lists.
+ *
+ * @param[in] parent Parent to insert into.
+ * @param[in] node Node to insert.
+ */
+static void
+lyd_insert_only_child(struct lyd_node *parent, struct lyd_node *node)
+{
+ struct lyd_node_inner *par;
+
+ assert(parent && !lyd_child(parent) && !node->next && (node->prev == node));
+ assert(!parent->schema || (parent->schema->nodetype & LYD_NODE_INNER));
+
+ par = (struct lyd_node_inner *)parent;
+
+ par->child = node;
+ node->parent = par;
+
+ for ( ; par; par = par->parent) {
+ if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
+ /* remove default flags from NP containers */
+ par->flags &= ~LYD_DEFAULT;
+ }
+ }
+}
+
+/**
+ * @brief Learn whether a list instance has all the keys.
+ *
+ * @param[in] list List instance to check.
+ * @return non-zero if all the keys were found,
+ * @return 0 otherwise.
+ */
+static int
+lyd_insert_has_keys(const struct lyd_node *list)
+{
+ const struct lyd_node *key;
+ const struct lysc_node *skey = NULL;
+
+ assert(list->schema->nodetype == LYS_LIST);
+ key = lyd_child(list);
+ while ((skey = lys_getnext(skey, list->schema, NULL, 0)) && (skey->flags & LYS_KEY)) {
+ if (!key || (key->schema != skey)) {
+ /* key missing */
+ return 0;
+ }
+
+ key = key->next;
+ }
+
+ /* all keys found */
+ return 1;
+}
+
+void
+lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, struct lyd_node *node, ly_bool last)
+{
+ struct lyd_node *anchor, *first_sibling;
+
+ /* inserting list without its keys is not supported */
+ assert((parent || first_sibling_p) && node && (node->hash || !node->schema));
+ assert(!parent || !parent->schema ||
+ (parent->schema->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_RPC | LYS_ACTION | LYS_NOTIF)));
+
+ if (!parent && first_sibling_p && (*first_sibling_p) && (*first_sibling_p)->parent) {
+ parent = lyd_parent(*first_sibling_p);
+ }
+
+ /* get first sibling */
+ first_sibling = parent ? lyd_child(parent) : *first_sibling_p;
+
+ if (last || (first_sibling && (first_sibling->flags & LYD_EXT))) {
+ /* no next anchor */
+ anchor = NULL;
+ } else {
+ /* find the anchor, our next node, so we can insert before it */
+ anchor = lyd_insert_get_next_anchor(first_sibling, node);
+ }
+
+ if (anchor) {
+ /* insert before the anchor */
+ lyd_insert_before_node(anchor, node);
+ if (!parent && (*first_sibling_p == anchor)) {
+ /* move first sibling */
+ *first_sibling_p = node;
+ }
+ } else if (first_sibling) {
+ /* insert as the last node */
+ lyd_insert_after_node(first_sibling->prev, node);
+ } else if (parent) {
+ /* insert as the only child */
+ lyd_insert_only_child(parent, node);
+ } else {
+ /* insert as the only sibling */
+ *first_sibling_p = node;
+ }
+
+ /* insert into parent HT */
+ lyd_insert_hash(node);
+
+ /* finish hashes for our parent, if needed and possible */
+ if (node->schema && (node->schema->flags & LYS_KEY) && parent && lyd_insert_has_keys(parent)) {
+ lyd_hash(parent);
+
+ /* now we can insert even the list into its parent HT */
+ lyd_insert_hash(parent);
+ }
+}
+
+/**
+ * @brief Check schema place of a node to be inserted.
+ *
+ * @param[in] parent Schema node of the parent data node.
+ * @param[in] sibling Schema node of a sibling data node.
+ * @param[in] schema Schema node if the data node to be inserted.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINVAL if the place is invalid.
+ */
+static LY_ERR
+lyd_insert_check_schema(const struct lysc_node *parent, const struct lysc_node *sibling, const struct lysc_node *schema)
+{
+ const struct lysc_node *par2;
+
+ assert(!parent || !(parent->nodetype & (LYS_CASE | LYS_CHOICE)));
+ assert(!sibling || !(sibling->nodetype & (LYS_CASE | LYS_CHOICE)));
+ assert(!schema || !(schema->nodetype & (LYS_CASE | LYS_CHOICE)));
+
+ if (!schema || (!parent && !sibling)) {
+ /* opaque nodes can be inserted wherever */
+ return LY_SUCCESS;
+ }
+
+ if (!parent) {
+ parent = lysc_data_parent(sibling);
+ }
+
+ /* find schema parent */
+ par2 = lysc_data_parent(schema);
+
+ if (parent) {
+ /* inner node */
+ if (par2 != parent) {
+ LOGERR(schema->module->ctx, LY_EINVAL, "Cannot insert, parent of \"%s\" is not \"%s\".", schema->name,
+ parent->name);
+ return LY_EINVAL;
+ }
+ } else {
+ /* top-level node */
+ if (par2) {
+ LOGERR(schema->module->ctx, LY_EINVAL, "Cannot insert, node \"%s\" is not top-level.", schema->name);
+ return LY_EINVAL;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_insert_child(struct lyd_node *parent, struct lyd_node *node)
+{
+ struct lyd_node *iter;
+
+ 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_RET(lyd_insert_check_schema(parent->schema, NULL, node->schema));
+
+ if (node->schema && (node->schema->flags & LYS_KEY)) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+ return LY_EINVAL;
+ }
+
+ if (node->parent || node->prev->next) {
+ lyd_unlink_tree(node);
+ }
+
+ while (node) {
+ iter = node->next;
+ lyd_unlink_tree(node);
+ lyd_insert_node(parent, NULL, node, 0);
+ node = iter;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_ext_insert(struct lyd_node *parent, struct lyd_node *first)
+{
+ struct lyd_node *iter;
+
+ LY_CHECK_ARG_RET(NULL, parent, first, !first->parent, !first->prev->next,
+ !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL);
+
+ if (first->schema && (first->schema->flags & LYS_KEY)) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", first->schema->name);
+ return LY_EINVAL;
+ }
+
+ while (first) {
+ iter = first->next;
+ lyd_unlink_tree(first);
+ lyd_insert_node(parent, NULL, first, 1);
+ first = iter;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first)
+{
+ struct lyd_node *iter;
+
+ LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
+
+ if (sibling) {
+ LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema));
+ }
+
+ if (sibling == node) {
+ /* we need to keep the connection to siblings so we can insert into them */
+ sibling = sibling->prev;
+ }
+
+ if (node->parent || node->prev->next) {
+ lyd_unlink_tree(node);
+ }
+
+ while (node) {
+ if (lysc_is_key(node->schema)) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+ return LY_EINVAL;
+ }
+
+ iter = node->next;
+ lyd_unlink_tree(node);
+ lyd_insert_node(NULL, &sibling, node, 0);
+ node = iter;
+ }
+
+ if (first) {
+ /* find the first sibling */
+ *first = sibling;
+ while ((*first)->prev->next) {
+ *first = (*first)->prev;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+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_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema));
+
+ if (node->schema && (!(node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(node->schema->flags & LYS_ORDBY_USER))) {
+ LOGERR(LYD_CTX(sibling), LY_EINVAL, "Can be used only for user-ordered nodes.");
+ return LY_EINVAL;
+ }
+ if (node->schema && sibling->schema && (node->schema != sibling->schema)) {
+ LOGERR(LYD_CTX(sibling), LY_EINVAL, "Cannot insert before a different schema node instance.");
+ return LY_EINVAL;
+ }
+
+ lyd_unlink_tree(node);
+ lyd_insert_before_node(sibling, node);
+ lyd_insert_hash(node);
+
+ return LY_SUCCESS;
+}
+
+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_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema));
+
+ if (node->schema && (!(node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(node->schema->flags & LYS_ORDBY_USER))) {
+ LOGERR(LYD_CTX(sibling), LY_EINVAL, "Can be used only for user-ordered nodes.");
+ return LY_EINVAL;
+ }
+ if (node->schema && sibling->schema && (node->schema != sibling->schema)) {
+ LOGERR(LYD_CTX(sibling), LY_EINVAL, "Cannot insert after a different schema node instance.");
+ return LY_EINVAL;
+ }
+
+ lyd_unlink_tree(node);
+ lyd_insert_after_node(sibling, node);
+ lyd_insert_hash(node);
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF void
+lyd_unlink_siblings(struct lyd_node *node)
+{
+ struct lyd_node *next, *elem, *first = NULL;
+
+ LY_LIST_FOR_SAFE(node, next, elem) {
+ lyd_unlink_tree(elem);
+ lyd_insert_node(NULL, &first, elem, 1);
+ }
+}
+
+LIBYANG_API_DEF void
+lyd_unlink_tree(struct lyd_node *node)
+{
+ struct lyd_node *iter;
+
+ if (!node) {
+ return;
+ }
+
+ /* update hashes while still linked into the tree */
+ lyd_unlink_hash(node);
+
+ /* unlink from siblings */
+ if (node->prev->next) {
+ node->prev->next = node->next;
+ }
+ if (node->next) {
+ node->next->prev = node->prev;
+ } else {
+ /* unlinking the last node */
+ if (node->parent) {
+ iter = node->parent->child;
+ } else {
+ iter = node->prev;
+ while (iter->prev != node) {
+ iter = iter->prev;
+ }
+ }
+ /* update the "last" pointer from the first node */
+ iter->prev = node->prev;
+ }
+
+ /* unlink from parent */
+ if (node->parent) {
+ if (node->parent->child == node) {
+ /* the node is the first child */
+ node->parent->child = node->next;
+ }
+
+ /* check for NP container whether its last non-default node is not being unlinked */
+ lyd_cont_set_dflt(lyd_parent(node));
+
+ node->parent = NULL;
+ }
+
+ node->next = NULL;
+ node->prev = node;
+}
+
+void
+lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt)
+{
+ struct lyd_meta *last, *iter;
+
+ assert(parent);
+
+ if (!meta) {
+ return;
+ }
+
+ for (iter = meta; iter; iter = iter->next) {
+ iter->parent = parent;
+ }
+
+ /* insert as the last attribute */
+ if (parent->meta) {
+ for (last = parent->meta; last->next; last = last->next) {}
+ last->next = meta;
+ } else {
+ parent->meta = meta;
+ }
+
+ /* remove default flags from NP containers */
+ while (clear_dflt && parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
+ parent->flags &= ~LYD_DEFAULT;
+ parent = lyd_parent(parent);
+ }
+}
+
+LY_ERR
+lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
+ size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_ext_instance *ant = NULL;
+ const struct lysc_type *ant_type;
+ struct lyd_meta *mt, *last;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert((parent || meta) && mod);
+
+ LY_ARRAY_FOR(mod->compiled->exts, u) {
+ if (!strncmp(mod->compiled->exts[u].def->plugin->id, "ly2 metadata", 12) &&
+ !ly_strncmp(mod->compiled->exts[u].argument, name, name_len)) {
+ /* we have the annotation definition */
+ ant = &mod->compiled->exts[u];
+ break;
+ }
+ }
+ if (!ant) {
+ /* attribute is not defined as a metadata annotation (RFC 7952) */
+ LOGVAL(mod->ctx, LYVE_REFERENCE, "Annotation definition for attribute \"%s:%.*s\" not found.",
+ mod->name, (int)name_len, name);
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+
+ mt = calloc(1, sizeof *mt);
+ LY_CHECK_ERR_GOTO(!mt, LOGMEM(mod->ctx); ret = LY_EMEM, cleanup);
+ mt->parent = parent;
+ mt->annotation = ant;
+ lyplg_ext_get_storage(ant, LY_STMT_TYPE, sizeof ant_type, (const void **)&ant_type);
+ ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, dynamic, format, prefix_data, hints,
+ ctx_node, incomplete);
+ LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
+ ret = lydict_insert(mod->ctx, name, name_len, &mt->name);
+ LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
+
+ /* insert as the last attribute */
+ if (parent) {
+ lyd_insert_meta(parent, mt, clear_dflt);
+ } else if (*meta) {
+ for (last = *meta; last->next; last = last->next) {}
+ last->next = mt;
+ }
+
+ if (meta) {
+ *meta = mt;
+ }
+
+cleanup:
+ return ret;
+}
+
+void
+lyd_insert_attr(struct lyd_node *parent, struct lyd_attr *attr)
+{
+ struct lyd_attr *last, *iter;
+ struct lyd_node_opaq *opaq;
+
+ assert(parent && !parent->schema);
+
+ if (!attr) {
+ return;
+ }
+
+ opaq = (struct lyd_node_opaq *)parent;
+ for (iter = attr; iter; iter = iter->next) {
+ iter->parent = opaq;
+ }
+
+ /* insert as the last attribute */
+ if (opaq->attr) {
+ for (last = opaq->attr; last->next; last = last->next) {}
+ last->next = attr;
+ } else {
+ opaq->attr = attr;
+ }
+}
+
+LY_ERR
+lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name, size_t name_len,
+ const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len, const char *value,
+ size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_attr *at, *last;
+
+ assert(ctx && (parent || attr) && (!parent || !parent->schema));
+ assert(name && name_len && format);
+
+ if (!value_len && (!dynamic || !*dynamic)) {
+ value = "";
+ }
+
+ at = calloc(1, sizeof *at);
+ LY_CHECK_ERR_RET(!at, LOGMEM(ctx); ly_free_prefix_data(format, val_prefix_data), LY_EMEM);
+
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, name, name_len, &at->name.name), finish);
+ if (prefix_len) {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, prefix, prefix_len, &at->name.prefix), finish);
+ }
+ if (module_key_len) {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, module_key, module_key_len, &at->name.module_ns), finish);
+ }
+
+ if (dynamic && *dynamic) {
+ ret = lydict_insert_zc(ctx, (char *)value, &at->value);
+ LY_CHECK_GOTO(ret, finish);
+ *dynamic = 0;
+ } else {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, value, value_len, &at->value), finish);
+ }
+ at->format = format;
+ at->val_prefix_data = val_prefix_data;
+ at->hints = hints;
+
+ /* insert as the last attribute */
+ if (parent) {
+ lyd_insert_attr(parent, at);
+ } else if (*attr) {
+ for (last = *attr; last->next; last = last->next) {}
+ last->next = at;
+ }
+
+finish:
+ if (ret) {
+ lyd_free_attr_single(ctx, at);
+ } else if (attr) {
+ *attr = at;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const struct lyd_node_term *
+lyd_target(const struct ly_path *path, const struct lyd_node *tree)
+{
+ struct lyd_node *target;
+
+ if (ly_path_eval(path, tree, &target)) {
+ return NULL;
+ }
+
+ return (struct lyd_node_term *)target;
+}
+
+/**
+ * @brief Check the equality of the two schemas from different contexts.
+ *
+ * @param schema1 of first node.
+ * @param schema2 of second node.
+ * @return 1 if the schemas are equal otherwise 0.
+ */
+static ly_bool
+lyd_compare_schema_equal(const struct lysc_node *schema1, const struct lysc_node *schema2)
+{
+ if (!schema1 && !schema2) {
+ return 1;
+ } else if (!schema1 || !schema2) {
+ return 0;
+ }
+
+ assert(schema1->module->ctx != schema2->module->ctx);
+
+ if (schema1->nodetype != schema2->nodetype) {
+ return 0;
+ }
+
+ if (strcmp(schema1->name, schema2->name)) {
+ return 0;
+ }
+
+ if (strcmp(schema1->module->name, schema2->module->name)) {
+ return 0;
+ }
+
+ if (schema1->module->revision || schema2->module->revision) {
+ if (!schema1->module->revision || !schema2->module->revision) {
+ return 0;
+ }
+ if (strcmp(schema1->module->revision, schema2->module->revision)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Check the equality of the schemas for all parent nodes.
+ *
+ * Both nodes must be from different contexts.
+ *
+ * @param node1 Data of first node.
+ * @param node2 Data of second node.
+ * @return 1 if the all related parental schemas are equal otherwise 0.
+ */
+static ly_bool
+lyd_compare_schema_parents_equal(const struct lyd_node *node1, const struct lyd_node *node2)
+{
+ const struct lysc_node *parent1, *parent2;
+
+ assert(node1 && node2);
+
+ for (parent1 = node1->schema->parent, parent2 = node2->schema->parent;
+ parent1 && parent2;
+ parent1 = parent1->parent, parent2 = parent2->parent) {
+ if (!lyd_compare_schema_equal(parent1, parent2)) {
+ return 0;
+ }
+ }
+
+ if (parent1 || parent2) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Compare 2 nodes values including opaque node values.
+ *
+ * @param[in] node1 First node to compare.
+ * @param[in] node2 Second node to compare.
+ * @return LY_SUCCESS if equal.
+ * @return LY_ENOT if not equal.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyd_compare_single_value(const struct lyd_node *node1, const struct lyd_node *node2)
+{
+ const struct lyd_node_opaq *opaq1 = NULL, *opaq2 = NULL;
+ const char *val1, *val2, *col;
+ const struct lys_module *mod;
+ char *val_dyn = NULL;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (!node1->schema) {
+ opaq1 = (struct lyd_node_opaq *)node1;
+ }
+ if (!node2->schema) {
+ opaq2 = (struct lyd_node_opaq *)node2;
+ }
+
+ if (opaq1 && opaq2 && (opaq1->format == LY_VALUE_XML) && (opaq2->format == LY_VALUE_XML)) {
+ /* opaque XML and opaque XML node */
+ if (lyxml_value_compare(LYD_CTX(node1), opaq1->value, opaq1->val_prefix_data, LYD_CTX(node2), opaq2->value,
+ opaq2->val_prefix_data)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+ }
+
+ /* get their values */
+ if (opaq1 && ((opaq1->format == LY_VALUE_XML) || (opaq1->format == LY_VALUE_STR_NS)) && (col = strchr(opaq1->value, ':'))) {
+ /* XML value with a prefix, try to transform it into a JSON (canonical) value */
+ mod = ly_resolve_prefix(LYD_CTX(node1), opaq1->value, col - opaq1->value, opaq1->format, opaq1->val_prefix_data);
+ if (!mod) {
+ /* unable to compare */
+ return LY_ENOT;
+ }
+
+ if (asprintf(&val_dyn, "%s%s", mod->name, col) == -1) {
+ LOGMEM(LYD_CTX(node1));
+ return LY_EMEM;
+ }
+ val1 = val_dyn;
+ } else {
+ val1 = lyd_get_value(node1);
+ }
+ if (opaq2 && ((opaq2->format == LY_VALUE_XML) || (opaq2->format == LY_VALUE_STR_NS)) && (col = strchr(opaq2->value, ':'))) {
+ mod = ly_resolve_prefix(LYD_CTX(node2), opaq2->value, col - opaq2->value, opaq2->format, opaq2->val_prefix_data);
+ if (!mod) {
+ return LY_ENOT;
+ }
+
+ assert(!val_dyn);
+ if (asprintf(&val_dyn, "%s%s", mod->name, col) == -1) {
+ LOGMEM(LYD_CTX(node2));
+ return LY_EMEM;
+ }
+ val2 = val_dyn;
+ } else {
+ val2 = lyd_get_value(node2);
+ }
+
+ /* compare values */
+ if (strcmp(val1, val2)) {
+ rc = LY_ENOT;
+ }
+
+ free(val_dyn);
+ return rc;
+}
+
+/**
+ * @brief Internal implementation of @ref lyd_compare_single.
+ * @copydoc lyd_compare_single
+ * @param[in] parental_schemas_checked Flag used for optimization.
+ * When this function is called for the first time, the flag must be set to 0.
+ * The @ref lyd_compare_schema_parents_equal should be called only once during
+ * recursive calls, and this is accomplished by setting to 1 in the lyd_compare_single_ body.
+ */
+static LY_ERR
+lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked)
+{
+ const struct lyd_node *iter1, *iter2;
+ struct lyd_node_any *any1, *any2;
+ int len1, len2;
+ LY_ERR r;
+
+ if (!node1 || !node2) {
+ if (node1 == node2) {
+ return LY_SUCCESS;
+ } else {
+ return LY_ENOT;
+ }
+ }
+
+ if (LYD_CTX(node1) == LYD_CTX(node2)) {
+ /* same contexts */
+ if (options & LYD_COMPARE_OPAQ) {
+ if (lyd_node_schema(node1) != lyd_node_schema(node2)) {
+ return LY_ENOT;
+ }
+ } else {
+ if (node1->schema != node2->schema) {
+ return LY_ENOT;
+ }
+ }
+ } else {
+ /* different contexts */
+ if (!lyd_compare_schema_equal(node1->schema, node2->schema)) {
+ return LY_ENOT;
+ }
+ if (!parental_schemas_checked) {
+ if (!lyd_compare_schema_parents_equal(node1, node2)) {
+ return LY_ENOT;
+ }
+ parental_schemas_checked = 1;
+ }
+ }
+
+ if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) {
+ return LY_ENOT;
+ }
+ /* equal hashes do not mean equal nodes, they can be just in collision so the nodes must be checked explicitly */
+
+ if (!node1->schema || !node2->schema) {
+ if (!(options & LYD_COMPARE_OPAQ) && ((node1->schema && !node2->schema) || (!node1->schema && node2->schema))) {
+ return LY_ENOT;
+ }
+ if ((r = lyd_compare_single_value(node1, node2))) {
+ return r;
+ }
+
+ if (options & LYD_COMPARE_FULL_RECURSION) {
+ iter1 = lyd_child(node1);
+ iter2 = lyd_child(node2);
+ goto all_children_compare;
+ }
+ return LY_SUCCESS;
+ } else {
+ switch (node1->schema->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ if (options & LYD_COMPARE_DEFAULTS) {
+ if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
+ return LY_ENOT;
+ }
+ }
+ if ((r = lyd_compare_single_value(node1, node2))) {
+ return r;
+ }
+
+ return LY_SUCCESS;
+ case LYS_CONTAINER:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ if (options & LYD_COMPARE_DEFAULTS) {
+ if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
+ return LY_ENOT;
+ }
+ }
+ if (options & LYD_COMPARE_FULL_RECURSION) {
+ iter1 = lyd_child(node1);
+ iter2 = lyd_child(node2);
+ goto all_children_compare;
+ }
+ return LY_SUCCESS;
+ case LYS_LIST:
+ iter1 = lyd_child(node1);
+ iter2 = lyd_child(node2);
+
+ if (!(node1->schema->flags & LYS_KEYLESS) && !(options & LYD_COMPARE_FULL_RECURSION)) {
+ /* lists with keys, their equivalence is based on their keys */
+ for (const struct lysc_node *key = lysc_node_child(node1->schema);
+ key && (key->flags & LYS_KEY);
+ key = key->next) {
+ if (lyd_compare_single_(iter1, iter2, options, parental_schemas_checked)) {
+ return LY_ENOT;
+ }
+ iter1 = iter1->next;
+ iter2 = iter2->next;
+ }
+ } else {
+ /* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */
+
+all_children_compare:
+ if (!iter1 && !iter2) {
+ /* no children, nothing to compare */
+ return LY_SUCCESS;
+ }
+
+ for ( ; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) {
+ if (lyd_compare_single_(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION, parental_schemas_checked)) {
+ return LY_ENOT;
+ }
+ }
+ if (iter1 || iter2) {
+ return LY_ENOT;
+ }
+ }
+ return LY_SUCCESS;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ any1 = (struct lyd_node_any *)node1;
+ any2 = (struct lyd_node_any *)node2;
+
+ if (any1->value_type != any2->value_type) {
+ return LY_ENOT;
+ }
+ switch (any1->value_type) {
+ case LYD_ANYDATA_DATATREE:
+ iter1 = any1->value.tree;
+ iter2 = any2->value.tree;
+ goto all_children_compare;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ if ((!any1->value.str && any2->value.str) || (any1->value.str && !any2->value.str)) {
+ return LY_ENOT;
+ } else if (!any1->value.str && !any2->value.str) {
+ return LY_SUCCESS;
+ }
+ len1 = strlen(any1->value.str);
+ len2 = strlen(any2->value.str);
+ if ((len1 != len2) || strcmp(any1->value.str, any2->value.str)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+ case LYD_ANYDATA_LYB:
+ len1 = lyd_lyb_data_length(any1->value.mem);
+ len2 = lyd_lyb_data_length(any2->value.mem);
+ if ((len1 == -1) || (len2 == -1) || (len1 != len2) || memcmp(any1->value.mem, any2->value.mem, len1)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ LOGINT(LYD_CTX(node1));
+ return LY_EINT;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ return lyd_compare_single_(node1, node2, options, 0);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ for ( ; node1 && node2; node1 = node1->next, node2 = node2->next) {
+ LY_CHECK_RET(lyd_compare_single(node1, node2, options));
+ }
+
+ if (node1 == node2) {
+ return LY_SUCCESS;
+ }
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_compare_meta(const struct lyd_meta *meta1, const struct lyd_meta *meta2)
+{
+ if (!meta1 || !meta2) {
+ if (meta1 == meta2) {
+ return LY_SUCCESS;
+ } else {
+ return LY_ENOT;
+ }
+ }
+
+ if ((meta1->annotation->module->ctx != meta2->annotation->module->ctx) || (meta1->annotation != meta2->annotation)) {
+ return LY_ENOT;
+ }
+
+ return meta1->value.realtype->plugin->compare(&meta1->value, &meta2->value);
+}
+
+/**
+ * @brief Create a copy of the attribute.
+ *
+ * @param[in] attr Attribute to copy.
+ * @param[in] node Opaque where to append the new attribute.
+ * @param[out] dup Optional created attribute copy.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_dup_attr_single(const struct lyd_attr *attr, struct lyd_node *node, struct lyd_attr **dup)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_attr *a, *last;
+ struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)node;
+
+ LY_CHECK_ARG_RET(NULL, attr, node, !node->schema, LY_EINVAL);
+
+ /* create a copy */
+ a = calloc(1, sizeof *attr);
+ LY_CHECK_ERR_RET(!a, LOGMEM(LYD_CTX(node)), LY_EMEM);
+
+ LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.name, 0, &a->name.name), finish);
+ LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.prefix, 0, &a->name.prefix), finish);
+ LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.module_ns, 0, &a->name.module_ns), finish);
+ LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->value, 0, &a->value), finish);
+ a->hints = attr->hints;
+ a->format = attr->format;
+ if (attr->val_prefix_data) {
+ ret = ly_dup_prefix_data(LYD_CTX(node), attr->format, attr->val_prefix_data, &a->val_prefix_data);
+ LY_CHECK_GOTO(ret, finish);
+ }
+
+ /* insert as the last attribute */
+ a->parent = opaq;
+ if (opaq->attr) {
+ for (last = opaq->attr; last->next; last = last->next) {}
+ last->next = a;
+ } else {
+ opaq->attr = a;
+ }
+
+finish:
+ if (ret) {
+ lyd_free_attr_single(LYD_CTX(node), a);
+ } else if (dup) {
+ *dup = a;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Find @p schema equivalent in @p trg_ctx.
+ *
+ * @param[in] schema Schema node to find.
+ * @param[in] trg_ctx Target context to search in.
+ * @param[in] parent Data parent of @p schema, if any.
+ * @param[in] log Whether to log directly.
+ * @param[out] trg_schema Found schema from @p trg_ctx to use.
+ * @return LY_RRR value.
+ */
+static LY_ERR
+lyd_find_schema_ctx(const struct lysc_node *schema, const struct ly_ctx *trg_ctx, const struct lyd_node *parent,
+ ly_bool log, const struct lysc_node **trg_schema)
+{
+ const struct lysc_node *src_parent = NULL, *trg_parent = NULL, *sp, *tp;
+ const struct lys_module *trg_mod = NULL;
+ char *path;
+
+ if (!schema) {
+ /* opaque node */
+ *trg_schema = NULL;
+ return LY_SUCCESS;
+ }
+
+ if (lysc_data_parent(schema) && parent && parent->schema) {
+ /* start from schema parent */
+ trg_parent = parent->schema;
+ src_parent = lysc_data_parent(schema);
+ }
+
+ do {
+ /* find the next parent */
+ sp = schema;
+ while (lysc_data_parent(sp) != src_parent) {
+ sp = lysc_data_parent(sp);
+ }
+ src_parent = sp;
+
+ if (!src_parent->parent) {
+ /* find the module first */
+ trg_mod = ly_ctx_get_module_implemented(trg_ctx, src_parent->module->name);
+ if (!trg_mod) {
+ if (log) {
+ LOGERR(trg_ctx, LY_ENOTFOUND, "Module \"%s\" not present/implemented in the target context.",
+ src_parent->module->name);
+ }
+ return LY_ENOTFOUND;
+ }
+ }
+
+ /* find the next parent */
+ assert(trg_parent || trg_mod);
+ tp = NULL;
+ while ((tp = lys_getnext(tp, trg_parent, trg_mod ? trg_mod->compiled : NULL, 0))) {
+ if (!strcmp(tp->name, src_parent->name) && !strcmp(tp->module->name, src_parent->module->name)) {
+ break;
+ }
+ }
+ if (!tp) {
+ /* schema node not found */
+ if (log) {
+ path = lysc_path(src_parent, LYSC_PATH_LOG, NULL, 0);
+ LOGERR(trg_ctx, LY_ENOTFOUND, "Schema node \"%s\" not found in the target context.", path);
+ free(path);
+ }
+ return LY_ENOTFOUND;
+ }
+
+ trg_parent = tp;
+ } while (schema != src_parent);
+
+ /* success */
+ *trg_schema = trg_parent;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Duplicate a single node and connect it into @p parent (if present) or last of @p first siblings.
+ *
+ * Ignores ::LYD_DUP_WITH_PARENTS and ::LYD_DUP_WITH_SIBLINGS which are supposed to be handled by lyd_dup().
+ *
+ * @param[in] node Node to duplicate.
+ * @param[in] trg_ctx Target context for duplicated nodes.
+ * @param[in] parent Parent to insert into, NULL for top-level sibling.
+ * @param[in] insert_last Whether the duplicated node can be inserted as the last child of @p parent. Set for
+ * recursive duplication as an optimization.
+ * @param[in,out] first First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set.
+ * @param[in] options Bitmask of options flags, see @ref dupoptions.
+ * @param[out] dup_p Pointer where the created duplicated node is placed (besides connecting it to @p parent / @p first).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, ly_bool insert_last,
+ struct lyd_node **first, uint32_t options, struct lyd_node **dup_p)
+{
+ LY_ERR ret;
+ struct lyd_node *dup = NULL;
+ struct lyd_meta *meta;
+ struct lyd_attr *attr;
+ struct lyd_node_any *any;
+ const struct lysc_type *type;
+ const char *val_can;
+
+ LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
+
+ if (node->flags & LYD_EXT) {
+ if (options & LYD_DUP_NO_EXT) {
+ /* no not duplicate this subtree */
+ return LY_SUCCESS;
+ }
+
+ /* we need to use the same context */
+ trg_ctx = LYD_CTX(node);
+ }
+
+ if (!node->schema) {
+ dup = calloc(1, sizeof(struct lyd_node_opaq));
+ ((struct lyd_node_opaq *)dup)->ctx = trg_ctx;
+ } else {
+ switch (node->schema->nodetype) {
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ dup = calloc(1, sizeof(struct lyd_node_inner));
+ break;
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ dup = calloc(1, sizeof(struct lyd_node_term));
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ dup = calloc(1, sizeof(struct lyd_node_any));
+ break;
+ default:
+ LOGINT(trg_ctx);
+ ret = LY_EINT;
+ goto error;
+ }
+ }
+ LY_CHECK_ERR_GOTO(!dup, LOGMEM(trg_ctx); ret = LY_EMEM, error);
+
+ if (options & LYD_DUP_WITH_FLAGS) {
+ dup->flags = node->flags;
+ } else {
+ dup->flags = (node->flags & (LYD_DEFAULT | LYD_EXT)) | LYD_NEW;
+ }
+ 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) {
+ /* has no schema but is not an opaque node */
+ free(dup);
+ dup = NULL;
+ goto error;
+ }
+ }
+ dup->prev = dup;
+
+ /* duplicate metadata/attributes */
+ 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);
+ }
+ } else {
+ LY_LIST_FOR(node->meta, meta) {
+ LY_CHECK_GOTO(ret = lyd_dup_meta_single(meta, dup, NULL), error);
+ }
+ }
+ }
+
+ /* nodetype-specific work */
+ if (!dup->schema) {
+ struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)dup;
+ struct lyd_node_opaq *orig = (struct lyd_node_opaq *)node;
+ struct lyd_node *child;
+
+ 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, 1, NULL, options, NULL), error);
+ }
+ }
+ 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);
+ 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);
+ }
+ } else if (dup->schema->nodetype & LYD_NODE_TERM) {
+ struct lyd_node_term *term = (struct lyd_node_term *)dup;
+ struct lyd_node_term *orig = (struct lyd_node_term *)node;
+
+ 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);
+ } 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), NULL, LY_VALUE_CANON, NULL,
+ LYD_HINT_DATA, term->schema, NULL);
+ LY_CHECK_GOTO(ret, error);
+ }
+ } else if (dup->schema->nodetype & LYD_NODE_INNER) {
+ struct lyd_node_inner *orig = (struct lyd_node_inner *)node;
+ struct lyd_node *child;
+
+ 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, 1, NULL, options, NULL), error);
+ }
+ } 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, 1, NULL, options, NULL), error);
+ }
+ }
+ 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);
+ }
+
+ /* insert */
+ lyd_insert_node(parent, first, dup, insert_last);
+
+ if (dup_p) {
+ *dup_p = dup;
+ }
+ return LY_SUCCESS;
+
+error:
+ lyd_free_tree(dup);
+ return ret;
+}
+
+/**
+ * @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] 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.
+ * @param[out] local_parent Correct parent to directly connect duplicated @p node to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, const struct lyd_node_inner *parent,
+ uint32_t options, struct lyd_node **dup_parent, struct lyd_node_inner **local_parent)
+{
+ const struct lyd_node_inner *orig_parent, *iter;
+ ly_bool repeat = 1, ext_parent = 0;
+
+ *dup_parent = NULL;
+ *local_parent = NULL;
+
+ if (node->flags & LYD_EXT) {
+ ext_parent = 1;
+ }
+ for (orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) {
+ if (ext_parent) {
+ /* use the standard context */
+ trg_ctx = LYD_CTX(orig_parent);
+ }
+ if (parent && (parent->schema == orig_parent->schema)) {
+ /* stop creating parents, connect what we have into the provided parent */
+ iter = parent;
+ repeat = 0;
+ } else {
+ iter = NULL;
+ LY_CHECK_RET(lyd_dup_r((struct lyd_node *)orig_parent, trg_ctx, NULL, 0, (struct lyd_node **)&iter, options,
+ (struct lyd_node **)&iter));
+ }
+ if (!*local_parent) {
+ *local_parent = (struct lyd_node_inner *)iter;
+ }
+ if (iter->child) {
+ /* 1) list - add after keys
+ * 2) provided parent with some children */
+ iter->child->prev->next = *dup_parent;
+ if (*dup_parent) {
+ (*dup_parent)->prev = iter->child->prev;
+ iter->child->prev = *dup_parent;
+ }
+ } else {
+ ((struct lyd_node_inner *)iter)->child = *dup_parent;
+ }
+ if (*dup_parent) {
+ (*dup_parent)->parent = (struct lyd_node_inner *)iter;
+ }
+ *dup_parent = (struct lyd_node *)iter;
+ if (orig_parent->flags & LYD_EXT) {
+ ext_parent = 1;
+ }
+ }
+
+ if (repeat && parent) {
+ /* given parent and created parents chain actually do not interconnect */
+ LOGERR(trg_ctx, LY_EINVAL,
+ "Invalid argument parent (%s()) - does not interconnect with the created node's parents chain.", __func__);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, uint32_t options,
+ ly_bool nosiblings, struct lyd_node **dup)
+{
+ LY_ERR rc;
+ const struct lyd_node *orig; /* original node to be duplicated */
+ struct lyd_node *first = NULL; /* the first duplicated node, this is returned */
+ struct lyd_node *top = NULL; /* the most higher created node */
+ struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
+
+ assert(node && trg_ctx);
+
+ 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),
+ &top, &local_parent), error);
+ } else {
+ local_parent = parent;
+ }
+
+ LY_LIST_FOR(node, orig) {
+ if (lysc_is_key(orig->schema)) {
+ if (local_parent) {
+ /* the key must already exist in the parent */
+ rc = lyd_find_sibling_schema(local_parent->child, orig->schema, first ? NULL : &first);
+ LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error);
+ } else {
+ assert(!(options & LYD_DUP_WITH_PARENTS));
+ /* duplicating a single key, okay, I suppose... */
+ rc = lyd_dup_r(orig, trg_ctx, NULL, 0, &first, options, first ? NULL : &first);
+ LY_CHECK_GOTO(rc, error);
+ }
+ } else {
+ /* if there is no local parent, it will be inserted into first */
+ rc = lyd_dup_r(orig, trg_ctx, local_parent ? &local_parent->node : NULL, 0, &first, options,
+ first ? NULL : &first);
+ LY_CHECK_GOTO(rc, error);
+ }
+ if (nosiblings) {
+ break;
+ }
+ }
+
+ if (dup) {
+ *dup = first;
+ }
+ return LY_SUCCESS;
+
+error:
+ if (top) {
+ lyd_free_tree(top);
+ } else {
+ lyd_free_siblings(first);
+ }
+ 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(NULL, 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));
+
+ return lyd_dup(node, LYD_CTX(node), parent, options, 1, dup);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent,
+ uint32_t options, struct lyd_node **dup)
+{
+ LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
+
+ return lyd_dup(node, trg_ctx, parent, options, 1, dup);
+}
+
+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));
+
+ return lyd_dup(node, LYD_CTX(node), parent, options, 0, dup);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent,
+ uint32_t options, struct lyd_node **dup)
+{
+ LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
+
+ return lyd_dup(node, trg_ctx, parent, options, 0, dup);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_dup_meta_single(const struct lyd_meta *meta, struct lyd_node *node, struct lyd_meta **dup)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct ly_ctx *ctx;
+ struct lyd_meta *mt, *last;
+
+ LY_CHECK_ARG_RET(NULL, meta, node, LY_EINVAL);
+
+ /* log to node context but value must always use the annotation context */
+ ctx = meta->annotation->module->ctx;
+
+ /* create a copy */
+ mt = calloc(1, sizeof *mt);
+ LY_CHECK_ERR_RET(!mt, LOGMEM(LYD_CTX(node)), LY_EMEM);
+ mt->annotation = meta->annotation;
+ ret = meta->value.realtype->plugin->duplicate(ctx, &meta->value, &mt->value);
+ LY_CHECK_ERR_GOTO(ret, LOGERR(LYD_CTX(node), LY_EINT, "Value duplication failed."), finish);
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, meta->name, 0, &mt->name), finish);
+
+ /* insert as the last attribute */
+ mt->parent = node;
+ if (node->meta) {
+ for (last = node->meta; last->next; last = last->next) {}
+ last->next = mt;
+ } else {
+ node->meta = mt;
+ }
+
+finish:
+ if (ret) {
+ lyd_free_meta_single(mt);
+ } else if (dup) {
+ *dup = mt;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Merge a source sibling into target siblings.
+ *
+ * @param[in,out] first_trg First target sibling, is updated if top-level.
+ * @param[in] parent_trg Target parent.
+ * @param[in,out] sibling_src Source sibling to merge, set to NULL if spent.
+ * @param[in] merge_cb Optional merge callback.
+ * @param[in] cb_data Arbitrary callback data.
+ * @param[in] options Merge options.
+ * @param[in,out] dup_inst Duplicate instance cache for all @p first_trg siblings.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, const struct lyd_node **sibling_src_p,
+ lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct lyd_dup_inst **dup_inst)
+{
+ const struct lyd_node *child_src, *tmp, *sibling_src;
+ struct lyd_node *match_trg, *dup_src, *elem;
+ struct lyd_node_opaq *opaq_trg, *opaq_src;
+ struct lysc_type *type;
+ struct lyd_dup_inst *child_dup_inst = NULL;
+ LY_ERR ret;
+ ly_bool first_inst = 0;
+
+ sibling_src = *sibling_src_p;
+ if (!sibling_src->schema) {
+ /* try to find the same opaque node */
+ lyd_find_sibling_opaq_next(*first_trg, LYD_NAME(sibling_src), &match_trg);
+ } else if (sibling_src->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ /* try to find the exact instance */
+ lyd_find_sibling_first(*first_trg, sibling_src, &match_trg);
+ } else {
+ /* try to simply find the node, there cannot be more instances */
+ lyd_find_sibling_val(*first_trg, sibling_src->schema, NULL, 0, &match_trg);
+ }
+
+ if (match_trg) {
+ /* update match as needed */
+ LY_CHECK_RET(lyd_dup_inst_next(&match_trg, *first_trg, dup_inst));
+ } else {
+ /* first instance of this node */
+ first_inst = 1;
+ }
+
+ if (match_trg) {
+ /* call callback */
+ if (merge_cb) {
+ LY_CHECK_RET(merge_cb(match_trg, sibling_src, cb_data));
+ }
+
+ /* node found, make sure even value matches for all node types */
+ if (!match_trg->schema) {
+ if (lyd_compare_single(sibling_src, match_trg, 0)) {
+ /* update value */
+ opaq_trg = (struct lyd_node_opaq *)match_trg;
+ opaq_src = (struct lyd_node_opaq *)sibling_src;
+
+ lydict_remove(LYD_CTX(opaq_trg), opaq_trg->value);
+ lydict_insert(LYD_CTX(opaq_trg), opaq_src->value, 0, &opaq_trg->value);
+ opaq_trg->hints = opaq_src->hints;
+
+ ly_free_prefix_data(opaq_trg->format, opaq_trg->val_prefix_data);
+ opaq_trg->format = opaq_src->format;
+ ly_dup_prefix_data(LYD_CTX(opaq_trg), opaq_src->format, opaq_src->val_prefix_data,
+ &opaq_trg->val_prefix_data);
+ }
+ } else if ((match_trg->schema->nodetype == LYS_LEAF) &&
+ lyd_compare_single(sibling_src, match_trg, LYD_COMPARE_DEFAULTS)) {
+ /* since they are different, they cannot both be default */
+ assert(!(sibling_src->flags & LYD_DEFAULT) || !(match_trg->flags & LYD_DEFAULT));
+
+ /* update value (or only LYD_DEFAULT flag) only if flag set or the source node is not default */
+ if ((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT)) {
+ type = ((struct lysc_node_leaf *)match_trg->schema)->type;
+ type->plugin->free(LYD_CTX(match_trg), &((struct lyd_node_term *)match_trg)->value);
+ LY_CHECK_RET(type->plugin->duplicate(LYD_CTX(match_trg), &((struct lyd_node_term *)sibling_src)->value,
+ &((struct lyd_node_term *)match_trg)->value));
+
+ /* copy flags and add LYD_NEW */
+ match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW);
+ }
+ } else if ((match_trg->schema->nodetype & LYS_ANYDATA) && lyd_compare_single(sibling_src, match_trg, 0)) {
+ /* update value */
+ LY_CHECK_RET(lyd_any_copy_value(match_trg, &((struct lyd_node_any *)sibling_src)->value,
+ ((struct lyd_node_any *)sibling_src)->value_type));
+
+ /* copy flags and add LYD_NEW */
+ match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW);
+ }
+
+ /* check descendants, recursively */
+ ret = LY_SUCCESS;
+ LY_LIST_FOR_SAFE(lyd_child_no_keys(sibling_src), tmp, child_src) {
+ ret = lyd_merge_sibling_r(lyd_node_child_p(match_trg), match_trg, &child_src, merge_cb, cb_data, options,
+ &child_dup_inst);
+ if (ret) {
+ break;
+ }
+ }
+ lyd_dup_inst_free(child_dup_inst);
+ LY_CHECK_RET(ret);
+ } else {
+ /* node not found, merge it */
+ if (options & LYD_MERGE_DESTRUCT) {
+ dup_src = (struct lyd_node *)sibling_src;
+ lyd_unlink_tree(dup_src);
+ /* spend it */
+ *sibling_src_p = NULL;
+ } else {
+ LY_CHECK_RET(lyd_dup_single(sibling_src, NULL, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &dup_src));
+ }
+
+ if (!(options & LYD_MERGE_WITH_FLAGS)) {
+ /* set LYD_NEW for all the new nodes, required for validation */
+ LYD_TREE_DFS_BEGIN(dup_src, elem) {
+ elem->flags |= LYD_NEW;
+ LYD_TREE_DFS_END(dup_src, elem);
+ }
+ }
+
+ /* insert */
+ lyd_insert_node(parent_trg, first_trg, dup_src, 0);
+
+ if (first_inst) {
+ /* remember not to find this instance next time */
+ LY_CHECK_RET(lyd_dup_inst_next(&dup_src, *first_trg, dup_inst));
+ }
+
+ /* call callback, no source node */
+ if (merge_cb) {
+ LY_CHECK_RET(merge_cb(dup_src, NULL, cb_data));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod,
+ lyd_merge_cb merge_cb, void *cb_data, uint16_t options, ly_bool nosiblings)
+{
+ const struct lyd_node *sibling_src, *tmp;
+ struct lyd_dup_inst *dup_inst = NULL;
+ ly_bool first;
+ LY_ERR ret = LY_SUCCESS;
+
+ 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);
+
+ if (!source) {
+ /* nothing to merge */
+ return LY_SUCCESS;
+ }
+
+ if ((*target && lysc_data_parent((*target)->schema)) || lysc_data_parent(source->schema)) {
+ LOGERR(LYD_CTX(source), LY_EINVAL, "Invalid arguments - can merge only 2 top-level subtrees (%s()).", __func__);
+ return LY_EINVAL;
+ }
+
+ LY_LIST_FOR_SAFE(source, tmp, sibling_src) {
+ if (mod && (lyd_owner_module(sibling_src) != mod)) {
+ /* skip data nodes from different modules */
+ continue;
+ }
+
+ first = (sibling_src == source) ? 1 : 0;
+ ret = lyd_merge_sibling_r(target, NULL, &sibling_src, merge_cb, cb_data, options, &dup_inst);
+ if (ret) {
+ break;
+ }
+ if (first && !sibling_src) {
+ /* source was spent (unlinked), move to the next node */
+ source = tmp;
+ }
+
+ if (nosiblings) {
+ break;
+ }
+ }
+
+ if (options & LYD_MERGE_DESTRUCT) {
+ /* free any leftover source data that were not merged */
+ lyd_free_siblings((struct lyd_node *)source);
+ }
+
+ lyd_dup_inst_free(dup_inst);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_merge_tree(struct lyd_node **target, const struct lyd_node *source, uint16_t options)
+{
+ return lyd_merge(target, source, NULL, NULL, NULL, options, 1);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_merge_siblings(struct lyd_node **target, const struct lyd_node *source, uint16_t options)
+{
+ return lyd_merge(target, source, NULL, NULL, NULL, options, 0);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_merge_module(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod,
+ lyd_merge_cb merge_cb, void *cb_data, uint16_t options)
+{
+ return lyd_merge(target, source, mod, merge_cb, cb_data, options, 0);
+}
+
+static LY_ERR
+lyd_path_str_enlarge(char **buffer, size_t *buflen, size_t reqlen, ly_bool is_static)
+{
+ /* ending \0 */
+ ++reqlen;
+
+ if (reqlen > *buflen) {
+ if (is_static) {
+ return LY_EINCOMPLETE;
+ }
+
+ *buffer = ly_realloc(*buffer, reqlen * sizeof **buffer);
+ if (!*buffer) {
+ return LY_EMEM;
+ }
+
+ *buflen = reqlen;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_path_list_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static)
+{
+ const struct lyd_node *key;
+ size_t len;
+ const char *val;
+ char quot;
+
+ for (key = lyd_child(node); key && key->schema && (key->schema->flags & LYS_KEY); key = key->next) {
+ val = lyd_get_value(key);
+ len = 1 + strlen(key->schema->name) + 2 + strlen(val) + 2;
+ LY_CHECK_RET(lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static));
+
+ quot = '\'';
+ if (strchr(val, '\'')) {
+ quot = '"';
+ }
+ *bufused += sprintf(*buffer + *bufused, "[%s=%c%s%c]", key->schema->name, quot, val, quot);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Append leaf-list value predicate to path.
+ *
+ * @param[in] node Node to print.
+ * @param[in,out] buffer Buffer to print to.
+ * @param[in,out] buflen Current buffer length.
+ * @param[in,out] bufused Current number of characters used in @p buffer.
+ * @param[in] is_static Whether buffer is static or can be reallocated.
+ * @return LY_ERR
+ */
+static LY_ERR
+lyd_path_leaflist_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static)
+{
+ size_t len;
+ const char *val;
+ char quot;
+
+ val = lyd_get_value(node);
+ len = 4 + strlen(val) + 2; /* "[.='" + val + "']" */
+ LY_CHECK_RET(lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static));
+
+ quot = '\'';
+ if (strchr(val, '\'')) {
+ quot = '"';
+ }
+ *bufused += sprintf(*buffer + *bufused, "[.=%c%s%c]", quot, val, quot);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Append node position (relative to its other instances) predicate to path.
+ *
+ * @param[in] node Node to print.
+ * @param[in,out] buffer Buffer to print to.
+ * @param[in,out] buflen Current buffer length.
+ * @param[in,out] bufused Current number of characters used in @p buffer.
+ * @param[in] is_static Whether buffer is static or can be reallocated.
+ * @return LY_ERR
+ */
+static LY_ERR
+lyd_path_position_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static)
+{
+ size_t len;
+ uint32_t pos;
+ char *val = NULL;
+ LY_ERR rc;
+
+ pos = lyd_list_pos(node);
+ if (asprintf(&val, "%" PRIu32, pos) == -1) {
+ return LY_EMEM;
+ }
+
+ len = 1 + strlen(val) + 1;
+ rc = lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static);
+ if (rc != LY_SUCCESS) {
+ goto cleanup;
+ }
+
+ *bufused += sprintf(*buffer + *bufused, "[%s]", val);
+
+cleanup:
+ free(val);
+ return rc;
+}
+
+LIBYANG_API_DEF char *
+lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size_t buflen)
+{
+ ly_bool is_static = 0;
+ uint32_t i, depth;
+ size_t bufused = 0, len;
+ const struct lyd_node *iter, *parent;
+ const struct lys_module *mod, *prev_mod;
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(NULL, node, NULL);
+ if (buffer) {
+ LY_CHECK_ARG_RET(LYD_CTX(node), buflen > 1, NULL);
+ is_static = 1;
+ } else {
+ buflen = 0;
+ }
+
+ switch (pathtype) {
+ case LYD_PATH_STD:
+ case LYD_PATH_STD_NO_LAST_PRED:
+ depth = 1;
+ for (iter = node; iter->parent; iter = lyd_parent(iter)) {
+ ++depth;
+ }
+
+ goto iter_print;
+ while (depth) {
+ /* find the right node */
+ for (iter = node, i = 1; i < depth; iter = lyd_parent(iter), ++i) {}
+iter_print:
+ /* get the module */
+ mod = iter->schema ? iter->schema->module : lyd_owner_module(iter);
+ parent = lyd_parent(iter);
+ prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ if (prev_mod == mod) {
+ mod = NULL;
+ }
+
+ /* realloc string */
+ len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) :
+ strlen(((struct lyd_node_opaq *)iter)->name.name));
+ rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, is_static);
+ if (rc != LY_SUCCESS) {
+ break;
+ }
+
+ /* print next node */
+ bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", LYD_NAME(iter));
+
+ /* do not always print the last (first) predicate */
+ if (iter->schema && ((depth > 1) || (pathtype == LYD_PATH_STD))) {
+ switch (iter->schema->nodetype) {
+ case LYS_LIST:
+ if (iter->schema->flags & LYS_KEYLESS) {
+ /* print its position */
+ rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, is_static);
+ } else {
+ /* print all list keys in predicates */
+ rc = lyd_path_list_predicate(iter, &buffer, &buflen, &bufused, is_static);
+ }
+ break;
+ case LYS_LEAFLIST:
+ if (iter->schema->flags & LYS_CONFIG_W) {
+ /* print leaf-list value */
+ rc = lyd_path_leaflist_predicate(iter, &buffer, &buflen, &bufused, is_static);
+ } else {
+ /* print its position */
+ rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, is_static);
+ }
+ break;
+ default:
+ /* nothing to print more */
+ break;
+ }
+ }
+ if (rc != LY_SUCCESS) {
+ break;
+ }
+
+ --depth;
+ }
+ break;
+ }
+
+ return buffer;
+}
+
+char *
+lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype)
+{
+ uint32_t depth;
+ size_t bufused = 0, buflen = 0, len;
+ char *buffer = NULL;
+ const struct lyd_node *iter, *parent;
+ const struct lys_module *mod, *prev_mod;
+ LY_ERR rc = LY_SUCCESS;
+
+ switch (pathtype) {
+ case LYD_PATH_STD:
+ case LYD_PATH_STD_NO_LAST_PRED:
+ for (depth = 1; depth <= dnodes->count; ++depth) {
+ /* current node */
+ iter = dnodes->dnodes[depth - 1];
+ mod = iter->schema ? iter->schema->module : lyd_owner_module(iter);
+
+ /* parent */
+ parent = (depth > 1) ? dnodes->dnodes[depth - 2] : NULL;
+ assert(!parent || !iter->schema || !parent->schema || (lysc_data_parent(iter->schema) == parent->schema) ||
+ (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent))));
+
+ /* get module to print, if any */
+ prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ if (prev_mod == mod) {
+ mod = NULL;
+ }
+
+ /* realloc string */
+ len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) :
+ strlen(((struct lyd_node_opaq *)iter)->name.name));
+ if ((rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, 0))) {
+ break;
+ }
+
+ /* print next node */
+ bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", LYD_NAME(iter));
+
+ /* do not always print the last (first) predicate */
+ if (iter->schema && ((depth > 1) || (pathtype == LYD_PATH_STD))) {
+ switch (iter->schema->nodetype) {
+ case LYS_LIST:
+ if (iter->schema->flags & LYS_KEYLESS) {
+ /* print its position */
+ rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, 0);
+ } else {
+ /* print all list keys in predicates */
+ rc = lyd_path_list_predicate(iter, &buffer, &buflen, &bufused, 0);
+ }
+ break;
+ case LYS_LEAFLIST:
+ if (iter->schema->flags & LYS_CONFIG_W) {
+ /* print leaf-list value */
+ rc = lyd_path_leaflist_predicate(iter, &buffer, &buflen, &bufused, 0);
+ } else {
+ /* print its position */
+ rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, 0);
+ }
+ break;
+ default:
+ /* nothing to print more */
+ break;
+ }
+ }
+ if (rc) {
+ break;
+ }
+ }
+ break;
+ }
+
+ return buffer;
+}
+
+LIBYANG_API_DEF struct lyd_meta *
+lyd_find_meta(const struct lyd_meta *first, const struct lys_module *module, const char *name)
+{
+ struct lyd_meta *ret = NULL;
+ const struct ly_ctx *ctx;
+ const char *prefix, *tmp;
+ char *str;
+ 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);
+
+ if (!first) {
+ return NULL;
+ }
+
+ ctx = first->annotation->module->ctx;
+
+ /* parse the name */
+ tmp = name;
+ if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
+ LOGERR(ctx, LY_EINVAL, "Metadata name \"%s\" is not valid.", name);
+ return NULL;
+ }
+
+ /* find the module */
+ if (prefix) {
+ str = strndup(prefix, pref_len);
+ module = ly_ctx_get_module_latest(ctx, str);
+ free(str);
+ LY_CHECK_ERR_RET(!module, LOGERR(ctx, LY_EINVAL, "Module \"%.*s\" not found.", (int)pref_len, prefix), NULL);
+ }
+
+ /* find the metadata */
+ LY_LIST_FOR(first, first) {
+ if ((first->annotation->module == module) && !strcmp(first->name, name)) {
+ ret = (struct lyd_meta *)first;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *target, struct lyd_node **match)
+{
+ struct lyd_node **match_p, *iter, *dup = NULL;
+ struct lyd_node_inner *parent;
+ ly_bool found;
+
+ LY_CHECK_ARG_RET(NULL, target, LY_EINVAL);
+ if (!siblings) {
+ /* no data */
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ if (LYD_CTX(siblings) != LYD_CTX(target)) {
+ /* create a duplicate in this context */
+ LY_CHECK_RET(lyd_dup_single_to_ctx(target, LYD_CTX(siblings), NULL, 0, &dup));
+ target = dup;
+ }
+
+ if ((siblings->schema && target->schema && (lysc_data_parent(siblings->schema) != lysc_data_parent(target->schema)))) {
+ /* schema mismatch */
+ lyd_free_tree(dup);
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ /* get first sibling */
+ siblings = lyd_first_sibling(siblings);
+
+ parent = siblings->parent;
+ if (parent && parent->schema && parent->children_ht) {
+ assert(target->hash);
+
+ if (lysc_is_dup_inst_list(target->schema)) {
+ /* we must search the instances from beginning to find the first matching one */
+ found = 0;
+ LYD_LIST_FOR_INST(siblings, target->schema, iter) {
+ if (!lyd_compare_single(target, iter, 0)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ siblings = iter;
+ } else {
+ siblings = NULL;
+ }
+ } else {
+ /* find by hash */
+ if (!lyht_find(parent->children_ht, &target, target->hash, (void **)&match_p)) {
+ siblings = *match_p;
+ } else {
+ /* not found */
+ siblings = NULL;
+ }
+ }
+ } else {
+ /* no children hash table */
+ for ( ; siblings; siblings = siblings->next) {
+ if (!lyd_compare_single(siblings, target, LYD_COMPARE_OPAQ)) {
+ break;
+ }
+ }
+ }
+
+ lyd_free_tree(dup);
+ if (!siblings) {
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ if (match) {
+ *match = (struct lyd_node *)siblings;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *schema, const char *key_or_value,
+ size_t val_len, struct lyd_node **match)
+{
+ LY_ERR rc;
+ struct lyd_node *target = NULL;
+ const struct lyd_node *parent;
+
+ LY_CHECK_ARG_RET(NULL, schema, !(schema->nodetype & (LYS_CHOICE | LYS_CASE)), LY_EINVAL);
+ if (!siblings) {
+ /* no data */
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ if ((LYD_CTX(siblings) != schema->module->ctx)) {
+ /* parent of ext nodes is useless */
+ parent = (siblings->flags & LYD_EXT) ? NULL : lyd_parent(siblings);
+ if (lyd_find_schema_ctx(schema, LYD_CTX(siblings), parent, 0, &schema)) {
+ /* no schema node in siblings so certainly no data node either */
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+ }
+
+ if (siblings->schema && (lysc_data_parent(siblings->schema) != lysc_data_parent(schema))) {
+ /* schema mismatch */
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ if (key_or_value && !val_len) {
+ val_len = strlen(key_or_value);
+ }
+
+ if ((schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && key_or_value) {
+ /* create a data node and find the instance */
+ if (schema->nodetype == LYS_LEAFLIST) {
+ /* target used attributes: schema, hash, value */
+ rc = lyd_create_term(schema, key_or_value, val_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target);
+ LY_CHECK_RET(rc);
+ } else {
+ /* target used attributes: schema, hash, child (all keys) */
+ LY_CHECK_RET(lyd_create_list2(schema, key_or_value, val_len, &target));
+ }
+
+ /* find it */
+ rc = lyd_find_sibling_first(siblings, target, match);
+ } else {
+ /* find the first schema node instance */
+ rc = lyd_find_sibling_schema(siblings, schema, match);
+ }
+
+ lyd_free_tree(target);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_node *target, struct ly_set **set)
+{
+ struct lyd_node **match_p, *first, *iter;
+ struct lyd_node_inner *parent;
+
+ 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_RET(ly_set_new(set));
+
+ if (!siblings || (siblings->schema && (lysc_data_parent(siblings->schema) != lysc_data_parent(target->schema)))) {
+ /* no data or schema mismatch */
+ return LY_ENOTFOUND;
+ }
+
+ /* get first sibling */
+ siblings = lyd_first_sibling(siblings);
+
+ parent = siblings->parent;
+ if (parent && parent->schema && parent->children_ht) {
+ assert(target->hash);
+
+ /* find the first instance */
+ lyd_find_sibling_first(siblings, target, &first);
+ if (first) {
+ /* add it so that it is the first in the set */
+ if (ly_set_add(*set, first, 1, NULL)) {
+ goto error;
+ }
+
+ /* find by hash */
+ if (!lyht_find(parent->children_ht, &target, target->hash, (void **)&match_p)) {
+ iter = *match_p;
+ } else {
+ /* not found */
+ iter = NULL;
+ }
+ while (iter) {
+ /* add all found nodes into the set */
+ if ((iter != first) && !lyd_compare_single(iter, target, 0) && ly_set_add(*set, iter, 1, NULL)) {
+ goto error;
+ }
+
+ /* find next instance */
+ if (lyht_find_next(parent->children_ht, &iter, iter->hash, (void **)&match_p)) {
+ iter = NULL;
+ } else {
+ iter = *match_p;
+ }
+ }
+ }
+ } else {
+ /* no children hash table */
+ LY_LIST_FOR(siblings, siblings) {
+ if (!lyd_compare_single(target, siblings, LYD_COMPARE_OPAQ)) {
+ ly_set_add(*set, (void *)siblings, 1, NULL);
+ }
+ }
+ }
+
+ if (!(*set)->count) {
+ return LY_ENOTFOUND;
+ }
+ return LY_SUCCESS;
+
+error:
+ ly_set_free(*set, NULL);
+ *set = NULL;
+ return LY_EMEM;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struct lyd_node **match)
+{
+ LY_CHECK_ARG_RET(NULL, name, LY_EINVAL);
+
+ for ( ; first; first = first->next) {
+ if (!first->schema && !strcmp(LYD_NAME(first), name)) {
+ break;
+ }
+ }
+
+ if (match) {
+ *match = (struct lyd_node *)first;
+ }
+ return first ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format,
+ void *prefix_data, const struct lyxp_var *vars, struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_set xp_set = {0};
+ struct lyxp_expr *exp = NULL;
+ uint32_t i;
+
+ LY_CHECK_ARG_RET(NULL, tree, xpath, format, set, LY_EINVAL);
+
+ *set = NULL;
+
+ /* parse expression */
+ ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate expression */
+ ret = lyxp_eval(LYD_CTX(tree), exp, NULL, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
+ LYXP_IGNORE_WHEN);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (xp_set.type != LYXP_SET_NODE_SET) {
+ LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* transform into ly_set, allocate memory for all the elements once (even though not all items must be
+ * elements but most likely will be) */
+ (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
+ LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup);
+ (*set)->size = xp_set.used;
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
+ ret = ly_set_add(*set, xp_set.val.nodes[i].node, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
+ if (ret) {
+ ly_set_free(*set, NULL);
+ *set = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath,
+ const struct lyxp_var *vars, struct ly_set **set)
+{
+ LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL);
+
+ return lyd_find_xpath4(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set)
+{
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+
+ return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set)
+{
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+
+ return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_set xp_set = {0};
+ struct lyxp_expr *exp = NULL;
+
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, result, LY_EINVAL);
+
+ /* compile expression */
+ ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate expression */
+ ret = lyxp_eval(LYD_CTX(ctx_node), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, ctx_node, vars, &xp_set,
+ LYXP_IGNORE_WHEN);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* transform into boolean */
+ ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* set result */
+ *result = xp_set.val.bln;
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free((struct ly_ctx *)LYD_CTX(ctx_node), exp);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result)
+{
+ return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
+{
+ return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, struct lyd_node **match)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *expr = NULL;
+ struct ly_path *lypath = NULL;
+
+ 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_OPTIONAL, LY_PATH_PRED_SIMPLE, &expr);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compile the path */
+ ret = ly_path_compile(LYD_CTX(ctx_node), NULL, ctx_node->schema, NULL, expr,
+ output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 0, LY_VALUE_JSON, NULL, &lypath);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate the path */
+ ret = ly_path_eval_partial(lypath, ctx_node, NULL, match);
+
+cleanup:
+ lyxp_expr_free(LYD_CTX(ctx_node), expr);
+ ly_path_free(LYD_CTX(ctx_node), lypath);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match)
+{
+ LY_ERR ret;
+ struct lyd_node *m;
+
+ LY_CHECK_ARG_RET(NULL, path, LY_EINVAL);
+
+ ret = ly_path_eval(path, tree, &m);
+ if (ret) {
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ if (match) {
+ *match = m;
+ }
+ return LY_SUCCESS;
+}
diff --git a/src/tree_data.h b/src/tree_data.h
new file mode 100644
index 0000000..ff98f60
--- /dev/null
+++ b/src/tree_data.h
@@ -0,0 +1,2601 @@
+/**
+ * @file tree_data.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang representation of YANG data trees.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_DATA_H_
+#define LY_TREE_DATA_H_
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "config.h"
+#include "log.h"
+#include "tree.h"
+#include "tree_schema.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_ctx;
+struct ly_path;
+struct ly_set;
+struct lyd_node;
+struct lyd_node_opaq;
+struct lyd_node_term;
+struct timespec;
+struct lyxp_var;
+
+/**
+ * @page howtoData Data Instances
+ *
+ * All the nodes in data tree comes are based on ::lyd_node structure. According to the content of the ::lyd_node.schema
+ * it can be cast to several other structures.
+ *
+ * In case the ::lyd_node.schema pointer is NULL, the node is actually __opaq__ and can be safely cast to ::lyd_node_opaq.
+ * The opaq node represent an unknown node which wasn't mapped to any [(compiled) schema](@ref howtoSchema) node in the
+ * context. Such a node can appear in several places in the data tree.
+ * - As a part of the tree structure, but only in the case the ::LYD_PARSE_OPAQ option was used when input data were
+ * [parsed](@ref howtoDataParsers), because unknown data instances are ignored by default. The same way, the opaq nodes can
+ * appear as a node's attributes.
+ * - As a representation of YANG anydata/anyxml content.
+ * - As envelopes of standard data tree instances (RPCs, actions or Notifications).
+ *
+ * In case the data node has its definition in a [compiled schema tree](@ref howtoSchema), the structure of the data node is
+ * actually one of the followings according to the schema node's nodetype (::lysc_node.nodetype).
+ * - ::lyd_node_inner - represents data nodes corresponding to schema nodes matching ::LYD_NODE_INNER nodetypes. They provide
+ * structure of the tree by having children nodes.
+ * - ::lyd_node_term - represents data nodes corresponding to schema nodes matching ::LYD_NODE_TERM nodetypes. The terminal
+ * nodes provide values of the particular configuration/status information. The values are represented as ::lyd_value
+ * structure with string representation of the value (retrieved by ::lyd_get_value() and ::lyd_get_meta_value()) and
+ * the type specific data stored in the structure's union according to the real type of the value (::lyd_value.realtype).
+ * The string representation provides canonical representation of the value in case the type has the canonical
+ * representation specified. Otherwise, it is the original value or, in case the value can contain prefixes, the JSON
+ * format is used to make the value unambiguous.
+ * - ::lyd_node_any - represents data nodes corresponding to schema nodes matching ::LYD_NODE_ANY nodetypes.
+ *
+ * Despite all the aforementioned structures and their members are available as part of the libyang API and callers can use
+ * it to navigate through the data tree structure or to obtain various information, we recommend to use the following macros
+ * and functions.
+ * - ::lyd_child() (or ::lyd_child_no_keys()) and ::lyd_parent() to get the node's child/parent node.
+ * - ::LYD_CTX to get libyang context from a data node.
+ * - ::lyd_get_value()/::lyd_get_meta_value() to get canonical string value from a terminal node/metadata instance.
+ * - ::LYD_TREE_DFS_BEGIN and ::LYD_TREE_DFS_END to traverse the data tree (depth-first).
+ * - ::LY_LIST_FOR and ::LY_ARRAY_FOR as described on @ref howtoStructures page.
+ *
+ * Instead of going through the data tree on your own, a specific data node can be also located using a wide set of
+ * \b lyd_find_*() functions.
+ *
+ * More information about specific operations with data instances can be found on the following pages:
+ * - @subpage howtoDataParsers
+ * - @subpage howtoDataValidation
+ * - @subpage howtoDataWD
+ * - @subpage howtoDataManipulation
+ * - @subpage howtoDataPrinters
+ * - @subpage howtoDataLYB
+ *
+ * \note API for this group of functions is described in the [Data Instances module](@ref datatree).
+ *
+ * Functions List (not assigned to above subsections)
+ * --------------------------------------------------
+ * - ::lyd_child()
+ * - ::lyd_child_no_keys()
+ * - ::lyd_parent()
+ * - ::lyd_owner_module()
+ * - ::lyd_get_value()
+ * - ::lyd_get_meta_value()
+ * - ::lyd_find_xpath()
+ * - ::lyd_find_path()
+ * - ::lyd_find_target()
+ * - ::lyd_find_sibling_val()
+ * - ::lyd_find_sibling_first()
+ * - ::lyd_find_sibling_opaq_next()
+ * - ::lyd_find_meta()
+ *
+ * - ::lyd_path()
+ * - ::lyd_target()
+ *
+ * - ::lyd_lyb_data_length()
+ *
+ *
+ * @section howtoDataMetadata Metadata Support
+ *
+ * YANG Metadata annotations are defined in [RFC 7952](https://tools.ietf.org/html/rfc7952) as YANG extension (and libyang
+ * [implements them as internal extension plugin](@ref howtoPluginsExtensions)). In practice, it allows to have XML
+ * attributes (there is also a special encoding for JSON) in YANG modeled data. libyang does not allow to have any XML
+ * attribute without the appropriate annotation definition describing the data as it is done e.g. for leafs. When an
+ * attribute without a matching annotation definition is found in the input data, it is:
+ * - silently dropped (with warning) or
+ * - an error is reported in case the ::LYD_PARSE_STRICT parser option is provided to the
+ * [parser function](@ref howtoDataParsers) or
+ * - stored into a generic ::lyd_attr structure without a connection with any YANG module in case the ::LYD_PARSE_OPAQ
+ * parser options is provided to the [parser function](@ref howtoDataParsers).
+ *
+ * There are some XML attributes, described by [YANG](https://tools.ietf.org/html/rfc7950) and
+ * [NETCONF](https://tools.ietf.org/html/rfc6241) specifications, which are not defined as annotations, but libyang
+ * implements them this way. In case of attributes in the YANG namespace (`insert`, `value` and `key` attributes
+ * for the NETCONF edit-config operation), they are defined in special libyang's internal module `yang`, which is
+ * available in each context and the content of this schema can be printed via
+ * [schema printers](@ref howtoSchemaPrinters).
+ *
+ * In case of the attributes described in [NETCONF specification](https://tools.ietf.org/html/rfc6241), the libyang's
+ * annotations structures are hidden and cannot be printed despite, internally, they are part of the `ietf-netconf`'s
+ * schema structure. Therefore, these attributes are available only when the `ietf-netconf` schema is loaded in the
+ * context. The definitions of these annotations are as follows:
+ *
+ * md:annotation operation {
+ * type enumeration {
+ * enum merge;
+ * enum replace;
+ * enum create;
+ * enum delete;
+ * enum remove;
+ * }
+ * }
+ *
+ * md:annotation type {
+ * type enumeration {
+ * enum subtree;
+ * enum xpath {
+ * if-feature "nc:xpath";
+ * }
+ * }
+ * }
+ *
+ * md:annotation select {
+ * type string;
+ * }
+ *
+ * Note, that, following the specification,
+ * - the `type` and `select` XML attributes are supposed to be unqualified (without namespace) and that
+ * - the `select`'s content is XPath and it is internally transformed by libyang into the format where the
+ * XML namespace prefixes are replaced by the YANG module names.
+ *
+ *
+ * @section howtoDataYangdata yang-data Support
+ *
+ * [RFC 8040](https://tools.ietf.org/html/rfc8040) defines ietf-restconf module, which includes yang-data extension. Despite
+ * the definition in the RESTCONF YANG module, the yang-data concept is quite generic and used even in modules without a
+ * connection to RESTCONF protocol. The extension allows to define a separated YANG trees usable separately from any
+ * datastore.
+ *
+ * libyang implements support for yang-data internally as an [extension plugin](@ref howtoPluginsExtensions). To ease the
+ * use of yang-data with libyang, there are several generic functions, which are usable for yang-data:
+ *
+ * - ::lyd_parse_ext_data()
+ * - ::lyd_parse_ext_op()
+ *
+ * - ::lys_getnext_ext()
+ *
+ * - ::lyd_new_ext_inner()
+ * - ::lyd_new_ext_list()
+ * - ::lyd_new_ext_term()
+ * - ::lyd_new_ext_any()
+ * - ::lyd_new_ext_path()
+ *
+ * @section howtoDataMountpoint mount-point Support
+ *
+ * [RFC 8528](https://tools.ietf.org/html/rfc8528) defines mount-point extension in ietf-yang-schema-mount YANG module.
+ * This extension is supported out-of-the-box but to be able to parse data in a mount point, additional run-time data
+ * need to be provided by a callback:
+ *
+ * - ::ly_ctx_set_ext_data_clb()
+ *
+ * The mounted data can be parsed directly from data files or created manually using the standard functions. However,
+ * note that the mounted data use **their own context** created as needed. For *inline* data this means that any new
+ * request for a mount-point schema node results in a new context creation because it is impossible to determine
+ * whether any existing context can be used. Also, all these contexts created for the mounted data are **never**
+ * freed automatically except when the parent context is being freed. So, to avoid redundant context creation, it is
+ * always advised to use *shared-schema* for mount-points.
+ *
+ * In case it is not possible and *inline* mount point must be defined, it is still possible to avoid creating
+ * additional contexts. When the top-level node right under a schema node with a mount-point is created, always use
+ * this node for creation of any descendants. So, when using ::lyd_new_path(), use the node as `parent` and specify
+ * relative `path`.
+ */
+
+/**
+ * @page howtoDataManipulation Manipulating Data
+ *
+ * There are many functions to create or modify an existing data tree. You can add new nodes, reconnect nodes from
+ * one tree to another (or e.g. from one list instance to another) or remove nodes. The functions doesn't allow you
+ * to put a node to a wrong place (by checking the YANG module structure), but not all validation checks can be made directly
+ * (or you have to make a valid change by multiple tree modifications) when the tree is being changed. Therefore,
+ * the [validation process](@ref howtoDataValidation) is expected to be invoked after changing the data tree to make sure
+ * that the changed data tree is valid.
+ *
+ * When inserting a node into data tree (no matter if the node already exists, via ::lyd_insert_child() and
+ * ::lyd_insert_sibling(), or a new node is being created), the node is automatically inserted to the place respecting the
+ * nodes order from the YANG schema. So the node is not inserted to the end or beginning of the siblings list, but after the
+ * existing instance of the closest preceding sibling node from the schema. In case the node is opaq (it is not connected
+ * with any schema node), it is placed to the end of the sibling node in the order they are inserted in. The only situation
+ * when it is possible to influence the order of the nodes is the order of user-ordered list/leaf-list instances. In such
+ * a case the ::lyd_insert_after() or ::lyd_insert_before() can be used.
+ *
+ * Creating data is generally possible in two ways, they can be combined. You can add nodes one-by-one based on
+ * the node name and/or its parent (::lyd_new_inner(), ::lyd_new_term(), ::lyd_new_any(), ::lyd_new_list(), ::lyd_new_list2()
+ * and ::lyd_new_opaq()) or address the nodes using a [simple XPath addressing](@ref howtoXPath) (::lyd_new_path() and
+ * ::lyd_new_path2()). The latter enables to create a whole path of nodes, requires less information
+ * about the modified data, and is generally simpler to use. Actually the third way is duplicating the existing data using
+ * ::lyd_dup_single(), ::lyd_dup_siblings() and ::lyd_dup_meta_single().
+ *
+ * Note, that in case the node is defined in an extension instance, the functions mentioned above do not work until you
+ * provide parent where the new node is supposed to be inserted. The reason is that all the functions searches for the
+ * top-level nodes directly inside modules. To create a top-level node defined in an extension instance, use
+ * ::lyd_new_ext_inner(), ::lyd_new_ext_term(), ::lyd_new_ext_any(), ::lyd_new_ext_list() and ::lyd_new_ext_path()
+ * functions.
+ *
+ * The [metadata](@ref howtoDataMetadata) (and attributes in opaq nodes) can be created with ::lyd_new_meta()
+ * and ::lyd_new_attr().
+ *
+ * Changing value of a terminal node (leaf, leaf-list) is possible with ::lyd_change_term(). Similarly, the metadata value
+ * can be changed with ::lyd_change_meta(). Before changing the value, it might be useful to compare the node's value
+ * with a string value (::lyd_value_compare()) or verify that the new string value is correct for the specific data node
+ * (::lyd_value_validate()).
+ *
+ * Working with two existing subtrees can also be performed two ways. Usually, you would use lyd_insert*() functions.
+ * They are generally meant for simple inserts of a node into a data tree. For more complicated inserts and when
+ * merging 2 trees use ::lyd_merge_tree() or ::lyd_merge_siblings(). It offers additional options and is basically a more
+ * powerful insert.
+ *
+ * Besides merging, libyang is also capable to provide information about differences between two data trees. For this purpose,
+ * ::lyd_diff_tree() and ::lyd_diff_siblings() generates annotated data trees which can be, in addition, used to change one
+ * data tree to another one using ::lyd_diff_apply_all(), ::lyd_diff_apply_module() and ::lyd_diff_reverse_all(). Multiple
+ * diff data trees can be also put together for further work using ::lyd_diff_merge_all(), ::lyd_diff_merge_module() and
+ * ::lyd_diff_merge_tree() functions. To just check equivalence of the data nodes, ::lyd_compare_single(),
+ * ::lyd_compare_siblings() and ::lyd_compare_meta() can be used.
+ *
+ * To remove a node or subtree from a data tree, use ::lyd_unlink_tree() and then free the unwanted data using
+ * ::lyd_free_all() (or other \b lyd_free_*() functions).
+ *
+ * Also remember, that when you are creating/inserting a node, all the objects in that operation must belong to the
+ * same context.
+ *
+ * Modifying the single data tree in multiple threads is not safe.
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_new_inner()
+ * - ::lyd_new_term()
+ * - ::lyd_new_term_bin()
+ * - ::lyd_new_term_canon()
+ * - ::lyd_new_list()
+ * - ::lyd_new_list_bin()
+ * - ::lyd_new_list_canon()
+ * - ::lyd_new_list2()
+ * - ::lyd_new_any()
+ * - ::lyd_new_opaq()
+ * - ::lyd_new_opaq2()
+ * - ::lyd_new_attr()
+ * - ::lyd_new_attr2()
+ * - ::lyd_new_meta()
+ * - ::lyd_new_path()
+ * - ::lyd_new_path2()
+ *
+ * - ::lyd_new_ext_inner()
+ * - ::lyd_new_ext_term()
+ * - ::lyd_new_ext_list()
+ * - ::lyd_new_ext_any()
+ * - ::lyd_new_ext_path()
+ *
+ * - ::lyd_dup_single()
+ * - ::lyd_dup_siblings()
+ * - ::lyd_dup_meta_single()
+ *
+ * - ::lyd_insert_child()
+ * - ::lyd_insert_sibling()
+ * - ::lyd_insert_after()
+ * - ::lyd_insert_before()
+ *
+ * - ::lyd_value_compare()
+ * - ::lyd_value_validate()
+ *
+ * - ::lyd_change_term()
+ * - ::lyd_change_term_bin()
+ * - ::lyd_change_term_canon()
+ * - ::lyd_change_meta()
+ *
+ * - ::lyd_compare_single()
+ * - ::lyd_compare_siblings()
+ * - ::lyd_compare_meta()
+ * - ::lyd_diff_tree()
+ * - ::lyd_diff_siblings()
+ * - ::lyd_diff_apply_all()
+ * - ::lyd_diff_apply_module()
+ * - ::lyd_diff_reverse_all()
+ * - ::lyd_diff_merge_all()
+ * - ::lyd_diff_merge_module()
+ * - ::lyd_diff_merge_tree()
+ *
+ * - ::lyd_merge_tree()
+ * - ::lyd_merge_siblings()
+ * - ::lyd_merge_module()
+ *
+ * - ::lyd_unlink_tree()
+ *
+ * - ::lyd_free_all()
+ * - ::lyd_free_siblings()
+ * - ::lyd_free_tree()
+ * - ::lyd_free_meta_single()
+ * - ::lyd_free_meta_siblings()
+ * - ::lyd_free_attr_single()
+ * - ::lyd_free_attr_siblings()
+ *
+ * - ::lyd_any_value_str()
+ * - ::lyd_any_copy_value()
+ */
+
+/**
+ * @page howtoDataWD Default Values
+ *
+ * libyang provides support for work with default values as defined in [RFC 6243](https://tools.ietf.org/html/rfc6243).
+ * However, libyang context do not contains the *ietf-netconf-with-defaults* module on its own and caller is supposed to
+ * add this YANG module to enable full support of the *with-defaults* features described below. Without presence of the
+ * mentioned module in the context, the default nodes are still present and handled in the data trees, but the metadata
+ * providing the information about the default values cannot be used. It means that when parsing data, the default nodes
+ * marked with the metadata as implicit default nodes are handled as explicit data and when printing data tree, the expected
+ * nodes are printed without the ietf-netconf-with-defaults metadata.
+ *
+ * The RFC document defines 4 modes for handling default nodes in a data tree, libyang adds the fifth mode and use them
+ * via @ref dataprinterflags when printing data trees.
+ * - \b explicit - Only the explicitly set configuration data. But in the case of status data, missing default
+ * data are added into the tree. In libyang, this mode is represented by ::LYD_PRINT_WD_EXPLICIT option.
+ * This is the default with-defaults mode of the printer. The data nodes do not contain any additional
+ * metadata information.
+ * - \b trim - Data nodes containing the default value are removed. This mode is applied with ::LYD_PRINT_WD_TRIM option.
+ * - \b report-all - This mode provides all the default data nodes despite they were explicitly present in source data or
+ * they were added by libyang's [validation process](@ref howtoDataValidation). This mode is activated by
+ * ::LYD_PRINT_WD_ALL option.
+ * - \b report-all-tagged - In this case, all the data nodes (implicit as well the explicit) containing the default value
+ * are printed and tagged (see the note below). Printers accept ::LYD_PRINT_WD_ALL_TAG option for this mode.
+ * - \b report-implicit-tagged - The last mode is similar to the previous one, except only the implicitly added nodes
+ * are tagged. This is the libyang's extension and it is activated by ::LYD_PRINT_WD_IMPL_TAG option.
+ *
+ * Internally, libyang adds the default nodes into the data tree as part of the [validation process](@ref howtoDataValidation).
+ * When [parsing data](@ref howtoDataParsers) from an input source, adding default nodes can be avoided only by avoiding
+ * the whole [validation process](@ref howtoDataValidation). In case the ietf-netconf-with-defaults module is present in the
+ * context, the [parser process](@ref howtoDataParsers) also supports to recognize the implicit default nodes marked with the
+ * appropriate metadata.
+ *
+ * Note, that in a modified data tree (via e.g. \b lyd_insert_*() or \b lyd_free_*() functions), some of the default nodes
+ * can be missing or they can be present by mistake. Such a data tree is again corrected during the next run of the
+ * [validation process](@ref howtoDataValidation) or manualy using \b lyd_new_implicit_*() functions.
+ *
+ * The implicit (default) nodes, created by libyang, are marked with the ::LYD_DEFAULT flag in ::lyd_node.flags member
+ * Note, that besides leafs and leaf-lists, the flag can appear also in containers, where it means that the container
+ * holds only a default node(s) or it is implicitly added empty container (according to YANG 1.1 spec, all such containers are part of
+ * the accessible data tree). When printing data trees, the presence of empty containers (despite they were added
+ * explicitly or implicitly as part of accessible data tree) depends on ::LYD_PRINT_KEEPEMPTYCONT option.
+ *
+ * To get know if the particular leaf or leaf-list node contains default value (despite implicit or explicit), you can
+ * use ::lyd_is_default() function.
+ *
+ * Functions List
+ * --------------
+ * - ::lyd_is_default()
+ * - ::lyd_new_implicit_all()
+ * - ::lyd_new_implicit_module()
+ * - ::lyd_new_implicit_tree()
+ */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ *
+ * LYB (LibYang Binary) is a proprietary libyang binary data and file format. Its primary purpose is efficient
+ * serialization (printing) and deserialization (parsing). With this goal in mind, every term node value is stored
+ * in its new binary format specification according to its type. Following is the format for all types with explicit
+ * support out-of-the-box (meaning that have a special type plugin). Any derived types inherit the format of its
+ * closest type with explicit support (up to a built-in type).
+ *
+ * @section howtoDataLYBTypes Format of specific data type values
+ */
+
+/**
+ * @ingroup trees
+ * @defgroup datatree Data Tree
+ * @{
+ *
+ * Data structures and functions to manipulate and access instance data tree.
+ */
+
+/* *INDENT-OFF* */
+
+/**
+ * @brief Macro to iterate via all elements in a data tree. This is the opening part
+ * to the #LYD_TREE_DFS_END - they always have to be used together.
+ *
+ * The function follows deep-first search algorithm:
+ * <pre>
+ * 1
+ * / \
+ * 2 4
+ * / / \
+ * 3 5 6
+ * </pre>
+ *
+ * Use the same parameters for #LYD_TREE_DFS_BEGIN and #LYD_TREE_DFS_END. While
+ * START can be any of the lyd_node* types, ELEM variable must be a pointer to
+ * the generic struct lyd_node.
+ *
+ * To skip a particular subtree, instead of the continue statement, set LYD_TREE_DFS_continue
+ * variable to non-zero value.
+ *
+ * Use with opening curly bracket '{' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+#define LYD_TREE_DFS_BEGIN(START, ELEM) \
+ { ly_bool LYD_TREE_DFS_continue = 0; struct lyd_node *LYD_TREE_DFS_next; \
+ for ((ELEM) = (LYD_TREE_DFS_next) = (struct lyd_node *)(START); \
+ (ELEM); \
+ (ELEM) = (LYD_TREE_DFS_next), LYD_TREE_DFS_continue = 0)
+
+/**
+ * @brief Macro to iterate via all elements in a tree. This is the closing part
+ * to the #LYD_TREE_DFS_BEGIN - they always have to be used together.
+ *
+ * Use the same parameters for #LYD_TREE_DFS_BEGIN and #LYD_TREE_DFS_END. While
+ * START can be any of the lyd_node* types, ELEM variable must be a pointer
+ * to the generic struct lyd_node.
+ *
+ * Use with closing curly bracket '}' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+
+#define LYD_TREE_DFS_END(START, ELEM) \
+ /* select element for the next run - children first */ \
+ if (LYD_TREE_DFS_continue) { \
+ (LYD_TREE_DFS_next) = NULL; \
+ } else { \
+ (LYD_TREE_DFS_next) = lyd_child(ELEM); \
+ }\
+ if (!(LYD_TREE_DFS_next)) { \
+ /* no children */ \
+ if ((ELEM) == (struct lyd_node *)(START)) { \
+ /* we are done, (START) has no children */ \
+ break; \
+ } \
+ /* try siblings */ \
+ (LYD_TREE_DFS_next) = (ELEM)->next; \
+ } \
+ while (!(LYD_TREE_DFS_next)) { \
+ /* parent is already processed, go to its sibling */ \
+ (ELEM) = (struct lyd_node *)(ELEM)->parent; \
+ /* no siblings, go back through parents */ \
+ if ((ELEM)->parent == (START)->parent) { \
+ /* we are done, no next element to process */ \
+ break; \
+ } \
+ (LYD_TREE_DFS_next) = (ELEM)->next; \
+ } }
+
+/**
+ * @brief Macro to iterate via all schema node data instances in data siblings.
+ *
+ * @param START Pointer to the starting sibling. Even if it is not first, all the siblings are searched.
+ * @param SCHEMA Schema node of the searched instances.
+ * @param ELEM Iterator.
+ */
+#define LYD_LIST_FOR_INST(START, SCHEMA, ELEM) \
+ for (lyd_find_sibling_val(START, SCHEMA, NULL, 0, &(ELEM)); \
+ (ELEM) && ((ELEM)->schema == (SCHEMA)); \
+ (ELEM) = (ELEM)->next)
+
+/**
+ * @brief Macro to iterate via all schema node data instances in data siblings allowing to modify the list itself.
+ *
+ * @param START Pointer to the starting sibling. Even if it is not first, all the siblings are searched.
+ * @param SCHEMA Schema node of the searched instances.
+ * @param NEXT Temporary storage to allow removing of the current iterator content.
+ * @param ELEM Iterator.
+ */
+#define LYD_LIST_FOR_INST_SAFE(START, SCHEMA, NEXT, ELEM) \
+ for ((NEXT) = (ELEM) = NULL, lyd_find_sibling_val(START, SCHEMA, NULL, 0, &(ELEM)); \
+ (ELEM) && ((ELEM)->schema == (SCHEMA)) ? ((NEXT) = (ELEM)->next, 1) : 0; \
+ (ELEM) = (NEXT))
+
+/* *INDENT-ON* */
+
+/**
+ * @brief Macro to get context from a data tree node.
+ */
+#define LYD_CTX(node) ((node)->schema ? (node)->schema->module->ctx : ((const struct lyd_node_opaq *)(node))->ctx)
+
+/**
+ * @brief Data input/output formats supported by libyang [parser](@ref howtoDataParsers) and
+ * [printer](@ref howtoDataPrinters) functions.
+ */
+typedef enum {
+ LYD_UNKNOWN = 0, /**< unknown data format, invalid value */
+ LYD_XML, /**< XML instance data format */
+ LYD_JSON, /**< JSON instance data format */
+ LYD_LYB /**< LYB instance data format */
+} LYD_FORMAT;
+
+/**
+ * @brief List of possible value types stored in ::lyd_node_any.
+ */
+typedef enum {
+ LYD_ANYDATA_DATATREE, /**< Value is a pointer to ::lyd_node structure (first sibling). When provided as input parameter, the pointer
+ is directly connected into the anydata node without duplication, caller is supposed to not manipulate
+ with the data after a successful call (including calling ::lyd_free_all() on the provided data) */
+ LYD_ANYDATA_STRING, /**< Value is a generic string without any knowledge about its format (e.g. anyxml value in JSON encoded
+ as string). XML sensitive characters (such as & or \>) are automatically escaped when the anydata
+ is printed in XML format. */
+ LYD_ANYDATA_XML, /**< Value is a string containing the serialized XML data. */
+ LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */
+ LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */
+} LYD_ANYDATA_VALUETYPE;
+
+/** @} */
+
+/**
+ * @brief YANG data representation
+ */
+struct lyd_value {
+ const char *_canonical; /**< Should never be accessed directly, instead ::lyd_get_value() and ::lyd_get_meta_value()
+ should be used. Serves as a cache for the canonical value or the JSON
+ representation if no canonical value is defined. */
+ const struct lysc_type *realtype; /**< pointer to the real type of the data stored in the value structure. This type can differ from the type
+ in the schema node of the data node since the type's store plugin can use other types/plugins for
+ storing data. Speaking about built-in types, this is the case of leafref which stores data as its
+ target type. In contrast, union type also uses its subtype's callbacks, but inside an internal data
+ stored in subvalue member of ::lyd_value structure, so here is the pointer to the union type.
+ In general, this type is used to get free callback for this lyd_value structure, so it must reflect
+ the type used to store data directly in the same lyd_value instance. */
+
+ union {
+ int8_t boolean; /**< 0 as false, 1 as true */
+ int64_t dec64; /**< decimal64: value = dec64 / 10^fraction-digits */
+ int8_t int8; /**< 8-bit signed integer */
+ int16_t int16; /**< 16-bit signed integer */
+ int32_t int32; /**< 32-bit signed integer */
+ int64_t int64; /**< 64-bit signed integer */
+ uint8_t uint8; /**< 8-bit unsigned integer */
+ uint16_t uint16; /**< 16-bit unsigned integer */
+ uint32_t uint32; /**< 32-bit unsigned integer */
+ uint64_t uint64; /**< 64-bit unsigned integer */
+ struct lysc_type_bitenum_item *enum_item; /**< pointer to the definition of the enumeration value */
+ struct lysc_ident *ident; /**< pointer to the schema definition of the identityref value */
+ struct ly_path *target; /**< Instance-identifier target path, use ::lyd_find_target() to evaluate
+ it on data. */
+ struct lyd_value_union *subvalue; /** Union value with some metadata. */
+
+ void *dyn_mem; /**< pointer to generic data type value stored in dynamic memory */
+ uint8_t fixed_mem[LYD_VALUE_FIXED_MEM_SIZE]; /**< fixed-size buffer for a generic data type value */
+ }; /**< The union is just a list of shorthands to possible values stored by a type's plugin. libyang itself uses the ::lyd_value.realtype
+ plugin's callbacks to work with the data.*/
+};
+
+/**
+ * @brief Get the value in format specific to the type.
+ *
+ * Should be used for any types that do not have their specific representation in the ::lyd_value union.
+ *
+ * @param[in] value Pointer to the value structure to read from (struct ::lyd_value *).
+ * @param[out] type_val Pointer to the type-specific value structure.
+ */
+#define LYD_VALUE_GET(value, type_val) \
+ ((sizeof *(type_val) > LYD_VALUE_FIXED_MEM_SIZE) \
+ ? ((type_val) = (((value)->dyn_mem))) \
+ : ((type_val) = ((void *)((value)->fixed_mem))))
+
+/**
+ * @brief Special lyd_value structure for built-in union values.
+ *
+ * Represents data with multiple types (union). The ::lyd_value_union.value contains representation according to
+ * one of the union's types. The ::lyd_value_union.prefix_data provides (possible) mappings from prefixes in
+ * the original value to YANG modules. These prefixes are necessary to parse original value to the union's subtypes.
+ */
+struct lyd_value_union {
+ struct lyd_value value; /**< representation of the value according to the selected union's subtype
+ (stored as ::lyd_value.realtype here) */
+ void *original; /**< Original value. */
+ size_t orig_len; /**< Original value length. */
+ uint32_t hints; /**< [Value hints](@ref lydvalhints) from the parser */
+ LY_VALUE_FORMAT format; /**< Prefix format of the value. However, this information is also used to decide
+ whether a value is valid for the specific format or not on later validations
+ (instance-identifier in XML looks different than in JSON). */
+ void *prefix_data; /**< Format-specific data for prefix resolution (see ly_resolve_prefix()) */
+ const struct lysc_node *ctx_node; /**< Context schema node. */
+};
+
+/**
+ * @brief Special lyd_value structure for built-in bits values.
+ */
+struct lyd_value_bits {
+ char *bitmap; /**< bitmap of size ::lyplg_type_bits_bitmap_size(), if its value is
+ cast to an integer type of the corresponding size, can be used
+ directly as a bitmap */
+ struct lysc_type_bitenum_item **items; /**< list of set pointers to the specification of the set
+ bits ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Special lyd_value structure for built-in binary values.
+ */
+struct lyd_value_binary {
+ void *data; /**< pointer to the binary value */
+ size_t size; /**< size of @p data value in bytes */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-inet-types ipv4-address-no-zone values.
+ */
+struct lyd_value_ipv4_address_no_zone {
+ struct in_addr addr; /**< IPv4 address in binary */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-inet-types ipv4-address values.
+ */
+struct lyd_value_ipv4_address {
+ struct in_addr addr; /**< IPv4 address in binary */
+ const char *zone; /**< Optional address zone */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-inet-types ipv4-prefix values.
+ */
+struct lyd_value_ipv4_prefix {
+ struct in_addr addr; /**< IPv4 host address in binary */
+ uint8_t prefix; /**< prefix length (0 - 32) */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-inet-types ipv6-address-no-zone values.
+ */
+struct lyd_value_ipv6_address_no_zone {
+ struct in6_addr addr; /**< IPv6 address in binary */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-inet-types ipv6-address values.
+ */
+struct lyd_value_ipv6_address {
+ struct in6_addr addr; /**< IPv6 address in binary */
+ const char *zone; /**< Optional address zone */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-inet-types ipv6-prefix values.
+ */
+struct lyd_value_ipv6_prefix {
+ struct in6_addr addr; /**< IPv6 host address in binary */
+ uint8_t prefix; /**< prefix length (0 - 128) */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-yang-types date-and-time values.
+ */
+struct lyd_value_date_and_time {
+ time_t time; /**< UNIX timestamp */
+ char *fractions_s; /**< Optional fractions of a second */
+ ly_bool unknown_tz; /**< Whether the value is in the special -00:00 timezone. */
+};
+
+/**
+ * @brief Special lyd_value structure for ietf-yang-types xpath1.0 values.
+ */
+struct lyd_value_xpath10 {
+ struct lyxp_expr *exp;
+ const struct ly_ctx *ctx;
+ void *prefix_data;
+ LY_VALUE_FORMAT format;
+};
+
+/**
+ * @brief Generic prefix and namespace mapping, meaning depends on the format.
+ *
+ * The union is used as a reference to the data's module and according to the format, it can be used as a key for
+ * ::ly_ctx_get_module_implemented_ns() or ::ly_ctx_get_module_implemented(). While the module reference is always present,
+ * the prefix member can be omitted in case it is not present in the source data as a reference to the default module/namespace.
+ */
+struct ly_opaq_name {
+ const char *name; /**< node name, without prefix if any was defined */
+ const char *prefix; /**< identifier used in the qualified name as the prefix, can be NULL */
+
+ union {
+ const char *module_ns; /**< format ::LY_VALUE_XML - XML namespace of the node element */
+ const char *module_name; /**< format ::LY_VALUE_JSON - (inherited) name of the module of the element */
+ };
+};
+
+/**
+ * @brief Generic attribute structure.
+ */
+struct lyd_attr {
+ struct lyd_node_opaq *parent; /**< data node where the attribute is placed */
+ struct lyd_attr *next; /**< pointer to the next attribute */
+ struct ly_opaq_name name; /**< attribute name with module information */
+ const char *value; /**< attribute value */
+ uint32_t hints; /**< additional information about from the data source, see the [hints list](@ref lydhints) */
+ LY_VALUE_FORMAT format; /**< format of the attribute and any prefixes, ::LY_VALUE_XML or ::LY_VALUE_JSON */
+ void *val_prefix_data; /**< format-specific prefix data */
+};
+
+#define LYD_NODE_INNER (LYS_CONTAINER|LYS_LIST|LYS_RPC|LYS_ACTION|LYS_NOTIF) /**< Schema nodetype mask for lyd_node_inner */
+#define LYD_NODE_TERM (LYS_LEAF|LYS_LEAFLIST) /**< Schema nodetype mask for lyd_node_term */
+#define LYD_NODE_ANY (LYS_ANYDATA) /**< Schema nodetype mask for lyd_node_any */
+
+/**
+ * @ingroup datatree
+ * @defgroup dnodeflags Data node flags
+ * @{
+ *
+ * Various flags of data nodes.
+ *
+ * 1 - container 5 - anydata/anyxml
+ * 2 - list 6 - rpc/action
+ * 3 - leaf 7 - notification
+ * 4 - leaflist
+ *
+ * bit name 1 2 3 4 5 6 7
+ * ---------------------+-+-+-+-+-+-+-+
+ * 1 LYD_DEFAULT |x| |x|x| | | |
+ * +-+-+-+-+-+-+-+
+ * 2 LYD_WHEN_TRUE |x|x|x|x|x| | |
+ * +-+-+-+-+-+-+-+
+ * 3 LYD_NEW |x|x|x|x|x|x|x|
+ * +-+-+-+-+-+-+-+
+ * 4 LYD_EXT |x|x|x|x|x|x|x|
+ * ---------------------+-+-+-+-+-+-+-+
+ *
+ */
+
+#define LYD_DEFAULT 0x01 /**< default (implicit) node */
+#define LYD_WHEN_TRUE 0x02 /**< all when conditions of this node were evaluated to true */
+#define LYD_NEW 0x04 /**< node was created after the last validation, is needed for the next validation */
+#define LYD_EXT 0x08 /**< node is the first sibling parsed as extension instance data */
+
+/** @} */
+
+/**
+ * @brief Generic structure for a data node.
+ */
+struct lyd_node {
+ uint32_t hash; /**< hash of this particular node (module name + schema name + key string values if list or
+ hashes of all nodes of subtree in case of keyless list). Note that while hash can be
+ used to get know that nodes are not equal, it cannot be used to decide that the
+ nodes are equal due to possible collisions. */
+ uint32_t flags; /**< [data node flags](@ref dnodeflags) */
+ const struct lysc_node *schema; /**< pointer to the schema definition of this node */
+ struct lyd_node_inner *parent; /**< pointer to the parent node, NULL in case of root node */
+ struct lyd_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lyd_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ struct lyd_meta *meta; /**< pointer to the list of metadata of this node */
+ void *priv; /**< private user data, not used by libyang */
+};
+
+/**
+ * @brief Data node structure for the inner data tree nodes - containers, lists, RPCs, actions and Notifications.
+ */
+struct lyd_node_inner {
+ union {
+ struct lyd_node node; /**< implicit cast for the members compatible with ::lyd_node */
+
+ struct {
+ uint32_t hash; /**< hash of this particular node (module name + schema name + key string
+ values if list or hashes of all nodes of subtree in case of keyless
+ list). Note that while hash can be used to get know that nodes are
+ not equal, it cannot be used to decide that the nodes are equal due
+ to possible collisions. */
+ uint32_t flags; /**< [data node flags](@ref dnodeflags) */
+ const struct lysc_node *schema; /**< pointer to the schema definition of this node */
+ struct lyd_node_inner *parent; /**< pointer to the parent node, NULL in case of root node */
+ struct lyd_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lyd_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ struct lyd_meta *meta; /**< pointer to the list of metadata of this node */
+ void *priv; /**< private user data, not used by libyang */
+ };
+ }; /**< common part corresponding to ::lyd_node */
+
+ struct lyd_node *child; /**< pointer to the first child node. */
+ struct hash_table *children_ht; /**< hash table with all the direct children (except keys for a list, lists without keys) */
+
+#define LYD_HT_MIN_ITEMS 4 /**< minimal number of children to create ::lyd_node_inner.children_ht hash table. */
+};
+
+/**
+ * @brief Data node structure for the terminal data tree nodes - leaves and leaf-lists.
+ */
+struct lyd_node_term {
+ union {
+ struct lyd_node node; /**< implicit cast for the members compatible with ::lyd_node */
+
+ struct {
+ uint32_t hash; /**< hash of this particular node (module name + schema name + key string
+ values if list or hashes of all nodes of subtree in case of keyless
+ list). Note that while hash can be used to get know that nodes are
+ not equal, it cannot be used to decide that the nodes are equal due
+ to possible collisions. */
+ uint32_t flags; /**< [data node flags](@ref dnodeflags) */
+ const struct lysc_node *schema; /**< pointer to the schema definition of this node */
+ struct lyd_node_inner *parent; /**< pointer to the parent node, NULL in case of root node */
+ struct lyd_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lyd_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ struct lyd_meta *meta; /**< pointer to the list of metadata of this node */
+ void *priv; /**< private user data, not used by libyang */
+ };
+ }; /**< common part corresponding to ::lyd_node */
+
+ struct lyd_value value; /**< node's value representation */
+};
+
+/**
+ * @brief union for anydata/anyxml value representation.
+ */
+union lyd_any_value {
+ struct lyd_node *tree; /**< data tree */
+ const char *str; /**< Generic string data */
+ const char *xml; /**< Serialized XML data */
+ const char *json; /**< I-JSON encoded string */
+ char *mem; /**< LYD_ANYDATA_LYB memory chunk */
+};
+
+/**
+ * @brief Data node structure for the anydata data tree nodes - anydata or
+ * anyxml.
+ */
+struct lyd_node_any {
+ union {
+ struct lyd_node node; /**< implicit cast for the members compatible with ::lyd_node */
+
+ struct {
+ uint32_t hash; /**< hash of this particular node (module name + schema name + key string
+ values if list or hashes of all nodes of subtree in case of keyless
+ list). Note that while hash can be used to get know that nodes are
+ not equal, it cannot be used to decide that the nodes are equal due
+ to possible collisions. */
+ uint32_t flags; /**< [data node flags](@ref dnodeflags) */
+ const struct lysc_node *schema; /**< pointer to the schema definition of this node */
+ struct lyd_node_inner *parent; /**< pointer to the parent node, NULL in case of root node */
+ struct lyd_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lyd_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ struct lyd_meta *meta; /**< pointer to the list of metadata of this node */
+ void *priv; /**< private user data, not used by libyang */
+ };
+ }; /**< common part corresponding to ::lyd_node */
+
+ union lyd_any_value value; /**< pointer to the stored value representation of the anydata/anyxml node */
+ LYD_ANYDATA_VALUETYPE value_type; /**< type of the data stored as ::lyd_node_any.value */
+};
+
+/**
+ * @brief Get the name (associated with) of a data node. Works for opaque nodes as well.
+ *
+ * @param[in] node Node to examine.
+ * @return Data node name.
+ */
+#define LYD_NAME(node) ((node)->schema ? (node)->schema->name : ((struct lyd_node_opaq *)node)->name.name)
+
+/**
+ * @ingroup datatree
+ * @defgroup lydvalhints Value format hints.
+ * @{
+ *
+ * Hints for the type of the data value.
+ *
+ * Any information about value types encoded in the format is hinted by these values.
+ */
+#define LYD_VALHINT_STRING 0x0001 /**< value is allowed to be a string */
+#define LYD_VALHINT_DECNUM 0x0002 /**< value is allowed to be a decimal number */
+#define LYD_VALHINT_OCTNUM 0x0004 /**< value is allowed to be an octal number */
+#define LYD_VALHINT_HEXNUM 0x0008 /**< value is allowed to be a hexadecimal number */
+#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 */
+/**
+ * @} lydvalhints
+ */
+
+/**
+ * @ingroup datatree
+ * @defgroup lydnodehints Node type format hints
+ * @{
+ *
+ * Hints for the type of the data node.
+ *
+ * Any information about node types encoded in the format is hinted by these values.
+ */
+#define LYD_NODEHINT_LIST 0x0080 /**< node is allowed to be a list instance */
+#define LYD_NODEHINT_LEAFLIST 0x0100 /**< node is allowed to be a leaf-list instance */
+/**
+ * @} lydnodehints
+ */
+
+/**
+ * @ingroup datatree
+ * @defgroup lydhints Value and node type format hints
+ * @{
+ *
+ * Hints for the types of data node and its value.
+ *
+ * Any information about value and node types encoded in the format is hinted by these values.
+ * It combines [value hints](@ref lydvalhints) and [node hints](@ref lydnodehints).
+ */
+#define LYD_HINT_DATA 0x01F3 /**< special node/value hint to be used for generic data node/value (for cases when
+ there is no encoding or it does not provide any additional information about
+ a node/value type); do not combine with specific [value hints](@ref lydvalhints)
+ or [node hints](@ref lydnodehints). */
+#define LYD_HINT_SCHEMA 0x01FF /**< special node/value hint to be used for generic schema node/value(for cases when
+ there is no encoding or it does not provide any additional information about
+ a node/value type); do not combine with specific [value hints](@ref lydvalhints)
+ or [node hints](@ref lydnodehints). */
+/**
+ * @} lydhints
+ */
+
+/**
+ * @brief Data node structure for unparsed (opaque) nodes.
+ */
+struct lyd_node_opaq {
+ union {
+ struct lyd_node node; /**< implicit cast for the members compatible with ::lyd_node */
+
+ struct {
+ uint32_t hash; /**< always 0 */
+ uint32_t flags; /**< always 0 */
+ const struct lysc_node *schema; /**< always NULL */
+ struct lyd_node_inner *parent; /**< pointer to the parent node, NULL in case of root node */
+ struct lyd_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lyd_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ struct lyd_meta *meta; /**< always NULL */
+ void *priv; /**< private user data, not used by libyang */
+ };
+ }; /**< common part corresponding to ::lyd_node */
+
+ struct lyd_node *child; /**< pointer to the child node (compatible with ::lyd_node_inner) */
+
+ struct ly_opaq_name name; /**< node name with module information */
+ const char *value; /**< original value */
+ uint32_t hints; /**< additional information about from the data source, see the [hints list](@ref lydhints) */
+ LY_VALUE_FORMAT format; /**< format of the node and any prefixes, ::LY_VALUE_XML or ::LY_VALUE_JSON */
+ void *val_prefix_data; /**< format-specific prefix data */
+
+ struct lyd_attr *attr; /**< pointer to the list of generic attributes of this node */
+ const struct ly_ctx *ctx; /**< libyang context */
+};
+
+/**
+ * @brief Get the generic parent pointer of a data node.
+ *
+ * @param[in] node Node whose parent pointer to get.
+ * @return Pointer to the parent node of the @p node.
+ * @return NULL in case of the top-level node or if the @p node is NULL itself.
+ */
+static inline struct lyd_node *
+lyd_parent(const struct lyd_node *node)
+{
+ if (!node || !node->parent) {
+ return NULL;
+ }
+
+ return &node->parent->node;
+}
+
+/**
+ * @brief Get the child pointer of a generic data node.
+ *
+ * Decides the node's type and in case it has a children list, returns it. Supports even the opaq nodes (::lyd_node_opaq).
+ *
+ * If you need to skip key children, use ::lyd_child_no_keys().
+ *
+ * @param[in] node Node to use.
+ * @return Pointer to the first child node (if any) of the @p node.
+ */
+static inline struct lyd_node *
+lyd_child(const struct lyd_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ /* opaq node */
+ return ((const struct lyd_node_opaq *)node)->child;
+ }
+
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ return ((const struct lyd_node_inner *)node)->child;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * @brief Get the child pointer of a generic data node but skip its keys in case it is ::LYS_LIST.
+ *
+ * Decides the node's type and in case it has a children list, returns it. Supports even the opaq nodes (::lyd_node_opaq).
+ *
+ * If you need to take key children into account, use ::lyd_child().
+ *
+ * @param[in] node Node to use.
+ * @return Pointer to the first child node (if any) of the @p node.
+ */
+LIBYANG_API_DECL struct lyd_node *lyd_child_no_keys(const struct lyd_node *node);
+
+/**
+ * @brief Get the owner module of the data node. It is the module of the top-level schema node. Generally,
+ * in case of augments it is the target module, recursively, otherwise it is the module where the data node is defined.
+ *
+ * Also works for opaque nodes, if it is possible to resolve the module.
+ *
+ * @param[in] node Data node to examine.
+ * @return Module owner of the node.
+ */
+LIBYANG_API_DECL const struct lys_module *lyd_owner_module(const struct lyd_node *node);
+
+/**
+ * @brief Check whether a node value equals to its default one.
+ *
+ * @param[in] node Term node to test.
+ * @return false (no, it is not a default node) or true (yes, it is default)
+ */
+LIBYANG_API_DECL ly_bool lyd_is_default(const struct lyd_node *node);
+
+/**
+ * @brief Learn the relative position of a list or leaf-list instance within other instances of the same schema node.
+ *
+ * @param[in] instance List or leaf-list instance to get the position of.
+ * @return 0 on error.
+ * @return Positive integer of the @p instance position.
+ */
+LIBYANG_API_DECL uint32_t lyd_list_pos(const struct lyd_node *instance);
+
+/**
+ * @brief Get the first sibling of the given node.
+ *
+ * @param[in] node Node which first sibling is going to be the result.
+ * @return The first sibling of the given node or the node itself if it is the first child of the parent.
+ */
+LIBYANG_API_DECL struct lyd_node *lyd_first_sibling(const struct lyd_node *node);
+
+/**
+ * @brief Learn the length of LYB data.
+ *
+ * @param[in] data LYB data to examine.
+ * @return Length of the LYB data chunk,
+ * @return -1 on error.
+ */
+LIBYANG_API_DECL int lyd_lyb_data_length(const char *data);
+
+/**
+ * @brief Check node parsed into an opaque node for the reason (error) why it could not be parsed as data node.
+ *
+ * The node is expected to be produced by a parser and must either have no parent or a data node parent (not opaque).
+ *
+ * @param[in] node Opaque node to check.
+ * @return LY_EINVAL if @p node is in some way unexpected (even valid);
+ * @return LY_ERR value of the reason.
+ */
+LIBYANG_API_DECL LY_ERR lyd_parse_opaq_error(const struct lyd_node *node);
+
+/**
+ * @brief Get the (canonical) value of a lyd_value.
+ *
+ * Whenever possible, ::lyd_get_value() or ::lyd_get_meta_value() should be used instead.
+ *
+ * @param[in] ctx Context for the value
+ * @param[in] value Value structure to use.
+ * @return Canonical value.
+ */
+LIBYANG_API_DECL const char *lyd_value_get_canonical(const struct ly_ctx *ctx, const struct lyd_value *value);
+
+/**
+ * @brief Get the (canonical) value of a data node.
+ *
+ * @param[in] node Data node to use.
+ * @return Canonical value.
+ */
+static inline const char *
+lyd_get_value(const struct lyd_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ return ((const struct lyd_node_opaq *)node)->value;
+ } else if (node->schema->nodetype & LYD_NODE_TERM) {
+ const struct lyd_value *value = &((const struct lyd_node_term *)node)->value;
+
+ return value->_canonical ? value->_canonical : lyd_value_get_canonical(LYD_CTX(node), value);
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Get anydata string value.
+ *
+ * @param[in] any Anyxml/anydata node to read from.
+ * @param[out] value_str String representation of the value.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_any_value_str(const struct lyd_node *any, char **value_str);
+
+/**
+ * @brief Copy anydata value from one node to another. Target value is freed first.
+ *
+ * @param[in,out] trg Target node.
+ * @param[in] value Source value, may be NULL when the target value is only freed.
+ * @param[in] value_type Source value type.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_any_copy_value(struct lyd_node *trg, const union lyd_any_value *value,
+ LYD_ANYDATA_VALUETYPE value_type);
+
+/**
+ * @brief Create a new inner node in the data tree.
+ *
+ * To create list, use ::lyd_new_list() or ::lyd_new_list2().
+ *
+ * To create a top-level inner node defined in an extension instance, use ::lyd_new_ext_inner().
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_CONTAINER, #LYS_NOTIF, #LYS_RPC, or #LYS_ACTION.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new top-level inner node defined in the given extension instance.
+ *
+ * To create list, use ::lyd_new_list() or ::lyd_new_list2().
+ *
+ * To create an inner node with parent (no matter if defined inside extension instance or a standard tree) or a top-level
+ * node of a standard module's tree, use ::lyd_new_inner().
+ *
+ * @param[in] ext Extension instance where the inner node being created is defined.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_CONTAINER, #LYS_NOTIF, #LYS_RPC, or #LYS_ACTION.
+ * @param[out] node The created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_ext_inner(const struct lysc_ext_instance *ext, const char *name, struct lyd_node **node);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @param[in] ... Ordered key values of the new list instance, all must be set. In case of an instance-identifier
+ * or identityref value, the JSON format is expected (module names instead of prefixes). No keys are expected for
+ * key-less lists.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ ly_bool output, struct lyd_node **node, ...);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @param[in] ... Ordered binary key values of the new list instance, all must be set. Every key value must be followed
+ * by its length. No keys are expected for key-less lists.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list_bin(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ ly_bool output, struct lyd_node **node, ...);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @param[in] ... Ordered canonical key values of the new list instance, all must be set. No keys are expected for
+ * key-less lists.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list_canon(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ ly_bool output, struct lyd_node **node, ...);
+
+/**
+ * @brief Create a new top-level list node defined in the given extension instance.
+ *
+ * To create a list node with parent (no matter if defined inside extension instance or a standard tree) or a top-level
+ * list node of a standard module's tree, use ::lyd_new_list() or ::lyd_new_list2().
+ *
+ * @param[in] ext Extension instance where the list node being created is defined.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[out] node The created node.
+ * @param[in] ... Ordered key values of the new list instance, all must be set. In case of an instance-identifier
+ * or identityref value, the JSON format is expected (module names instead of prefixes). No keys are expected for
+ * key-less lists.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_ext_list(const struct lysc_ext_instance *ext, const char *name, struct lyd_node **node, ...);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] keys All key values predicate in the form of "[key1='val1'][key2='val2']...", they do not have to be ordered.
+ * In case of an instance-identifier or identityref value, the JSON format is expected (module names instead of prefixes).
+ * Use NULL or string of length 0 in case of key-less list.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char *keys, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new term node in the data tree.
+ *
+ * To create a top-level term node defined in an extension instance, use ::lyd_new_ext_term().
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
+ * @param[in] val_str String value of the node. If it varies based on the format, ::LY_VALUE_JSON is expected.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char *val_str, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new term node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
+ * @param[in] value Binary value of the node. To learn what exactly is expected see @ref howtoDataLYB.
+ * @param[in] value_len Length of @p value.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_term_bin(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const void *value, size_t value_len, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new term node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
+ * @param[in] val_str Canonical string value of the node. If it is not, it may lead to unexpected behavior.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_term_canon(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char *val_str, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new top-level term node defined in the given extension instance.
+ *
+ * To create a term node with parent (no matter if defined inside extension instance or a standard tree) or a top-level
+ * node of a standard module's tree, use ::lyd_new_term().
+ *
+ * @param[in] ext Extension instance where the term node being created is defined.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
+ * @param[in] val_str String form of the value of the node being created. In case of an instance-identifier or identityref
+ * value, the JSON format is expected (module names instead of prefixes).
+ * @param[out] node The created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_ext_term(const struct lysc_ext_instance *ext, const char *name, const char *val_str,
+ struct lyd_node **node);
+
+/**
+ * @brief Create a new any node in the data tree.
+ *
+ * To create a top-level any node defined in an extension instance, use ::lyd_new_ext_any().
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_ANYDATA or #LYS_ANYXML.
+ * @param[in] value Value for the node. Expected type is determined by @p value_type.
+ * @param[in] use_value Whether to use dynamic @p value or make a copy.
+ * @param[in] value_type Type of the provided value in @p value.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const void *value, ly_bool use_value, LYD_ANYDATA_VALUETYPE value_type, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new top-level any node defined in the given extension instance.
+ *
+ * To create an any node with parent (no matter if defined inside extension instance or a standard tree) or a top-level
+ * any node of a standard module's tree, use ::lyd_new_any().
+ *
+ * @param[in] ext Extension instance where the any node being created is defined.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_ANYDATA or #LYS_ANYXML.
+ * @param[in] value Value for the node. Expected type is determined by @p value_type.
+ * @param[in] use_value Whether to use dynamic @p value or make a copy.
+ * @param[in] value_type Type of the provided value in @p value.
+ * @param[out] node The created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_ext_any(const struct lysc_ext_instance *ext, const char *name, const void *value,
+ ly_bool use_value, LYD_ANYDATA_VALUETYPE value_type, struct lyd_node **node);
+
+/**
+ * @brief Create new metadata.
+ *
+ * @param[in] ctx libyang context,
+ * @param[in] parent Optional parent node for the metadata being created. Must be set if @p meta is NULL.
+ * @param[in] module Module of the metadata being created. If NULL, @p name must include module name as the prefix.
+ * @param[in] name Annotation name of the new metadata. It can include the annotation module as the prefix.
+ * If the prefix is specified it is always used but if not specified, @p module must be set.
+ * @param[in] val_str String form of the value of the metadata. In case of an instance-identifier or identityref
+ * value, the JSON format is expected (module names instead of prefixes).
+ * @param[in] clear_dflt Whether to clear the default flag starting from @p parent, recursively all NP containers.
+ * @param[out] meta Optional created metadata. Must be set if @p parent is NULL.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys_module *module,
+ const char *name, const char *val_str, ly_bool clear_dflt, struct lyd_meta **meta);
+
+/**
+ * @brief Create new metadata from an opaque node attribute if possible.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] parent Optional parent node for the metadata being created. Must be set if @p meta is NULL.
+ * @param[in] clear_dflt Whether to clear the default flag starting from @p parent, recursively all NP containers.
+ * @param[in] attr Opaque node attribute to parse into metadata.
+ * @param[out] meta Optional created metadata. Must be set if @p parent is NULL.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the attribute could not be parsed into any metadata.
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, ly_bool clear_dflt,
+ const struct lyd_attr *attr, struct lyd_meta **meta);
+
+/**
+ * @brief Create a new JSON opaque node in the data tree. To create an XML opaque node, use ::lyd_new_opaq2().
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] ctx libyang context. If NULL, @p parent context will be used.
+ * @param[in] name Node name.
+ * @param[in] value Optional node value.
+ * @param[in] prefix Optional node prefix, must be equal to @p module_name if set.
+ * @param[in] module_name Node module name.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_opaq(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
+ const char *prefix, const char *module_name, struct lyd_node **node);
+
+/**
+ * @brief Create a new XML opaque node in the data tree. To create a JSON opaque node, use ::lyd_new_opaq().
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] ctx libyang context. If NULL, @p parent context will be used.
+ * @param[in] name Node name.
+ * @param[in] value Optional node value.
+ * @param[in] prefix Optional node prefix.
+ * @param[in] module_ns Node module namespace.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_opaq2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
+ const char *prefix, const char *module_ns, struct lyd_node **node);
+
+/**
+ * @brief Create new JSON attribute for an opaque data node. To create an XML attribute, use ::lyd_new_attr2().
+ *
+ * Note that for an attribute to be later resolved as YANG metadata, it needs @p module_nane and a prefix in @p name.
+ *
+ * @param[in] parent Parent opaque node for the attribute.
+ * @param[in] module_name Optional name of the module of the attribute.
+ * @param[in] name Attribute name with optional prefix, which is a module name. If the prefix is set, it is also stored
+ * as the explicit module name if @p module_name is not set.
+ * @param[in] value Optional attribute value.
+ * @param[out] attr Optional created attribute.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *value,
+ struct lyd_attr **attr);
+
+/**
+ * @brief Create new XML attribute for an opaque data node. To create a JSON attribute, use ::lyd_new_attr().
+ *
+ * Note that for an attribute to be later resolved as YANG metadata, it needs @p module_ns and a prefix in @p name.
+ *
+ * @param[in] parent Parent opaque node for the attribute being created.
+ * @param[in] module_ns Optional namespace of the module of the attribute.
+ * @param[in] name Attribute name with optional prefix, which is an XML prefix.
+ * @param[in] value Optional attribute value.
+ * @param[out] attr Optional created attribute.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *module_ns, const char *name, const char *value,
+ struct lyd_attr **attr);
+
+/**
+ * @ingroup datatree
+ * @defgroup pathoptions Data path creation options
+ *
+ * Various options to change lyd_new_path*() behavior.
+ *
+ * Default behavior:
+ * - if the target node already exists (and is not default), an error is returned.
+ * - the whole path to the target node is created (with any missing parents) if necessary.
+ * - RPC output schema children are completely ignored in all modules. Input is searched and nodes created normally.
+ * @{
+ */
+
+#define LYD_NEW_PATH_UPDATE 0x01 /**< If the target node exists, is a leaf, and it is updated with a new value or its
+ default flag is changed, it is returned. If the target node exists and is not
+ a leaf or generally no change occurs in the @p parent tree, NULL is returned and
+ no error set. */
+#define LYD_NEW_PATH_OUTPUT 0x02 /**< Changes the behavior to ignoring RPC/action input schema nodes and using only
+ output ones. */
+#define LYD_NEW_PATH_OPAQ 0x04 /**< Enables the creation of opaque nodes with some specific rules. If the __last node__
+ in the path is not uniquely defined ((leaf-)list without a predicate) or has an
+ invalid value (leaf/leaf-list), it is created as opaque. */
+#define LYD_NEW_PATH_BIN_VALUE 0x08 /**< Interpret the provided leaf/leaf-list @p value as being in the binary
+ ::LY_VALUE_LYB format, to learn what exactly is expected see @ref howtoDataLYB. */
+#define LYD_NEW_PATH_CANON_VALUE 0x10 /**< Interpret the provided leaf/leaf-list @p value as being in the canonical
+ (or JSON if no defined) ::LY_VALUE_CANON format. If it is not, it may lead
+ to unexpected behavior. */
+
+/** @} pathoptions */
+
+/**
+ * @brief Create a new node in the data tree based on a path. If creating anyxml/anydata nodes, ::lyd_new_path2
+ * should be used instead, this function expects the value as string.
+ *
+ * If creating data nodes defined inside an extension instance, use ::lyd_new_ext_path().
+ *
+ * If @p path points to a list key, the key value from the predicate is used and @p value is ignored.
+ * Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * For key-less lists and non-configuration leaf-lists, positional predicates should be used (indices starting from 1).
+ * 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
+ * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] path [Path](@ref howtoXPath) to create.
+ * @param[in] value String value of the new leaf/leaf-list. If it varies based on the format, ::LY_VALUE_JSON is expected.
+ * For other node types, it should be NULL.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] node Optional first created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value,
+ uint32_t options, struct lyd_node **node);
+
+/**
+ * @brief Create a new node in the data tree based on a path. All node types can be created.
+ *
+ * Details are mentioned in ::lyd_new_path().
+ *
+ * @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
+ * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] path [Path](@ref howtoXPath) to create.
+ * @param[in] value Value of the new leaf/leaf-list (const char *) in ::LY_VALUE_JSON format. If creating an
+ * anyxml/anydata node, the expected type depends on @p value_type. For other node types, it should be NULL.
+ * @param[in] value_len Length of @p value in bytes. May be 0 if @p value is a zero-terminated string. Ignored when
+ * creating anyxml/anydata nodes.
+ * @param[in] value_type Anyxml/anydata node @p value type.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
+ * @param[out] new_node Optional last node created.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent,
+ struct lyd_node **new_node);
+
+/**
+ * @brief Create a new node defined in the given extension instance. In case of anyxml/anydata nodes, this function expects
+ * the @p value as string.
+ *
+ * If creating data nodes defined in a module's standard tree, use ::lyd_new_path() or ::lyd_new_path2().
+ *
+ * Details are mentioned in ::lyd_new_path().
+ *
+ * @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
+ * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
+ * @param[in] ext Extension instance where the node being created is defined.
+ * @param[in] path [Path](@ref howtoXPath) to create.
+ * @param[in] value Value of the new leaf/leaf-list. For other node types, it should be NULL.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] node Optional first created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, const char *path,
+ const void *value, uint32_t options, struct lyd_node **node);
+
+/**
+ * @ingroup datatree
+ * @defgroup implicitoptions Implicit node creation options
+ *
+ * Various options to change lyd_new_implicit*() behavior.
+ *
+ * Default behavior:
+ * - both configuration and state missing implicit nodes are added.
+ * - for existing RPC/action nodes, input implicit nodes are added.
+ * - all implicit node types are added (non-presence containers, default leaves, and default leaf-lists).
+ * @{
+ */
+
+#define LYD_IMPLICIT_NO_STATE 0x01 /**< Do not add any implicit state nodes. */
+#define LYD_IMPLICIT_NO_CONFIG 0x02 /**< Do not add any implicit config nodes. */
+#define LYD_IMPLICIT_OUTPUT 0x04 /**< For RPC/action nodes, add output implicit nodes instead of input. */
+#define LYD_IMPLICIT_NO_DEFAULTS 0x08 /**< Do not add any default nodes (leaves/leaf-lists), only non-presence
+ containers. */
+
+/** @} implicitoptions */
+
+/**
+ * @brief Add any missing implicit nodes into a data subtree. Default nodes with a false "when" are not added.
+ *
+ * @param[in] tree Tree to add implicit nodes into.
+ * @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
+ * @param[out] diff Optional diff with any created nodes.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_implicit_tree(struct lyd_node *tree, uint32_t implicit_options, struct lyd_node **diff);
+
+/**
+ * @brief Add any missing implicit nodes. Default nodes with a false "when" are not added.
+ *
+ * @param[in,out] tree Tree to add implicit nodes into. Note that in case a first top-level sibling is used,
+ * it may no longer be first if an implicit node was inserted before @p tree. Use ::lyd_first_sibling() to
+ * adjust @p tree in these cases.
+ * @param[in] ctx libyang context, must be set only if @p tree is an empty tree.
+ * @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
+ * @param[out] diff Optional diff with any created nodes.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t implicit_options,
+ struct lyd_node **diff);
+
+/**
+ * @brief Add any missing implicit nodes of one module. Default nodes with a false "when" are not added.
+ *
+ * @param[in,out] tree Tree to add implicit nodes into. Note that in case a first top-level sibling is used,
+ * it may no longer be first if an implicit node was inserted before @p tree. Use ::lyd_first_sibling() to
+ * adjust @p tree in these cases.
+ * @param[in] module Module whose implicit nodes to create.
+ * @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
+ * @param[out] diff Optional diff with any created nodes.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module,
+ uint32_t implicit_options, struct lyd_node **diff);
+
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node to a string value.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] val_str New value to set, any prefixes are expected in JSON format.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occurred,
+ * @return LY_ERR value on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lyd_change_term(struct lyd_node *term, const char *val_str);
+
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node to a binary value.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] value New value to set in binary format, see @ref howtoDataLYB.
+ * @param[in] value_len Length of @p value.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occurred,
+ * @return LY_ERR value on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lyd_change_term_bin(struct lyd_node *term, const void *value, size_t value_len);
+
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node to a canonical string value.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] val_str New value to set in canonical (or JSON if no defined) format. If the value is not
+ * canonical, it may lead to unexpected behavior.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occurred,
+ * @return LY_ERR value on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lyd_change_term_canon(struct lyd_node *term, const char *val_str);
+
+/**
+ * @brief Change the value of a metadata instance.
+ *
+ * @param[in] meta Metadata to change.
+ * @param[in] val_str New value to set, any prefixes are expected in JSON format.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_ENOT if the values were equal and no change occurred,
+ * @return LY_ERR value on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lyd_change_meta(struct lyd_meta *meta, const char *val_str);
+
+/**
+ * @brief Insert a child into a parent.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] parent Parent node to insert into.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_insert_child(struct lyd_node *parent, struct lyd_node *node);
+
+/**
+ * @brief Insert a node into siblings.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] sibling Siblings to insert into, can even be NULL.
+ * @param[in] node Node to insert.
+ * @param[out] first Optionally return the first sibling after insertion. Can be the address of @p sibling.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first);
+
+/**
+ * @brief Insert a node before another node, can be used only for user-ordered nodes.
+ * If inserting several siblings, each of them must be inserted individually.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ *
+ * @param[in] sibling Sibling node to insert before.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Insert a node after another node, can be used only for user-ordered nodes.
+ * If inserting several siblings, each of them must be inserted individually.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ *
+ * @param[in] sibling Sibling node to insert after.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Unlink the specified node with all the following siblings.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children and following siblings).
+ */
+LIBYANG_API_DECL void lyd_unlink_siblings(struct lyd_node *node);
+
+/**
+ * @brief Unlink the specified data subtree.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children).
+ */
+LIBYANG_API_DECL void lyd_unlink_tree(struct lyd_node *node);
+
+/**
+ * @brief Free all the nodes (even parents of the node) in the data tree.
+ *
+ * @param[in] node Any of the nodes inside the tree.
+ */
+LIBYANG_API_DECL void lyd_free_all(struct lyd_node *node);
+
+/**
+ * @brief Free all the sibling nodes (preceding as well as succeeding).
+ *
+ * @param[in] node Any of the sibling nodes to free.
+ */
+LIBYANG_API_DECL void lyd_free_siblings(struct lyd_node *node);
+
+/**
+ * @brief Free (and unlink) the specified data (sub)tree.
+ *
+ * @param[in] node Root of the (sub)tree to be freed.
+ */
+LIBYANG_API_DECL void lyd_free_tree(struct lyd_node *node);
+
+/**
+ * @brief Free a single metadata instance.
+ *
+ * @param[in] meta Metadata to free.
+ */
+LIBYANG_API_DECL void lyd_free_meta_single(struct lyd_meta *meta);
+
+/**
+ * @brief Free the metadata instance with any following instances.
+ *
+ * @param[in] meta Metadata to free.
+ */
+LIBYANG_API_DECL void lyd_free_meta_siblings(struct lyd_meta *meta);
+
+/**
+ * @brief Free a single attribute.
+ *
+ * @param[in] ctx Context where the attributes were created.
+ * @param[in] attr Attribute to free.
+ */
+LIBYANG_API_DECL void lyd_free_attr_single(const struct ly_ctx *ctx, struct lyd_attr *attr);
+
+/**
+ * @brief Free the attribute with any following attributes.
+ *
+ * @param[in] ctx Context where the attributes were created.
+ * @param[in] attr First attribute to free.
+ */
+LIBYANG_API_DECL void lyd_free_attr_siblings(const struct ly_ctx *ctx, struct lyd_attr *attr);
+
+/**
+ * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
+ *
+ * The given node is not modified in any way - it is just checked if the @p value can be set to the node.
+ *
+ * @param[in] ctx libyang context for logging (function does not log errors when @p ctx is NULL)
+ * @param[in] schema Schema node of the @p value.
+ * @param[in] value String value to be checked, it is expected to be in JSON format.
+ * @param[in] value_len Length of the given @p value (mandatory).
+ * @param[in] ctx_node Optional data tree context node for the value (leafref target, instance-identifier).
+ * If not set and is required for the validation to complete, ::LY_EINCOMPLETE is be returned.
+ * @param[out] realtype Optional real type of @p value.
+ * @param[out] canonical Optional canonical value of @p value (in the dictionary).
+ * @return LY_SUCCESS on success
+ * @return LY_EINCOMPLETE in case the @p ctx_node is not provided and it was needed to finish the validation
+ * (e.g. due to require-instance).
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_value_validate(const struct ly_ctx *ctx, const struct lysc_node *schema, const char *value,
+ size_t value_len, const struct lyd_node *ctx_node, const struct lysc_type **realtype, const char **canonical);
+
+/**
+ * @brief Compare the node's value with the given string value. The string value is first validated according to
+ * the (current) node's type.
+ *
+ * @param[in] node Data node to compare.
+ * @param[in] value String value to be compared. It does not need to be in a canonical form - as part of the process,
+ * it is validated and canonized if possible. But it is expected to be in JSON format.
+ * @param[in] value_len Length of the given @p value (mandatory).
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if the values do not match,
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len);
+
+/**
+ * @ingroup datatree
+ * @defgroup datacompareoptions Data compare options
+ * @{
+ * Various options to change the ::lyd_compare_single() and ::lyd_compare_siblings() behavior.
+ */
+#define LYD_COMPARE_FULL_RECURSION 0x01 /* Lists and containers are the same only in case all they children
+ (subtree, so direct as well as indirect children) are the same. By default,
+ containers are the same in case of the same schema node and lists are the same
+ in case of equal keys (keyless lists do the full recursion comparison all the time). */
+#define LYD_COMPARE_DEFAULTS 0x02 /* By default, implicit and explicit default nodes are considered to be equal. This flag
+ changes this behavior and implicit (automatically created default node) and explicit
+ (explicitly created node with the default value) default nodes are considered different. */
+#define LYD_COMPARE_OPAQ 0x04 /* Opaque nodes can normally be never equal to data nodes. Using this flag even
+ opaque nodes members are compared to data node schema and value and can result
+ in a match. */
+/** @} datacompareoptions */
+
+/**
+ * @brief Compare 2 data nodes if they are equivalent.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
+ */
+LIBYANG_API_DECL LY_ERR lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options);
+
+/**
+ * @brief Compare 2 lists of siblings if they are equivalent.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first sibling list to compare.
+ * @param[in] node2 The second sibling list to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @return LY_SUCCESS if all the siblings are equivalent.
+ * @return LY_ENOT if the siblings are not equivalent.
+ */
+LIBYANG_API_DECL LY_ERR lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options);
+
+/**
+ * @brief Compare 2 metadata.
+ *
+ * If @p meta1 and @p meta2 have different contexts, they are never equivalent.
+ *
+ * @param[in] meta1 First metadata.
+ * @param[in] meta2 Second metadata.
+ * @return LY_SUCCESS if the metadata are equivalent.
+ * @return LY_ENOT if not.
+ */
+LIBYANG_API_DECL LY_ERR lyd_compare_meta(const struct lyd_meta *meta1, const struct lyd_meta *meta2);
+
+/**
+ * @ingroup datatree
+ * @defgroup dupoptions Data duplication options
+ *
+ * Various options to change ::lyd_dup_single() and ::lyd_dup_siblings() behavior.
+ *
+ * Default behavior:
+ * - only the specified node is duplicated without siblings, parents, or children.
+ * - all the metadata of the duplicated nodes are also duplicated.
+ * @{
+ */
+
+#define LYD_DUP_RECURSIVE 0x01 /**< Duplicate not just the node but also all the children. Note that
+ list's keys are always duplicated. */
+#define LYD_DUP_NO_META 0x02 /**< Do not duplicate metadata (or attributes) of any node. */
+#define LYD_DUP_WITH_PARENTS 0x04 /**< If a nested node is being duplicated, duplicate also all the parents.
+ Keys are also duplicated for lists. Return value does not change! */
+#define LYD_DUP_WITH_FLAGS 0x08 /**< Also copy any data node flags. That will cause the duplicated data to preserve
+ its validation/default node state. */
+#define LYD_DUP_NO_EXT 0x10 /**< Do not duplicate nodes with the ::LYD_EXT flag (nested extension instance data). */
+
+/** @} dupoptions */
+
+/**
+ * @brief Create a copy of the specified data tree @p node. Schema references are kept the same.
+ *
+ * @param[in] node Data tree node to be duplicated.
+ * @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.
+ * @param[out] dup Optional created copy of the node. Note that in case the parents chain is duplicated for the duplicated
+ * node(s) (when ::LYD_DUP_WITH_PARENTS used), the first duplicated node is still returned.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options,
+ struct lyd_node **dup);
+
+/**
+ * @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] 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.
+ * @param[out] dup Optional created copy of the node. Note that in case the parents chain is duplicated for the duplicated
+ * node(s) (when ::LYD_DUP_WITH_PARENTS used), the first duplicated node is still returned.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx,
+ struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup);
+
+/**
+ * @brief Create a copy of the specified data tree @p node with any following siblings. Schema references are kept the same.
+ *
+ * @param[in] node Data tree node to be duplicated.
+ * @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.
+ * @param[out] dup Optional created copy of the node. Note that in case the parents chain is duplicated for the duplicated
+ * node(s) (when ::LYD_DUP_WITH_PARENTS used), the first duplicated node is still returned.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options,
+ struct lyd_node **dup);
+
+/**
+ * @brief Create a copy of the specified data tree @p node with any following siblings. 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] 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.
+ * @param[out] dup Optional created copy of the node. Note that in case the parents chain is duplicated for the duplicated
+ * node(s) (when ::LYD_DUP_WITH_PARENTS used), the first duplicated node is still returned.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx,
+ struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup);
+
+/**
+ * @brief Create a copy of the metadata.
+ *
+ * @param[in] meta Metadata to copy.
+ * @param[in] parent Node where to append the new metadata.
+ * @param[out] dup Optional created metadata copy.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_dup_meta_single(const struct lyd_meta *meta, struct lyd_node *parent, struct lyd_meta **dup);
+
+/**
+ * @ingroup datatree
+ * @defgroup mergeoptions Data merge options.
+ *
+ * Various options to change ::lyd_merge_tree(), ::lyd_merge_siblings(), and ::lyd_merge_module() behavior.
+ *
+ * Default behavior:
+ * - source data tree is not modified in any way,
+ * - any default nodes in the source are ignored if there are explicit nodes in the target,
+ * - any metadata are ignored - those present in the target are kept, those in the source are not merged.
+ * - any merged nodes flags are set as non-validated.
+ * @{
+ */
+
+#define LYD_MERGE_DESTRUCT 0x01 /**< Spend source data tree in the function, it cannot be used afterwards! */
+#define LYD_MERGE_DEFAULTS 0x02 /**< Default nodes in the source tree replace even explicit nodes in the target. */
+#define LYD_MERGE_WITH_FLAGS 0x04 /**< Merged nodes (those missing in the source) keep their exact flags. */
+
+/** @} mergeoptions */
+
+/**
+ * @brief Merge the source data subtree into the target data tree. Merge may not be complete until validation
+ * is called on the resulting data tree (data from more cases may be present, default and non-default values).
+ *
+ * Example input:
+ *
+ * source (A1) - A2 - A3 target (B1) - B2 - B3
+ * /\ /\ /\ /\ /\ /\
+ * .... .... .... .... .... ....
+ *
+ * result target (A1) - B1 - B2 - B3
+ * /\ /\ /\ /\
+ * .... .... .... ....
+ *
+ * @param[in,out] target Target data tree to merge into, must be a top-level tree. Always points to the first sibling.
+ * @param[in] source Source data tree to merge, must be a top-level tree.
+ * @param[in] options Bitmask of option flags, see @ref mergeoptions.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_merge_tree(struct lyd_node **target, const struct lyd_node *source, uint16_t options);
+
+/**
+ * @brief Merge the source data tree with any following siblings into the target data tree. Merge may not be
+ * complete until validation called on the resulting data tree (data from more cases may be present, default
+ * and non-default values).
+ *
+ * Example input:
+ *
+ * source (A1) - A2 - A3 target (B1) - B2 - B3
+ * /\ /\ /\ /\ /\ /\
+ * .... .... .... .... .... ....
+ *
+ * result target (A1) - A2 - A3 - B1 - B2 - B3
+ * /\ /\ /\ /\ /\ /\
+ * .... .... .... .... .... ....
+ *
+ * @param[in,out] target Target data tree to merge into, must be a top-level tree. Always points to the first sibling.
+ * @param[in] source Source data tree to merge, must be a top-level tree.
+ * @param[in] options Bitmask of option flags, see @ref mergeoptions.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_merge_siblings(struct lyd_node **target, const struct lyd_node *source, uint16_t options);
+
+/**
+ * @brief Callback for matching merge nodes.
+ *
+ * @param[in] trg_node Target data node.
+ * @param[in] src_node Source data node, is NULL if it was actually duplicated (no target node found) and
+ * its copy is @p trg_node.
+ * @param[in] cb_data Arbitrary callback data.
+ * @return LY_ERR value.
+ */
+typedef LY_ERR (*lyd_merge_cb)(struct lyd_node *trg_node, const struct lyd_node *src_node, void *cb_data);
+
+/**
+ * @brief Merge all the nodes of a module from source data tree into the target data tree. Merge may not be
+ * complete until validation called on the resulting data tree (data from more cases may be present, default
+ * and non-default values).
+ *
+ * @param[in,out] target Target data tree to merge into, must be a top-level tree. Always points to the first sibling.
+ * @param[in] source Source data tree to merge, must be a top-level tree.
+ * @param[in] mod Module, whose source data only to consider, NULL for all modules.
+ * @param[in] merge_cb Optional merge callback that will be called for every merged node, before merging its descendants.
+ * If a subtree is being added into target (no matching node found), callback is called only once with the subtree root.
+ * @param[in] cb_data Arbitrary callback data.
+ * @param[in] options Bitmask of option flags, see @ref mergeoptions.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_merge_module(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod,
+ lyd_merge_cb merge_cb, void *cb_data, uint16_t options);
+
+/**
+ * @ingroup datatree
+ * @defgroup diffoptions Data diff options.
+ *
+ * Various options to change ::lyd_diff_tree() and ::lyd_diff_siblings() behavior.
+ *
+ * Default behavior:
+ * - any default nodes are treated as non-existent and ignored.
+ * @{
+ */
+
+#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. */
+
+/** @} diffoptions */
+
+/**
+ * @brief Learn the differences between 2 data trees.
+ *
+ * The resulting diff is represented as a data tree with specific metadata from the internal 'yang'
+ * module. Most importantly, every node has an effective 'operation' metadata. If there is none
+ * defined on the node, it inherits the operation from the nearest parent. Top-level nodes must
+ * always have the 'operation' metadata defined. Additional metadata ('orig-default', 'value',
+ * 'orig-value', 'key', 'orig-key') are used for storing more information about the value in the first
+ * or the second tree.
+ *
+ * The diff tree is completely independent on the @p first and @p second trees, meaning all
+ * the information about the change is stored in the diff and the trees are not needed.
+ *
+ * __!! Caution !!__
+ * The diff tree should never be validated because it may easily not be valid! For example,
+ * when data from one case branch are deleted and data from another branch created - data from both
+ * branches are then stored in the diff tree simultaneously.
+ *
+ * @param[in] first First data tree.
+ * @param[in] second Second data tree.
+ * @param[in] options Bitmask of options flags, see @ref diffoptions.
+ * @param[out] diff Generated diff.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
+ struct lyd_node **diff);
+
+/**
+ * @brief Learn the differences between 2 data trees including all the following siblings.
+ *
+ * Details are mentioned in ::lyd_diff_tree().
+ *
+ * @param[in] first First data tree.
+ * @param[in] second Second data tree.
+ * @param[in] options Bitmask of options flags, see @ref diffoptions.
+ * @param[out] diff Generated diff.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
+ struct lyd_node **diff);
+
+/**
+ * @brief Callback for diff nodes.
+ *
+ * @param[in] diff_node Diff node.
+ * @param[in] data_node Matching node in data.
+ * @param[in] cb_data Arbitrary callback data.
+ * @return LY_ERR value.
+ */
+typedef LY_ERR (*lyd_diff_cb)(const struct lyd_node *diff_node, struct lyd_node *data_node, void *cb_data);
+
+/**
+ * @brief Apply the whole diff on a data tree but restrict the operation to one module.
+ *
+ * __!! Caution !!__
+ * If applying a diff that was created __without__ the ::LYD_DIFF_DEFAULTS flag, there may be some duplicate values
+ * created. Unless the resulting tree is validated (and default values thus consolidated), using it further
+ * (such as applying another diff) may cause unexpected results or errors.
+ *
+ * @param[in,out] data Data to apply the diff on.
+ * @param[in] diff Diff to apply.
+ * @param[in] mod Module, whose diff/data only to consider, NULL for all modules.
+ * @param[in] diff_cb Optional diff callback that will be called for every changed node.
+ * @param[in] cb_data Arbitrary callback data.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff,
+ const struct lys_module *mod, lyd_diff_cb diff_cb, void *cb_data);
+
+/**
+ * @brief Apply the whole diff tree on a data tree.
+ *
+ * Details are mentioned in ::lyd_diff_apply_module().
+ *
+ * @param[in,out] data Data to apply the diff on.
+ * @param[in] diff Diff to apply.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff);
+
+/**
+ * @ingroup datatree
+ * @defgroup diffmergeoptions Data diff merge options.
+ *
+ * Various options to change ::lyd_diff_merge_module(), ::lyd_diff_merge_tree(), and ::lyd_diff_merge_all() behavior.
+ *
+ * Default behavior:
+ * - any default nodes are expected to be a result of validation corrections and not explicitly modified.
+ * @{
+ */
+
+#define LYD_DIFF_MERGE_DEFAULTS 0x01 /**< Default nodes in the diffs are treated as possibly explicitly modified. */
+
+/** @} diffmergeoptions */
+
+/**
+ * @brief Merge 2 diffs into each other but restrict the operation to one module.
+ *
+ * The diffs must be possible to be merged, which is guaranteed only if the source diff was
+ * created on data that had the target diff applied on them. In other words, this sequence is legal
+ *
+ * 1) get diff1 from data1 and data2 -> get data11 from apply diff1 on data1 -> get diff2 from data11 and data3 ->
+ * -> get data 33 from apply diff2 on data1
+ *
+ * and reusing these diffs
+ *
+ * 2) get diff11 from merge diff1 and diff2 -> get data33 from apply diff11 on data1
+ *
+ * @param[in,out] diff Target diff to merge into.
+ * @param[in] src_diff Source diff.
+ * @param[in] mod Module, whose diff only to consider, NULL for all modules.
+ * @param[in] diff_cb Optional diff callback that will be called for every merged node. Param @p diff_node is the source
+ * diff node while @p data_node is the updated target diff node. In case a whole subtree is added, the callback is
+ * called on the root with @p diff_node being NULL.
+ * @param[in] cb_data Arbitrary callback data.
+ * @param[in] options Bitmask of options flags, see @ref diffmergeoptions.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff,
+ const struct lys_module *mod, lyd_diff_cb diff_cb, void *cb_data, uint16_t options);
+
+/**
+ * @brief Merge 2 diff trees into each other.
+ *
+ * Details are mentioned in ::lyd_diff_merge_module().
+ *
+ * @param[in,out] diff_first Target diff first sibling to merge into.
+ * @param[in] diff_parent Target diff parent to merge into.
+ * @param[in] src_sibling Source diff sibling to merge.
+ * @param[in] diff_cb Optional diff callback that will be called for every merged node. Param @p diff_node is the source
+ * diff node while @p data_node is the updated target diff node. In case a whole subtree is added, the callback is
+ * called on the root with @p diff_node being NULL.
+ * @param[in] cb_data Arbitrary callback data.
+ * @param[in] options Bitmask of options flags, see @ref diffmergeoptions.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent,
+ const struct lyd_node *src_sibling, lyd_diff_cb diff_cb, void *cb_data, uint16_t options);
+
+/**
+ * @brief Merge 2 diffs into each other.
+ *
+ * Details are mentioned in ::lyd_diff_merge_module().
+ *
+ * @param[in,out] diff Target diff to merge into.
+ * @param[in] src_diff Source diff.
+ * @param[in] options Bitmask of options flags, see @ref diffmergeoptions.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options);
+
+/**
+ * @brief Reverse a diff and make the opposite changes. Meaning change create to delete, delete to create,
+ * or move from place A to B to move from B to A and so on.
+ *
+ * @param[in] src_diff Diff to reverse.
+ * @param[out] diff Reversed diff.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff);
+
+/**
+ * @brief Deprecated, use ::lyd_find_target() instead.
+ *
+ * @param[in] path Compiled path structure.
+ * @param[in] tree Data tree to be searched.
+ * @return Found target node,
+ * @return NULL if not found.
+ */
+LIBYANG_API_DECL const struct lyd_node_term *lyd_target(const struct ly_path *path, const struct lyd_node *tree);
+
+/**
+ * @brief Types of the different data paths.
+ */
+typedef enum {
+ LYD_PATH_STD, /**< Generic data path used for logging, node searching (::lyd_find_xpath(), ::lys_find_path()) as well as
+ creating new nodes (::lyd_new_path(), ::lyd_new_path2(), ::lyd_new_ext_path()). */
+ LYD_PATH_STD_NO_LAST_PRED /**< Similar to ::LYD_PATH_STD except there is never a predicate on the last node. While it
+ can be used to search for nodes, do not use it to create new data nodes (lists). */
+} LYD_PATH_TYPE;
+
+/**
+ * @brief Generate path of the given node in the requested format.
+ *
+ * @param[in] node Data path of this node will be generated.
+ * @param[in] pathtype Format of the path to generate.
+ * @param[in,out] buffer Prepared buffer of the @p buflen length to store the generated path.
+ * If NULL, memory for the complete path is allocated.
+ * @param[in] buflen Size of the provided @p buffer.
+ * @return NULL in case of memory allocation error, path of the node otherwise.
+ * In case the @p buffer is NULL, the returned string is dynamically allocated and caller is responsible to free it.
+ */
+LIBYANG_API_DECL char *lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size_t buflen);
+
+/**
+ * @brief Find a specific metadata.
+ *
+ * @param[in] first First metadata to consider.
+ * @param[in] module Module of the metadata definition, may be NULL if @p name includes a prefix.
+ * @param[in] name Name of the metadata to find, may not include a prefix (module name) if @p module is set.
+ * @return Found metadata,
+ * @return NULL if not found.
+ */
+LIBYANG_API_DECL struct lyd_meta *lyd_find_meta(const struct lyd_meta *first, const struct lys_module *module,
+ const char *name);
+
+/**
+ * @brief Search in the given siblings (NOT recursively) for the first target instance with the same value.
+ * Uses hashes - should be used whenever possible for best performance.
+ *
+ * @param[in] siblings Siblings to search in including preceding and succeeding nodes.
+ * @param[in] target Target node to find.
+ * @param[out] match Can be NULL, otherwise the found data node.
+ * @return LY_SUCCESS on success, @p match set.
+ * @return LY_ENOTFOUND if not found, @p match set to NULL.
+ * @return LY_ERR value if another error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *target,
+ struct lyd_node **match);
+
+/**
+ * @brief Search in the given siblings for the first schema instance.
+ * Uses hashes - should be used whenever possible for best performance.
+ *
+ * @param[in] siblings Siblings to search in including preceding and succeeding nodes.
+ * @param[in] schema Schema node of the data node to find.
+ * @param[in] key_or_value If it is NULL, the first schema node data instance is found. For nodes with many
+ * instances, it can be set based on the type of @p schema:
+ * LYS_LEAFLIST:
+ * Searched instance value.
+ * LYS_LIST:
+ * Searched instance key values in the form of "[key1='val1'][key2='val2']...".
+ * The keys do not have to be ordered but all of them must be set.
+ *
+ * Note that any explicit values (leaf-list or list key values) will be canonized first
+ * before comparison. But values that do not have a canonical value are expected to be in the
+ * JSON format!
+ * @param[in] val_len Optional length of @p key_or_value in case it is not 0-terminated.
+ * @param[out] match Can be NULL, otherwise the found data node.
+ * @return LY_SUCCESS on success, @p match set.
+ * @return LY_ENOTFOUND if not found, @p match set to NULL.
+ * @return LY_EINVAL if @p schema is a key-less list.
+ * @return LY_ERR value if another error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *schema,
+ const char *key_or_value, size_t val_len, struct lyd_node **match);
+
+/**
+ * @brief Search the given siblings for all the exact same instances of a specific node instance.
+ * Uses hashes to whatever extent possible.
+ *
+ * @param[in] siblings Siblings to search in including preceding and succeeding nodes.
+ * @param[in] target Target node instance to find.
+ * @param[out] set Set with all the found instances. The first item is always the first instance.
+ * @return LY_SUCCESS on success, @p set returned.
+ * @return LY_ENOTFOUND if not found, empty @p set returned.
+ * @return LY_ERR value if another error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_node *target,
+ struct ly_set **set);
+
+/**
+ * @brief Search the given siblings for an opaque node with a specific name.
+ *
+ * @param[in] first First sibling to consider.
+ * @param[in] name Opaque node name to find.
+ * @param[out] match Can be NULL, otherwise the found data node.
+ * @return LY_SUCCESS on success, @p match set.
+ * @return LY_ENOTFOUND if not found, @p match set to NULL.
+ * @return LY_ERR value is an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struct lyd_node **match);
+
+/**
+ * @brief Set a new XPath variable to @p vars.
+ *
+ * @param[in,out] vars Pointer to [sized array](@ref sizedarrays) of XPath variables.
+ * To create a new array, set the @p vars target pointer to NULL.
+ * Otherwise variable named @p name with a value @p value will be added to the @p vars
+ * or its value will be changed if the variable is already defined.
+ * @param[in] name Name of the added/edited variable.
+ * @param[in] value Value of the variable.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value);
+
+/**
+ * @brief Free the XPath variables.
+ *
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ */
+LIBYANG_API_DECL void lyxp_vars_free(struct lyxp_var *vars);
+
+/**
+ * @brief Search in the given data for instances of nodes matching the provided XPath.
+ *
+ * If a list instance is being selected with all its key values specified and ordered
+ * in the form `list[key1=...][key2=...][key3=...]` or a leaf-list instance in the form
+ * `leaf-list[.=...]`, these instances are found using hashes with constant (*O(1)*) complexity
+ * (unless they are defined in top-level). Other predicates can still follow the aforementioned ones.
+ *
+ * @param[in] ctx_node XPath context node.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format. It must evaluate into a node set.
+ * @param[out] set Set of found data nodes. In case the result is a number, a string, or a boolean,
+ * the returned set is empty.
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set);
+
+/**
+ * @brief Search in the given data for instances of nodes matching the provided XPath.
+ *
+ * It is ::lyd_find_xpath() with @p vars added.
+ *
+ * @param[in] ctx_node XPath context node.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] set Set of found data nodes. In case the result is a number, a string, or a boolean,
+ * the returned set is empty.
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars,
+ struct ly_set **set);
+
+/**
+ * @brief Search in the given data for instances of nodes matching the provided XPath.
+ *
+ * It is ::lyd_find_xpath2() with @p tree added so that @p ctx_node may be the root.
+ *
+ * @param[in] ctx_node XPath context node, NULL for the root node.
+ * @param[in] tree Data tree to evaluate on.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] set Set of found data nodes. In case the result is a number, a string, or a boolean,
+ * the returned set is empty.
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath,
+ const struct lyxp_var *vars, struct ly_set **set);
+
+/**
+ * @brief Search in the given data for instances of nodes matching the provided XPath.
+ *
+ * It is ::lyd_find_xpath3() with @p format and @p prefix_data added for special use-cases.
+ *
+ * @param[in] ctx_node XPath context node, NULL for the root node.
+ * @param[in] tree Data tree to evaluate on.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select with prefix in @p format.
+ * @param[in] format Format of any prefixes in @p xpath.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] set Set of found data nodes. In case the result is a number, a string, or a boolean,
+ * the returned set is empty.
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, struct ly_set **set);
+
+/**
+ * @brief Evaluate an XPath on data and return the result converted to boolean.
+ *
+ * Optimizations similar as in ::lyd_find_xpath().
+ *
+ * @param[in] ctx_node XPath context node.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select.
+ * @param[out] result Expression result converted to boolean.
+ * @return LY_SUCCESS on success, @p result is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result);
+
+/**
+ * @brief Evaluate an XPath on data and return the result converted to boolean.
+ *
+ * It is ::lyd_eval_xpath() with @p vars added.
+ *
+ * @param[in] ctx_node XPath context node.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] result Expression result converted to boolean.
+ * @return LY_SUCCESS on success, @p result is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath,
+ const struct lyxp_var *vars, ly_bool *result);
+
+/**
+ * @brief Evaluate an XPath on data and return the result converted to boolean.
+ *
+ * It is ::lyd_eval_xpath2() with @p format and @p prefix_data added for special use-cases.
+ *
+ * @param[in] ctx_node XPath context node.
+ * @param[in] cur_mod Current module of @p xpath, needed for some kinds of @p format.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select.
+ * @param[in] format Format of any prefixes in @p xpath.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] result Expression result converted to boolean.
+ * @return LY_SUCCESS on success, @p result is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod,
+ const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result);
+
+/**
+ * @brief Search in given data for a node uniquely identified by a path.
+ *
+ * Always works in constant (*O(1)*) complexity. To be exact, it is *O(n)* where *n* is the depth
+ * of the path used.
+ *
+ * @param[in] ctx_node Path context node.
+ * @param[in] path [Path](@ref howtoXPath) to find.
+ * @param[in] output Whether to search in RPC/action output nodes or in input nodes.
+ * @param[out] match Can be NULL, otherwise the found data node.
+ * @return LY_SUCCESS on success, @p match is set to the found node.
+ * @return LY_EINCOMPLETE if only a parent of the node was found, @p match is set to this parent node.
+ * @return LY_ENOTFOUND if no nodes in the path were found.
+ * @return LY_ERR on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output,
+ struct lyd_node **match);
+
+/**
+ * @brief Find the target node of a compiled path (::lyd_value instance-identifier).
+ *
+ * @param[in] path Compiled path structure.
+ * @param[in] tree Data tree to be searched.
+ * @param[out] match Can be NULL, otherwise the found data node.
+ * @return LY_SUCCESS on success, @p match is set to the found node.
+ * @return LY_ENOTFOUND if no match was found.
+ * @return LY_ERR on other errors.
+ */
+LIBYANG_API_DECL LY_ERR lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match);
+
+/**
+ * @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[out] time UNIX timestamp.
+ * @param[out] fractions_s Optional fractions of a second, set to NULL if none.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR ly_time_str2time(const char *value, time_t *time, char **fractions_s);
+
+/**
+ * @brief Convert UNIX timestamp and fractions of a second into canonical date-and-time string value.
+ *
+ * @param[in] time UNIX timestamp.
+ * @param[in] fractions_s Fractions of a second, if any.
+ * @param[out] str String date-and-time value in the local timezone.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR ly_time_time2str(time_t time, const char *fractions_s, char **str);
+
+/**
+ * @brief Convert date-and-time from string to timespec.
+ *
+ * @param[in] value Valid string date-and-time value.
+ * @param[out] ts Timespec.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR ly_time_str2ts(const char *value, struct timespec *ts);
+
+/**
+ * @brief Convert timespec into date-and-time string value.
+ *
+ * @param[in] ts Timespec.
+ * @param[out] str String date-and-time value in the local timezone.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR ly_time_ts2str(const struct timespec *ts, char **str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_DATA_H_ */
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
new file mode 100644
index 0000000..f35f8f5
--- /dev/null
+++ b/src/tree_data_common.c
@@ -0,0 +1,1626 @@
+/**
+ * @file tree_data_common.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Parsing and validation common functions for data trees
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "hash_table.h"
+#include "log.h"
+#include "lyb.h"
+#include "parser_data.h"
+#include "plugins_exts.h"
+#include "printer_data.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "validation.h"
+#include "xml.h"
+#include "xpath.h"
+
+/**
+ * @brief Find an entry in duplicate instance cache for an instance. Create it if it does not exist.
+ *
+ * @param[in] first_inst Instance of the cache entry.
+ * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @return Instance cache entry.
+ */
+static struct lyd_dup_inst *
+lyd_dup_inst_get(const struct lyd_node *first_inst, struct lyd_dup_inst **dup_inst_cache)
+{
+ struct lyd_dup_inst *item;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(*dup_inst_cache, u) {
+ if ((*dup_inst_cache)[u].inst_set->dnodes[0] == first_inst) {
+ return &(*dup_inst_cache)[u];
+ }
+ }
+
+ /* it was not added yet, add it now */
+ LY_ARRAY_NEW_RET(LYD_CTX(first_inst), *dup_inst_cache, item, NULL);
+
+ return item;
+}
+
+LY_ERR
+lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct lyd_dup_inst **dup_inst_cache)
+{
+ struct lyd_dup_inst *dup_inst;
+
+ if (!*inst) {
+ /* no match, inst is unchanged */
+ return LY_SUCCESS;
+ }
+
+ /* there can be more exact same instances (even if not allowed in invalid data) and we must make sure we do not
+ * match a single node more times */
+ dup_inst = lyd_dup_inst_get(*inst, dup_inst_cache);
+ LY_CHECK_ERR_RET(!dup_inst, LOGMEM(LYD_CTX(siblings)), LY_EMEM);
+
+ if (!dup_inst->used) {
+ /* we did not cache these instances yet, do so */
+ lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->inst_set);
+ assert(dup_inst->inst_set->count && (dup_inst->inst_set->dnodes[0] == *inst));
+ }
+
+ if (dup_inst->used == dup_inst->inst_set->count) {
+ if (lysc_is_dup_inst_list((*inst)->schema)) {
+ /* we have used all the instances */
+ *inst = NULL;
+ } /* else just keep using the last (ideally only) instance */
+ } else {
+ assert(dup_inst->used < dup_inst->inst_set->count);
+
+ /* use another instance */
+ *inst = dup_inst->inst_set->dnodes[dup_inst->used];
+ ++dup_inst->used;
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lyd_dup_inst_free(struct lyd_dup_inst *dup_inst)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(dup_inst, u) {
+ ly_set_free(dup_inst[u].inst_set, NULL);
+ }
+ LY_ARRAY_FREE(dup_inst);
+}
+
+struct lyd_node *
+lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling, const struct lysc_node **slast,
+ const struct lysc_node *parent, const struct lysc_module *module)
+{
+ const struct lysc_node *siter = NULL;
+ struct lyd_node *match = NULL;
+
+ assert(parent || module);
+ assert(!last || (slast && *slast));
+
+ if (slast) {
+ siter = *slast;
+ }
+
+ if (last && last->next && (last->next->schema == siter)) {
+ /* return next data instance */
+ return last->next;
+ }
+
+ /* find next schema node data instance */
+ while ((siter = lys_getnext(siter, parent, module, 0))) {
+ if (!lyd_find_sibling_val(sibling, siter, NULL, 0, &match)) {
+ break;
+ }
+ }
+
+ if (slast) {
+ *slast = siter;
+ }
+ return match;
+}
+
+struct lyd_node **
+lyd_node_child_p(struct lyd_node *node)
+{
+ assert(node);
+
+ if (!node->schema) {
+ return &((struct lyd_node_opaq *)node)->child;
+ } else {
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ return &((struct lyd_node_inner *)node)->child;
+ default:
+ return NULL;
+ }
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *var_name = NULL, *var_value = NULL;
+ struct lyxp_var *item;
+
+ if (!vars || !name || !value) {
+ return LY_EINVAL;
+ }
+
+ /* If variable is already defined then change its value. */
+ if (*vars && !lyxp_vars_find(*vars, name, 0, &item)) {
+ var_value = strdup(value);
+ LY_CHECK_RET(!var_value, LY_EMEM);
+
+ /* Set new value. */
+ free(item->value);
+ item->value = var_value;
+ } else {
+ var_name = strdup(name);
+ var_value = strdup(value);
+ LY_CHECK_ERR_GOTO(!var_name || !var_value, ret = LY_EMEM, error);
+
+ /* Add new variable. */
+ LY_ARRAY_NEW_GOTO(NULL, *vars, item, ret, error);
+ item->name = var_name;
+ item->value = var_value;
+ }
+
+ return LY_SUCCESS;
+
+error:
+ free(var_name);
+ free(var_value);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyxp_vars_free(struct lyxp_var *vars)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!vars) {
+ return;
+ }
+
+ LY_ARRAY_FOR(vars, u) {
+ free(vars[u].name);
+ free(vars[u].value);
+ }
+
+ LY_ARRAY_FREE(vars);
+}
+
+LIBYANG_API_DEF struct lyd_node *
+lyd_child_no_keys(const struct lyd_node *node)
+{
+ struct lyd_node **children;
+
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ /* opaq node */
+ return ((struct lyd_node_opaq *)node)->child;
+ }
+
+ children = lyd_node_child_p((struct lyd_node *)node);
+ if (children) {
+ struct lyd_node *child = *children;
+
+ while (child && child->schema && (child->schema->flags & LYS_KEY)) {
+ child = child->next;
+ }
+ return child;
+ } else {
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lys_module *
+lyd_owner_module(const struct lyd_node *node)
+{
+ const struct lyd_node_opaq *opaq;
+
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ opaq = (struct lyd_node_opaq *)node;
+ switch (opaq->format) {
+ case LY_VALUE_XML:
+ return opaq->name.module_ns ? ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns) : NULL;
+ case LY_VALUE_JSON:
+ return opaq->name.module_name ? ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name) : NULL;
+ default:
+ return NULL;
+ }
+ }
+
+ return lysc_owner_module(node->schema);
+}
+
+void
+lyd_first_module_sibling(struct lyd_node **node, const struct lys_module *mod)
+{
+ int cmp;
+ struct lyd_node *first;
+ const struct lys_module *own_mod;
+
+ assert(node && mod);
+
+ if (!*node) {
+ return;
+ }
+
+ first = *node;
+ own_mod = lyd_owner_module(first);
+ cmp = own_mod ? strcmp(own_mod->name, mod->name) : 1;
+ if (cmp > 0) {
+ /* there may be some preceding data */
+ while (first->prev->next) {
+ first = first->prev;
+ if (lyd_owner_module(first) == mod) {
+ cmp = 0;
+ break;
+ }
+ }
+ }
+
+ if (cmp == 0) {
+ /* there may be some preceding data belonging to this module */
+ while (first->prev->next) {
+ if (lyd_owner_module(first->prev) != mod) {
+ break;
+ }
+ first = first->prev;
+ }
+ }
+
+ if (cmp < 0) {
+ /* there may be some following data */
+ LY_LIST_FOR(first, first) {
+ if (lyd_owner_module(first) == mod) {
+ cmp = 0;
+ break;
+ }
+ }
+ }
+
+ if (cmp == 0) {
+ /* we have found the first module data node */
+ *node = first;
+ }
+}
+
+const struct lys_module *
+lyd_mod_next_module(struct lyd_node *tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t *i,
+ struct lyd_node **first)
+{
+ struct lyd_node *iter;
+ const struct lys_module *mod;
+
+ /* get the next module */
+ if (module) {
+ if (*i) {
+ mod = NULL;
+ } else {
+ mod = module;
+ ++(*i);
+ }
+ } else {
+ do {
+ mod = ly_ctx_get_module_iter(ctx, i);
+ } while (mod && !mod->implemented);
+ }
+
+ /* find its data */
+ *first = NULL;
+ if (mod) {
+ LY_LIST_FOR(tree, iter) {
+ if (lyd_owner_module(iter) == mod) {
+ *first = iter;
+ break;
+ }
+ }
+ }
+
+ return mod;
+}
+
+const struct lys_module *
+lyd_data_next_module(struct lyd_node **next, struct lyd_node **first)
+{
+ const struct lys_module *mod;
+
+ if (!*next) {
+ /* all data traversed */
+ *first = NULL;
+ return NULL;
+ }
+
+ *first = *next;
+
+ /* prepare next */
+ mod = lyd_owner_module(*next);
+ LY_LIST_FOR(*next, *next) {
+ if (lyd_owner_module(*next) != mod) {
+ break;
+ }
+ }
+
+ return mod;
+}
+
+LY_ERR
+lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value,
+ size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, ly_bool *incomplete)
+{
+ LY_ERR ret;
+ struct ly_err_item *err = NULL;
+ uint32_t options = (dynamic && *dynamic ? LYPLG_TYPE_STORE_DYNAMIC : 0);
+
+ if (!value) {
+ value = "";
+ }
+ if (incomplete) {
+ *incomplete = 0;
+ }
+
+ ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node, val, NULL, &err);
+ if (dynamic) {
+ *dynamic = 0;
+ }
+
+ if (ret == LY_EINCOMPLETE) {
+ if (incomplete) {
+ *incomplete = 1;
+ }
+ } else if (ret) {
+ if (err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ } else {
+ LOGVAL(ctx, LYVE_OTHER, "Storing value failed.");
+ }
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *val,
+ const struct lyd_node *ctx_node, const struct lyd_node *tree)
+{
+ LY_ERR ret;
+ struct ly_err_item *err = NULL;
+
+ assert(type->plugin->validate);
+
+ ret = type->plugin->validate(ctx, type, ctx_node, tree, val, &err);
+ if (ret) {
+ if (err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ } else {
+ LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.", type->plugin->print(ctx, val, LY_VALUE_CANON,
+ NULL, NULL, NULL));
+ }
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, void *prefix_data)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_err_item *err = NULL;
+ struct lyd_value storage;
+ struct lysc_type *type;
+
+ LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
+
+ if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGARG(ctx, node);
+ return LY_EINVAL;
+ }
+
+ type = ((struct lysc_node_leaf *)node)->type;
+ rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data,
+ LYD_HINT_SCHEMA, node, &storage, NULL, &err);
+ if (rc == LY_EINCOMPLETE) {
+ /* actually success since we do not provide the context tree and call validation with
+ * LY_TYPE_OPTS_INCOMPLETE_DATA */
+ rc = LY_SUCCESS;
+ } else if (rc && err) {
+ if (ctx) {
+ /* log only in case the ctx was provided as input parameter */
+ if (err->path) {
+ LOG_LOCSET(NULL, NULL, err->path, NULL);
+ } else {
+ /* use at least the schema path */
+ LOG_LOCSET(node, NULL, NULL, NULL);
+ }
+ LOGVAL_ERRITEM(ctx, err);
+ if (err->path) {
+ LOG_LOCBACK(0, 0, 1, 0);
+ } else {
+ LOG_LOCBACK(1, 0, 0, 0);
+ }
+ }
+ ly_err_free(err);
+ }
+
+ if (!rc) {
+ type->plugin->free(ctx ? ctx : node->module->ctx, &storage);
+ }
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_value_validate(const struct ly_ctx *ctx, const struct lysc_node *schema, const char *value, size_t value_len,
+ const struct lyd_node *ctx_node, const struct lysc_type **realtype, const char **canonical)
+{
+ LY_ERR rc;
+ struct ly_err_item *err = NULL;
+ struct lysc_type *type;
+ struct lyd_value val = {0};
+ ly_bool stored = 0, log = 1;
+
+ LY_CHECK_ARG_RET(ctx, schema, !value_len || value, LY_EINVAL);
+
+ if (!ctx) {
+ ctx = schema->module->ctx;
+ log = 0;
+ }
+ if (!value_len) {
+ value = "";
+ }
+ type = ((struct lysc_node_leaf *)schema)->type;
+
+ /* store */
+ rc = type->plugin->store(ctx, type, value, value_len, 0, LY_VALUE_JSON, NULL,
+ LYD_HINT_DATA, schema, &val, NULL, &err);
+ if (!rc || (rc == LY_EINCOMPLETE)) {
+ stored = 1;
+ }
+
+ if (ctx_node && (rc == LY_EINCOMPLETE)) {
+ /* resolve */
+ rc = type->plugin->validate(ctx, type, ctx_node, ctx_node, &val, &err);
+ }
+
+ if (rc && (rc != LY_EINCOMPLETE) && err) {
+ if (log) {
+ /* log error */
+ if (err->path) {
+ LOG_LOCSET(NULL, NULL, err->path, NULL);
+ } else if (ctx_node) {
+ LOG_LOCSET(NULL, ctx_node, NULL, NULL);
+ } else {
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+ }
+ LOGVAL_ERRITEM(ctx, err);
+ if (err->path) {
+ LOG_LOCBACK(0, 0, 1, 0);
+ } else if (ctx_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ } else {
+ LOG_LOCBACK(1, 0, 0, 0);
+ }
+ }
+ ly_err_free(err);
+ }
+
+ if (!rc || (rc == LY_EINCOMPLETE)) {
+ if (realtype) {
+ /* return realtype */
+ if (val.realtype->basetype == LY_TYPE_UNION) {
+ *realtype = val.subvalue->value.realtype;
+ } else {
+ *realtype = val.realtype;
+ }
+ }
+
+ if (canonical) {
+ /* return canonical value */
+ lydict_insert(ctx, val.realtype->plugin->print(ctx, &val, LY_VALUE_CANON, NULL, NULL, NULL), 0, canonical);
+ }
+ }
+
+ if (stored) {
+ /* free value */
+ type->plugin->free(ctx ? ctx : schema->module->ctx, &val);
+ }
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx;
+ struct lysc_type *type;
+ struct lyd_value val = {0};
+
+ LY_CHECK_ARG_RET(node ? node->schema->module->ctx : NULL, node, value, LY_EINVAL);
+
+ ctx = node->schema->module->ctx;
+ type = ((struct lysc_node_leaf *)node->schema)->type;
+
+ /* store the value */
+ LOG_LOCSET(node->schema, &node->node, NULL, NULL);
+ ret = lyd_value_store(ctx, &val, type, value, value_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL);
+ LOG_LOCBACK(1, 1, 0, 0);
+ LY_CHECK_RET(ret);
+
+ /* compare values */
+ ret = type->plugin->compare(&node->value, &val);
+
+ type->plugin->free(ctx, &val);
+ return ret;
+}
+
+LIBYANG_API_DEF ly_bool
+lyd_is_default(const struct lyd_node *node)
+{
+ const struct lysc_node_leaf *leaf;
+ const struct lysc_node_leaflist *llist;
+ const struct lyd_node_term *term;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!(node->schema->nodetype & LYD_NODE_TERM)) {
+ return 0;
+ }
+
+ term = (const struct lyd_node_term *)node;
+
+ if (node->schema->nodetype == LYS_LEAF) {
+ leaf = (const struct lysc_node_leaf *)node->schema;
+ if (!leaf->dflt) {
+ return 0;
+ }
+
+ /* compare with the default value */
+ if (!leaf->type->plugin->compare(&term->value, leaf->dflt)) {
+ return 1;
+ }
+ } else {
+ llist = (const struct lysc_node_leaflist *)node->schema;
+ if (!llist->dflts) {
+ return 0;
+ }
+
+ LY_ARRAY_FOR(llist->dflts, u) {
+ /* compare with each possible default value */
+ if (!llist->type->plugin->compare(&term->value, llist->dflts[u])) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+LIBYANG_API_DEF uint32_t
+lyd_list_pos(const struct lyd_node *instance)
+{
+ const struct lyd_node *iter = NULL;
+ uint32_t pos = 0;
+
+ if (!instance || !(instance->schema->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
+ return 0;
+ }
+
+ /* data instances are ordered, so we can stop when we found instance of other schema node */
+ for (iter = instance; iter->schema == instance->schema; iter = iter->prev) {
+ if (pos && (iter->next == NULL)) {
+ /* overrun to the end of the siblings list */
+ break;
+ }
+ ++pos;
+ }
+
+ return pos;
+}
+
+LIBYANG_API_DEF struct lyd_node *
+lyd_first_sibling(const struct lyd_node *node)
+{
+ struct lyd_node *start;
+
+ if (!node) {
+ return NULL;
+ }
+
+ /* get the first sibling */
+ if (node->parent) {
+ start = node->parent->child;
+ } else {
+ for (start = (struct lyd_node *)node; start->prev->next; start = start->prev) {}
+ }
+
+ return start;
+}
+
+/**
+ * @brief Check list node parsed into an opaque node for the reason.
+ *
+ * @param[in] node Opaque node.
+ * @param[in] snode Schema node of @p opaq.
+ * @return LY_SUCCESS if the node is valid;
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *snode)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_set key_set = {0};
+ const struct lysc_node *key = NULL;
+ const struct lyd_node *child;
+ const struct lyd_node_opaq *opaq_k;
+ uint32_t i;
+
+ assert(!node->schema);
+
+ /* get all keys into a set */
+ while ((key = lys_getnext(key, snode, NULL, 0)) && (snode->flags & LYS_KEY)) {
+ LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)snode, 1, NULL), cleanup);
+ }
+
+ LY_LIST_FOR(lyd_child(node), child) {
+ if (child->schema) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Unexpected node %s \"%s\".", lys_nodetype2str(child->schema->nodetype),
+ LYD_NAME(child));
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+
+ opaq_k = (struct lyd_node_opaq *)child;
+
+ /* find the key schema node */
+ for (i = 0; i < key_set.count; ++i) {
+ key = key_set.snodes[i];
+ if (!strcmp(key->name, opaq_k->name.name)) {
+ break;
+ }
+ }
+ if (i == key_set.count) {
+ /* some other node, skip */
+ continue;
+ }
+
+ /* key found */
+ ly_set_rm_index(&key_set, i, NULL);
+
+ /* check value */
+ ret = lys_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format,
+ opaq_k->val_prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ if (key_set.count) {
+ /* missing keys */
+ LOGVAL(LYD_CTX(node), LY_VCODE_NOKEY, key_set.snodes[0]->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+cleanup:
+ ly_set_erase(&key_set, NULL);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_parse_opaq_error(const struct lyd_node *node)
+{
+ const struct ly_ctx *ctx;
+ const struct lyd_node_opaq *opaq;
+ const struct lyd_node *parent;
+ const struct lys_module *mod;
+ const struct lysc_node *snode;
+
+ LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, !lyd_parent(node) || lyd_parent(node)->schema, LY_EINVAL);
+
+ ctx = LYD_CTX(node);
+ opaq = (struct lyd_node_opaq *)node;
+ parent = lyd_parent(node);
+
+ if (!opaq->name.module_ns) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Unknown module of node \"%s\".", opaq->name.name);
+ return LY_EVALID;
+ }
+
+ /* module */
+ switch (opaq->format) {
+ case LY_VALUE_XML:
+ if (!parent || strcmp(opaq->name.module_ns, parent->schema->module->ns)) {
+ mod = ly_ctx_get_module_implemented_ns(ctx, opaq->name.module_ns);
+ if (!mod) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module with namespace \"%s\" of node \"%s\" in the context.",
+ opaq->name.module_ns, opaq->name.name);
+ return LY_EVALID;
+ }
+ } else {
+ /* inherit */
+ mod = parent->schema->module;
+ }
+ break;
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ if (!parent || strcmp(opaq->name.module_name, parent->schema->module->name)) {
+ mod = ly_ctx_get_module_implemented(ctx, opaq->name.module_name);
+ if (!mod) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module named \"%s\" of node \"%s\" in the context.",
+ opaq->name.module_name, opaq->name.name);
+ return LY_EVALID;
+ }
+ } else {
+ /* inherit */
+ mod = parent->schema->module;
+ }
+ break;
+ default:
+ LOGERR(ctx, LY_EINVAL, "Unsupported value format.");
+ return LY_EINVAL;
+ }
+
+ /* schema */
+ snode = lys_find_child(parent ? parent->schema : NULL, mod, opaq->name.name, 0, 0, 0);
+ if (!snode && parent && parent->schema && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+ /* maybe output node */
+ snode = lys_find_child(parent->schema, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT);
+ }
+ if (!snode) {
+ if (parent) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found as a child of \"%s\" node.", opaq->name.name,
+ LYD_NAME(parent));
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found in the \"%s\" module.", opaq->name.name, mod->name);
+ }
+ return LY_EVALID;
+ }
+
+ if (snode->nodetype & LYD_NODE_TERM) {
+ /* leaf / leaf-list */
+ LY_CHECK_RET(lys_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data));
+ } else if (snode->nodetype == LYS_LIST) {
+ /* list */
+ LY_CHECK_RET(lyd_parse_opaq_list_error(node, snode));
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ /* inner node */
+ if (opaq->value) {
+ LOGVAL(ctx, LYVE_DATA, "Invalid value \"%s\" for %s \"%s\".", opaq->value,
+ lys_nodetype2str(snode->nodetype), snode->name);
+ return LY_EVALID;
+ }
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Unexpected opaque schema node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name);
+ return LY_EINVAL;
+ }
+
+ LOGERR(ctx, LY_EINVAL, "Unexpected valid opaque node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name);
+ return LY_EINVAL;
+}
+
+LIBYANG_API_DEF const char *
+lyd_value_get_canonical(const struct ly_ctx *ctx, const struct lyd_value *value)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, value, NULL);
+
+ return value->_canonical ? value->_canonical :
+ (const char *)value->realtype->plugin->print(ctx, value, LY_VALUE_CANON, NULL, NULL, NULL);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_any_value_str(const struct lyd_node *any, char **value_str)
+{
+ const struct lyd_node_any *a;
+ struct lyd_node *tree = NULL;
+ const char *str = NULL;
+ ly_bool dynamic = 0;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(NULL, any, value_str, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, any->schema, any->schema->nodetype & LYS_ANYDATA, LY_EINVAL);
+
+ a = (struct lyd_node_any *)any;
+ *value_str = NULL;
+
+ if (!a->value.str) {
+ /* there is no value in the union */
+ return LY_SUCCESS;
+ }
+
+ switch (a->value_type) {
+ case LYD_ANYDATA_LYB:
+ /* parse into a data tree */
+ ret = lyd_parse_data_mem(LYD_CTX(any), a->value.mem, LYD_LYB, LYD_PARSE_ONLY, 0, &tree);
+ LY_CHECK_GOTO(ret, cleanup);
+ dynamic = 1;
+ break;
+ case LYD_ANYDATA_DATATREE:
+ tree = a->value.tree;
+ break;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ /* simply use the string */
+ str = a->value.str;
+ break;
+ }
+
+ if (tree) {
+ /* print into a string */
+ ret = lyd_print_mem(value_str, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ assert(str);
+ *value_str = strdup(str);
+ LY_CHECK_ERR_GOTO(!*value_str, LOGMEM(LYD_CTX(any)), cleanup);
+ }
+
+ /* success */
+
+cleanup:
+ if (dynamic) {
+ lyd_free_all(tree);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_any_copy_value(struct lyd_node *trg, const union lyd_any_value *value, LYD_ANYDATA_VALUETYPE value_type)
+{
+ struct lyd_node_any *t;
+
+ LY_CHECK_ARG_RET(NULL, trg, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, trg->schema, trg->schema->nodetype & LYS_ANYDATA, LY_EINVAL);
+
+ t = (struct lyd_node_any *)trg;
+
+ /* free trg */
+ switch (t->value_type) {
+ case LYD_ANYDATA_DATATREE:
+ lyd_free_all(t->value.tree);
+ break;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ lydict_remove(LYD_CTX(trg), t->value.str);
+ break;
+ case LYD_ANYDATA_LYB:
+ free(t->value.mem);
+ break;
+ }
+ t->value.str = NULL;
+
+ if (!value) {
+ /* only free value in this case */
+ return LY_SUCCESS;
+ }
+
+ /* copy src */
+ t->value_type = value_type;
+ switch (value_type) {
+ case LYD_ANYDATA_DATATREE:
+ if (value->tree) {
+ LY_CHECK_RET(lyd_dup_siblings(value->tree, NULL, LYD_DUP_RECURSIVE, &t->value.tree));
+ }
+ break;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ if (value->str) {
+ LY_CHECK_RET(lydict_insert(LYD_CTX(trg), value->str, 0, &t->value.str));
+ }
+ break;
+ case LYD_ANYDATA_LYB:
+ if (value->mem) {
+ int len = lyd_lyb_data_length(value->mem);
+
+ LY_CHECK_RET(len == -1, LY_EINVAL);
+ t->value.mem = malloc(len);
+ LY_CHECK_ERR_RET(!t->value.mem, LOGMEM(LYD_CTX(trg)), LY_EMEM);
+ memcpy(t->value.mem, value->mem, len);
+ }
+ break;
+ }
+
+ return LY_SUCCESS;
+}
+
+const struct lysc_node *
+lyd_node_schema(const struct lyd_node *node)
+{
+ const struct lysc_node *schema = NULL;
+ const struct lyd_node *prev_iter = NULL, *iter;
+ const struct lys_module *mod;
+
+ if (!node) {
+ return NULL;
+ } else if (node->schema) {
+ return node->schema;
+ }
+
+ /* get schema node of an opaque node */
+ do {
+ /* get next data node */
+ for (iter = node; lyd_parent(iter) != prev_iter; iter = lyd_parent(iter)) {}
+
+ /* get equivalent schema node */
+ if (iter->schema) {
+ schema = iter->schema;
+ } else {
+ /* get module */
+ mod = lyd_owner_module(iter);
+ if (!mod && !schema) {
+ /* top-level opaque node has unknown module */
+ break;
+ }
+
+ /* get schema node */
+ schema = lys_find_child(schema, mod ? mod : schema->module, LYD_NAME(iter), 0, 0, 0);
+ }
+
+ /* remember to move to the descendant */
+ prev_iter = iter;
+ } while (schema && (iter != node));
+
+ return schema;
+}
+
+void
+lyd_cont_set_dflt(struct lyd_node *node)
+{
+ const struct lyd_node *child;
+
+ while (node) {
+ if (!node->schema || (node->flags & LYD_DEFAULT) || !lysc_is_np_cont(node->schema)) {
+ /* not a non-dflt NP container */
+ break;
+ }
+
+ LY_LIST_FOR(lyd_child(node), child) {
+ if (!(child->flags & LYD_DEFAULT)) {
+ break;
+ }
+ }
+ if (child) {
+ /* explicit child, no dflt change */
+ break;
+ }
+
+ /* set the dflt flag */
+ node->flags |= LYD_DEFAULT;
+
+ /* check all parent containers */
+ node = lyd_parent(node);
+ }
+}
+
+/**
+ * @brief Comparison callback to match schema node with a schema of a data node.
+ *
+ * @param[in] val1_p Pointer to the schema node
+ * @param[in] val2_p Pointer to the data node
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lyd_hash_table_schema_val_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct lysc_node *val1;
+ struct lyd_node *val2;
+
+ val1 = *((struct lysc_node **)val1_p);
+ val2 = *((struct lyd_node **)val2_p);
+
+ if (val1 == val2->schema) {
+ /* schema match is enough */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+LY_ERR
+lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node *schema, struct lyd_node **match)
+{
+ struct lyd_node **match_p;
+ struct lyd_node_inner *parent;
+ const struct lysc_node *cur_schema;
+ uint32_t hash;
+ lyht_value_equal_cb ht_cb;
+
+ assert(schema);
+ if (!siblings) {
+ /* no data */
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ parent = siblings->parent;
+ if (parent && parent->schema && parent->children_ht) {
+ /* calculate our hash */
+ hash = dict_hash_multi(0, schema->module->name, strlen(schema->module->name));
+ hash = dict_hash_multi(hash, schema->name, strlen(schema->name));
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ /* use special hash table function */
+ ht_cb = lyht_set_cb(parent->children_ht, lyd_hash_table_schema_val_equal);
+
+ /* find by hash */
+ if (!lyht_find(parent->children_ht, &schema, hash, (void **)&match_p)) {
+ siblings = *match_p;
+ } else {
+ /* not found */
+ siblings = NULL;
+ }
+
+ /* set the original hash table compare function back */
+ lyht_set_cb(parent->children_ht, ht_cb);
+ } else {
+ /* find first sibling */
+ if (siblings->parent) {
+ siblings = siblings->parent->child;
+ } else {
+ while (siblings->prev->next) {
+ siblings = siblings->prev;
+ }
+ }
+
+ /* search manually without hashes */
+ for ( ; siblings; siblings = siblings->next) {
+ cur_schema = lyd_node_schema(siblings);
+ if (!cur_schema) {
+ /* some unknown opaque node */
+ continue;
+ }
+
+ /* schema match is enough */
+ if (cur_schema->module->ctx == schema->module->ctx) {
+ if (cur_schema == schema) {
+ break;
+ }
+ } else {
+ if (!strcmp(cur_schema->name, schema->name) && !strcmp(cur_schema->module->name, schema->module->name)) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!siblings) {
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+ }
+
+ if (match) {
+ *match = (struct lyd_node *)siblings;
+ }
+ return LY_SUCCESS;
+}
+
+void
+lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod)
+{
+ if (*root && (lyd_owner_module(*root) != mod)) {
+ /* there are no data of mod so this is simply the first top-level sibling */
+ mod = NULL;
+ }
+
+ if ((*root != to_del) || (*root)->parent) {
+ return;
+ }
+
+ if (mod && (*root)->prev->next && (!(*root)->next || (lyd_owner_module(to_del) != lyd_owner_module((*root)->next)))) {
+ /* there are no more nodes from mod, simply get the first top-level sibling */
+ *root = lyd_first_sibling(*root);
+ } else {
+ *root = (*root)->next;
+ }
+}
+
+LY_ERR
+ly_nested_ext_schema(const struct lyd_node *parent, const struct lysc_node *sparent, const char *prefix,
+ size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
+ const struct lysc_node **snode, struct lysc_ext_instance **ext)
+{
+ LY_ERR r;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_ext_instance *nested_exts = NULL;
+ lyplg_ext_data_snode_clb ext_snode_cb;
+
+ /* check if there are any nested extension instances */
+ if (parent && parent->schema) {
+ nested_exts = parent->schema->exts;
+ } else if (sparent) {
+ nested_exts = sparent->exts;
+ }
+ LY_ARRAY_FOR(nested_exts, u) {
+ if (!nested_exts[u].def->plugin) {
+ /* no plugin */
+ continue;
+ }
+
+ ext_snode_cb = nested_exts[u].def->plugin->snode;
+ if (!ext_snode_cb) {
+ /* not an extension with nested data */
+ continue;
+ }
+
+ /* try to get the schema node */
+ r = ext_snode_cb(&nested_exts[u], parent, sparent, prefix, prefix_len, format, prefix_data, name, name_len, snode);
+ if (!r) {
+ if (ext) {
+ /* data successfully created, remember the ext instance */
+ *ext = &nested_exts[u];
+ }
+ return LY_SUCCESS;
+ } else if (r != LY_ENOT) {
+ /* fatal error */
+ return r;
+ }
+ /* data was not from this module, continue */
+ }
+
+ /* no extensions or none matched */
+ return LY_ENOT;
+}
+
+void
+ly_free_prefix_data(LY_VALUE_FORMAT format, void *prefix_data)
+{
+ struct ly_set *ns_list;
+ struct lysc_prefix *prefixes;
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!prefix_data) {
+ return;
+ }
+
+ switch (format) {
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ ns_list = prefix_data;
+ for (i = 0; i < ns_list->count; ++i) {
+ free(((struct lyxml_ns *)ns_list->objs[i])->prefix);
+ free(((struct lyxml_ns *)ns_list->objs[i])->uri);
+ }
+ ly_set_free(ns_list, free);
+ break;
+ case LY_VALUE_SCHEMA_RESOLVED:
+ prefixes = prefix_data;
+ LY_ARRAY_FOR(prefixes, u) {
+ free(prefixes[u].prefix);
+ }
+ LY_ARRAY_FREE(prefixes);
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ break;
+ }
+}
+
+LY_ERR
+ly_dup_prefix_data(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *prefix_data,
+ void **prefix_data_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxml_ns *ns;
+ struct lysc_prefix *prefixes = NULL, *orig_pref;
+ struct ly_set *ns_list, *orig_ns;
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(!*prefix_data_p);
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ *prefix_data_p = (void *)prefix_data;
+ break;
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* copy all the value prefixes */
+ orig_pref = (struct lysc_prefix *)prefix_data;
+ LY_ARRAY_CREATE_GOTO(ctx, prefixes, LY_ARRAY_COUNT(orig_pref), ret, cleanup);
+ *prefix_data_p = prefixes;
+
+ LY_ARRAY_FOR(orig_pref, u) {
+ if (orig_pref[u].prefix) {
+ prefixes[u].prefix = strdup(orig_pref[u].prefix);
+ LY_CHECK_ERR_GOTO(!prefixes[u].prefix, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+ prefixes[u].mod = orig_pref[u].mod;
+ LY_ARRAY_INCREMENT(prefixes);
+ }
+ break;
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ /* copy all the namespaces */
+ LY_CHECK_GOTO(ret = ly_set_new(&ns_list), cleanup);
+ *prefix_data_p = ns_list;
+
+ orig_ns = (struct ly_set *)prefix_data;
+ for (i = 0; i < orig_ns->count; ++i) {
+ ns = calloc(1, sizeof *ns);
+ LY_CHECK_ERR_GOTO(!ns, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(ns_list, ns, 1, NULL), cleanup);
+
+ if (((struct lyxml_ns *)orig_ns->objs[i])->prefix) {
+ ns->prefix = strdup(((struct lyxml_ns *)orig_ns->objs[i])->prefix);
+ LY_CHECK_ERR_GOTO(!ns->prefix, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+ ns->uri = strdup(((struct lyxml_ns *)orig_ns->objs[i])->uri);
+ LY_CHECK_ERR_GOTO(!ns->uri, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ assert(!prefix_data);
+ *prefix_data_p = NULL;
+ break;
+ }
+
+cleanup:
+ if (ret) {
+ ly_free_prefix_data(format, *prefix_data_p);
+ *prefix_data_p = NULL;
+ }
+ return ret;
+}
+
+LY_ERR
+ly_store_prefix_data(const struct ly_ctx *ctx, const void *value, size_t value_len, LY_VALUE_FORMAT format,
+ const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lys_module *mod;
+ const struct lyxml_ns *ns;
+ struct lyxml_ns *new_ns;
+ struct ly_set *ns_list;
+ struct lysc_prefix *prefixes = NULL, *val_pref;
+ const char *value_iter, *value_next, *value_end;
+ uint32_t substr_len;
+ ly_bool is_prefix;
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ /* copy all referenced modules as prefix - module pairs */
+ if (!*prefix_data_p) {
+ /* new prefix data */
+ LY_ARRAY_CREATE_GOTO(ctx, prefixes, 0, ret, cleanup);
+ *format_p = LY_VALUE_SCHEMA_RESOLVED;
+ *prefix_data_p = prefixes;
+ } else {
+ /* reuse prefix data */
+ assert(*format_p == LY_VALUE_SCHEMA_RESOLVED);
+ prefixes = *prefix_data_p;
+ }
+
+ /* add current module for unprefixed values */
+ LY_ARRAY_NEW_GOTO(ctx, prefixes, val_pref, ret, cleanup);
+ *prefix_data_p = prefixes;
+
+ val_pref->prefix = NULL;
+ val_pref->mod = ((const struct lysp_module *)prefix_data)->mod;
+
+ /* add all used prefixes */
+ value_end = (char *)value + value_len;
+ for (value_iter = value; value_iter; value_iter = value_next) {
+ LY_CHECK_GOTO(ret = ly_value_prefix_next(value_iter, value_end, &substr_len, &is_prefix, &value_next), cleanup);
+ if (is_prefix) {
+ /* we have a possible prefix. Do we already have the prefix? */
+ mod = ly_resolve_prefix(ctx, value_iter, substr_len, *format_p, *prefix_data_p);
+ if (!mod) {
+ mod = ly_resolve_prefix(ctx, value_iter, substr_len, format, prefix_data);
+ if (mod) {
+ assert(*format_p == LY_VALUE_SCHEMA_RESOLVED);
+ /* store a new prefix - module pair */
+ LY_ARRAY_NEW_GOTO(ctx, prefixes, val_pref, ret, cleanup);
+ *prefix_data_p = prefixes;
+
+ val_pref->prefix = strndup(value_iter, substr_len);
+ LY_CHECK_ERR_GOTO(!val_pref->prefix, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ val_pref->mod = mod;
+ } /* else it is not even defined */
+ } /* else the prefix is already present */
+ }
+ }
+ break;
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ /* copy all referenced namespaces as prefix - namespace pairs */
+ if (!*prefix_data_p) {
+ /* new prefix data */
+ LY_CHECK_GOTO(ret = ly_set_new(&ns_list), cleanup);
+ *format_p = format;
+ *prefix_data_p = ns_list;
+ } else {
+ /* reuse prefix data */
+ assert(*format_p == format);
+ ns_list = *prefix_data_p;
+ }
+
+ /* store default namespace */
+ ns = lyxml_ns_get(prefix_data, NULL, 0);
+ if (ns) {
+ new_ns = calloc(1, sizeof *new_ns);
+ LY_CHECK_ERR_GOTO(!new_ns, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(ns_list, new_ns, 1, NULL), cleanup);
+
+ new_ns->prefix = NULL;
+ new_ns->uri = strdup(ns->uri);
+ LY_CHECK_ERR_GOTO(!new_ns->uri, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+
+ /* add all used prefixes */
+ value_end = (char *)value + value_len;
+ for (value_iter = value; value_iter; value_iter = value_next) {
+ LY_CHECK_GOTO(ret = ly_value_prefix_next(value_iter, value_end, &substr_len, &is_prefix, &value_next), cleanup);
+ if (is_prefix) {
+ /* we have a possible prefix. Do we already have the prefix? */
+ ns = lyxml_ns_get(ns_list, value_iter, substr_len);
+ if (!ns) {
+ ns = lyxml_ns_get(prefix_data, value_iter, substr_len);
+ if (ns) {
+ /* store a new prefix - namespace pair */
+ new_ns = calloc(1, sizeof *new_ns);
+ LY_CHECK_ERR_GOTO(!new_ns, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(ns_list, new_ns, 1, NULL), cleanup);
+
+ new_ns->prefix = strndup(value_iter, substr_len);
+ LY_CHECK_ERR_GOTO(!new_ns->prefix, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ new_ns->uri = strdup(ns->uri);
+ LY_CHECK_ERR_GOTO(!new_ns->uri, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ } /* else it is not even defined */
+ } /* else the prefix is already present */
+ }
+ }
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ if (!*prefix_data_p) {
+ /* new prefix data - simply copy all the prefix data */
+ *format_p = format;
+ LY_CHECK_GOTO(ret = ly_dup_prefix_data(ctx, format, prefix_data, prefix_data_p), cleanup);
+ } /* else reuse prefix data - the prefix data are always the same, nothing to do */
+ break;
+ }
+
+cleanup:
+ if (ret) {
+ ly_free_prefix_data(*format_p, *prefix_data_p);
+ *prefix_data_p = NULL;
+ }
+ return ret;
+}
+
+const char *
+ly_format2str(LY_VALUE_FORMAT format)
+{
+ switch (format) {
+ case LY_VALUE_CANON:
+ return "canonical";
+ case LY_VALUE_SCHEMA:
+ return "schema imports";
+ case LY_VALUE_SCHEMA_RESOLVED:
+ return "schema stored mapping";
+ case LY_VALUE_XML:
+ return "XML prefixes";
+ case LY_VALUE_JSON:
+ return "JSON module names";
+ case LY_VALUE_LYB:
+ return "LYB prefixes";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_time_str2time(const char *value, time_t *time, char **fractions_s)
+{
+ struct tm tm = {0};
+ uint32_t i, frac_len;
+ const char *frac;
+ int64_t shift, shift_m;
+ time_t t;
+
+ LY_CHECK_ARG_RET(NULL, value, time, LY_EINVAL);
+
+ tm.tm_year = atoi(&value[0]) - 1900;
+ tm.tm_mon = atoi(&value[5]) - 1;
+ tm.tm_mday = atoi(&value[8]);
+ tm.tm_hour = atoi(&value[11]);
+ tm.tm_min = atoi(&value[14]);
+ tm.tm_sec = atoi(&value[17]);
+
+ t = timegm(&tm);
+ i = 19;
+
+ /* fractions of a second */
+ if (value[i] == '.') {
+ ++i;
+ frac = &value[i];
+ for (frac_len = 0; isdigit(frac[frac_len]); ++frac_len) {}
+
+ i += frac_len;
+ } else {
+ frac = NULL;
+ }
+
+ /* apply offset */
+ if ((value[i] == 'Z') || (value[i] == 'z')) {
+ /* zero shift */
+ shift = 0;
+ } else {
+ shift = strtol(&value[i], NULL, 10);
+ shift = shift * 60 * 60; /* convert from hours to seconds */
+ shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
+ /* correct sign */
+ if (shift < 0) {
+ shift_m *= -1;
+ }
+ /* connect hours and minutes of the shift */
+ shift = shift + shift_m;
+ }
+
+ /* we have to shift to the opposite way to correct the time */
+ t -= shift;
+
+ *time = t;
+ if (fractions_s) {
+ if (frac) {
+ *fractions_s = strndup(frac, frac_len);
+ LY_CHECK_RET(!*fractions_s, LY_EMEM);
+ } else {
+ *fractions_s = NULL;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_time_time2str(time_t time, const char *fractions_s, char **str)
+{
+ struct tm tm;
+ char zoneshift[8];
+ int32_t zonediff_h, zonediff_m;
+
+ LY_CHECK_ARG_RET(NULL, str, LY_EINVAL);
+
+ /* initialize the local timezone */
+ tzset();
+
+#ifdef HAVE_TM_GMTOFF
+ /* convert */
+ if (!localtime_r(&time, &tm)) {
+ return LY_ESYS;
+ }
+
+ /* get timezone offset */
+ if (tm.tm_gmtoff == 0) {
+ /* time is Zulu (UTC) */
+ zonediff_h = 0;
+ zonediff_m = 0;
+ } else {
+ /* timezone offset */
+ zonediff_h = tm.tm_gmtoff / 60 / 60;
+ zonediff_m = tm.tm_gmtoff / 60 % 60;
+ }
+ sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
+#else
+ /* convert */
+ if (!gmtime_r(&time, &tm)) {
+ return LY_ESYS;
+ }
+
+ (void)zonediff_h;
+ (void)zonediff_m;
+ sprintf(zoneshift, "-00:00");
+#endif
+
+ /* print */
+ if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ fractions_s ? "." : "", fractions_s ? fractions_s : "", zoneshift) == -1) {
+ return LY_EMEM;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_time_str2ts(const char *value, struct timespec *ts)
+{
+ LY_ERR rc;
+ char *fractions_s, frac_buf[10];
+ int frac_len;
+
+ LY_CHECK_ARG_RET(NULL, value, ts, LY_EINVAL);
+
+ rc = ly_time_str2time(value, &ts->tv_sec, &fractions_s);
+ LY_CHECK_RET(rc);
+
+ /* convert fractions of a second to nanoseconds */
+ if (fractions_s) {
+ /* init frac_buf with zeroes */
+ memset(frac_buf, '0', 9);
+ frac_buf[9] = '\0';
+
+ frac_len = strlen(fractions_s);
+ memcpy(frac_buf, fractions_s, frac_len > 9 ? 9 : frac_len);
+ ts->tv_nsec = atol(frac_buf);
+ free(fractions_s);
+ } else {
+ ts->tv_nsec = 0;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_time_ts2str(const struct timespec *ts, char **str)
+{
+ char frac_buf[10];
+
+ LY_CHECK_ARG_RET(NULL, ts, str, ((ts->tv_nsec <= 999999999) && (ts->tv_nsec >= 0)), LY_EINVAL);
+
+ /* convert nanoseconds to fractions of a second */
+ if (ts->tv_nsec) {
+ sprintf(frac_buf, "%09ld", ts->tv_nsec);
+ }
+
+ return ly_time_time2str(ts->tv_sec, ts->tv_nsec ? frac_buf : NULL, str);
+}
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
new file mode 100644
index 0000000..bf17a91
--- /dev/null
+++ b/src/tree_data_free.c
@@ -0,0 +1,241 @@
+/**
+ * @file tree_data_free.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Freeing functions for data tree structures
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "dict.h"
+#include "hash_table.h"
+#include "log.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_types.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+
+static void
+lyd_free_meta(struct lyd_meta *meta, ly_bool siblings)
+{
+ struct lyd_meta *iter;
+
+ if (!meta) {
+ return;
+ }
+
+ if (meta->parent) {
+ if (meta->parent->meta == meta) {
+ if (siblings) {
+ meta->parent->meta = NULL;
+ } else {
+ meta->parent->meta = meta->next;
+ }
+ } else {
+ for (iter = meta->parent->meta; iter->next != meta; iter = iter->next) {}
+ if (iter->next) {
+ if (siblings) {
+ iter->next = NULL;
+ } else {
+ iter->next = meta->next;
+ }
+ }
+ }
+ }
+
+ if (!siblings) {
+ meta->next = NULL;
+ }
+
+ for (iter = meta; iter; ) {
+ meta = iter;
+ iter = iter->next;
+
+ lydict_remove(meta->annotation->module->ctx, meta->name);
+ meta->value.realtype->plugin->free(meta->annotation->module->ctx, &meta->value);
+ free(meta);
+ }
+}
+
+LIBYANG_API_DEF void
+lyd_free_meta_single(struct lyd_meta *meta)
+{
+ lyd_free_meta(meta, 0);
+}
+
+LIBYANG_API_DEF void
+lyd_free_meta_siblings(struct lyd_meta *meta)
+{
+ lyd_free_meta(meta, 1);
+}
+
+static void
+lyd_free_attr(const struct ly_ctx *ctx, struct lyd_attr *attr, ly_bool siblings)
+{
+ struct lyd_attr *iter;
+
+ LY_CHECK_ARG_RET(NULL, ctx, );
+ if (!attr) {
+ return;
+ }
+
+ if (attr->parent) {
+ if (attr->parent->attr == attr) {
+ if (siblings) {
+ attr->parent->attr = NULL;
+ } else {
+ attr->parent->attr = attr->next;
+ }
+ } else {
+ for (iter = attr->parent->attr; iter->next != attr; iter = iter->next) {}
+ if (iter->next) {
+ if (siblings) {
+ iter->next = NULL;
+ } else {
+ iter->next = attr->next;
+ }
+ }
+ }
+ }
+
+ if (!siblings) {
+ attr->next = NULL;
+ }
+
+ for (iter = attr; iter; ) {
+ attr = iter;
+ iter = iter->next;
+
+ ly_free_prefix_data(attr->format, attr->val_prefix_data);
+ lydict_remove(ctx, attr->name.name);
+ lydict_remove(ctx, attr->name.prefix);
+ lydict_remove(ctx, attr->name.module_ns);
+ lydict_remove(ctx, attr->value);
+ free(attr);
+ }
+}
+
+LIBYANG_API_DEF void
+lyd_free_attr_single(const struct ly_ctx *ctx, struct lyd_attr *attr)
+{
+ lyd_free_attr(ctx, attr, 0);
+}
+
+LIBYANG_API_DEF void
+lyd_free_attr_siblings(const struct ly_ctx *ctx, struct lyd_attr *attr)
+{
+ lyd_free_attr(ctx, attr, 1);
+}
+
+/**
+ * @brief Free Data (sub)tree.
+ * @param[in] node Data node to be freed.
+ * @param[in] top Recursion flag to unlink the root of the subtree being freed.
+ */
+static void
+lyd_free_subtree(struct lyd_node *node, ly_bool top)
+{
+ struct lyd_node *iter, *next;
+ struct lyd_node_opaq *opaq = NULL;
+
+ assert(node);
+
+ if (!node->schema) {
+ opaq = (struct lyd_node_opaq *)node;
+
+ /* free the children */
+ LY_LIST_FOR_SAFE(lyd_child(node), next, iter) {
+ lyd_free_subtree(iter, 0);
+ }
+
+ lydict_remove(LYD_CTX(opaq), opaq->name.name);
+ lydict_remove(LYD_CTX(opaq), opaq->name.prefix);
+ lydict_remove(LYD_CTX(opaq), opaq->name.module_ns);
+ lydict_remove(LYD_CTX(opaq), opaq->value);
+ ly_free_prefix_data(opaq->format, opaq->val_prefix_data);
+ } else if (node->schema->nodetype & LYD_NODE_INNER) {
+ /* remove children hash table in case of inner data node */
+ lyht_free(((struct lyd_node_inner *)node)->children_ht);
+ ((struct lyd_node_inner *)node)->children_ht = NULL;
+
+ /* free the children */
+ LY_LIST_FOR_SAFE(lyd_child(node), next, iter) {
+ lyd_free_subtree(iter, 0);
+ }
+ } else if (node->schema->nodetype & LYD_NODE_ANY) {
+ /* only frees the value this way */
+ lyd_any_copy_value(node, NULL, 0);
+ } else if (node->schema->nodetype & LYD_NODE_TERM) {
+ ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_CTX(node), &((struct lyd_node_term *)node)->value);
+ }
+
+ if (!node->schema) {
+ lyd_free_attr_siblings(LYD_CTX(node), opaq->attr);
+ } else {
+ /* free the node's metadata */
+ lyd_free_meta_siblings(node->meta);
+ }
+
+ /* unlink only the nodes from the first level, nodes in subtree are freed all, so no unlink is needed */
+ if (top) {
+ lyd_unlink_tree(node);
+ }
+
+ free(node);
+}
+
+LIBYANG_API_DEF void
+lyd_free_tree(struct lyd_node *node)
+{
+ if (!node) {
+ return;
+ }
+
+ lyd_free_subtree(node, 1);
+}
+
+static void
+lyd_free_(struct lyd_node *node, ly_bool top)
+{
+ struct lyd_node *iter, *next;
+
+ if (!node) {
+ return;
+ }
+
+ /* get the first (top-level) sibling */
+ if (top) {
+ for ( ; node->parent; node = lyd_parent(node)) {}
+ }
+ while (node->prev->next) {
+ node = node->prev;
+ }
+
+ LY_LIST_FOR_SAFE(node, next, iter) {
+ /* in case of the top-level nodes (node->parent is NULL), no unlinking needed */
+ lyd_free_subtree(iter, iter->parent ? 1 : 0);
+ }
+}
+
+LIBYANG_API_DEF void
+lyd_free_siblings(struct lyd_node *node)
+{
+ lyd_free_(node, 0);
+}
+
+LIBYANG_API_DEF void
+lyd_free_all(struct lyd_node *node)
+{
+ lyd_free_(node, 1);
+}
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
new file mode 100644
index 0000000..52f99a8
--- /dev/null
+++ b/src/tree_data_hash.c
@@ -0,0 +1,237 @@
+/**
+ * @file tree_data_hash.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Functions to manipulate with the data node's hashes.
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "hash_table.h"
+#include "log.h"
+#include "plugins_types.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+LY_ERR
+lyd_hash(struct lyd_node *node)
+{
+ struct lyd_node *iter;
+ const void *hash_key;
+ ly_bool dyn;
+ size_t key_len;
+
+ if (!node->schema) {
+ return LY_SUCCESS;
+ }
+
+ /* hash always starts with the module and schema name */
+ node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
+
+ if (node->schema->nodetype == LYS_LIST) {
+ if (node->schema->flags & LYS_KEYLESS) {
+ /* key-less list simply calls hash function again with empty key,
+ * just so that it differs from the first-instance hash
+ */
+ node->hash = dict_hash_multi(node->hash, NULL, 0);
+ } else {
+ struct lyd_node_inner *list = (struct lyd_node_inner *)node;
+
+ /* list hash is made up from its keys */
+ for (iter = list->child; iter && iter->schema && (iter->schema->flags & LYS_KEY); iter = iter->next) {
+ struct lyd_node_term *key = (struct lyd_node_term *)iter;
+
+ hash_key = key->value.realtype->plugin->print(NULL, &key->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
+ node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+ if (dyn) {
+ free((void *)hash_key);
+ }
+ }
+ }
+ } else if (node->schema->nodetype == LYS_LEAFLIST) {
+ /* leaf-list adds its hash key */
+ struct lyd_node_term *llist = (struct lyd_node_term *)node;
+
+ hash_key = llist->value.realtype->plugin->print(NULL, &llist->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
+ node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+ if (dyn) {
+ free((void *)hash_key);
+ }
+ }
+
+ /* finish the hash */
+ node->hash = dict_hash_multi(node->hash, NULL, 0);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compare callback for values in hash table.
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lyd_hash_table_val_equal(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
+{
+ struct lyd_node *val1, *val2;
+
+ val1 = *((struct lyd_node **)val1_p);
+ val2 = *((struct lyd_node **)val2_p);
+
+ if (mod) {
+ if (val1 == val2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ if (val1->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ /* match on exact instance */
+ if (!lyd_compare_single(val1, val2, 0)) {
+ return 1;
+ }
+ } else if (val1->schema == val2->schema) {
+ /* just schema match */
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Add single node into children hash table.
+ *
+ * @param[in] ht Children hash table.
+ * @param[in] node Node to insert.
+ * @param[in] empty_ht Whether we started with an empty HT meaning no nodes were inserted yet.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_ht)
+{
+ uint32_t hash;
+
+ assert(ht && node && node->schema);
+
+ /* add node itself */
+ if (lyht_insert(ht, &node, node->hash, NULL)) {
+ LOGINT_RET(LYD_CTX(node));
+ }
+
+ /* add first instance of a (leaf-)list */
+ if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
+ (!node->prev->next || (node->prev->schema != node->schema))) {
+ /* get the simple hash */
+ hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ /* remove any previous stored instance, only if we did not start with an empty HT */
+ if (!empty_ht && node->next && (node->next->schema == node->schema)) {
+ if (lyht_remove(ht, &node->next, hash)) {
+ LOGINT_RET(LYD_CTX(node));
+ }
+ }
+
+ /* in this case there would be the exact same value twice in the hash table, not supported (by the HT) */
+ assert(hash != node->hash);
+
+ /* insert this instance as the first (leaf-)list instance */
+ if (lyht_insert(ht, &node, hash, NULL)) {
+ LOGINT_RET(LYD_CTX(node));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_insert_hash(struct lyd_node *node)
+{
+ struct lyd_node *iter;
+ uint32_t u;
+
+ if (!node->parent || !node->schema || !node->parent->schema) {
+ /* nothing to do */
+ return LY_SUCCESS;
+ }
+
+ /* create parent hash table if required, otherwise just add the new child */
+ if (!node->parent->children_ht) {
+ /* the hash table is created only when the number of children in a node exceeds the
+ * defined minimal limit LYD_HT_MIN_ITEMS
+ */
+ u = 0;
+ LY_LIST_FOR(node->parent->child, iter) {
+ if (iter->schema) {
+ ++u;
+ }
+ }
+ if (u >= LYD_HT_MIN_ITEMS) {
+ /* create hash table, insert all the children */
+ node->parent->children_ht = lyht_new(1, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
+ LY_LIST_FOR(node->parent->child, iter) {
+ if (iter->schema) {
+ LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, iter, 1));
+ }
+ }
+ }
+ } else {
+ LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, node, 0));
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lyd_unlink_hash(struct lyd_node *node)
+{
+ uint32_t hash;
+
+ if (!node->parent || !node->schema || !node->parent->schema || !node->parent->children_ht) {
+ /* not in any HT */
+ return;
+ }
+
+ /* remove from the parent HT */
+ if (lyht_remove(node->parent->children_ht, &node, node->hash)) {
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+
+ /* first instance of the (leaf-)list, needs to be removed from HT */
+ if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
+ /* get the simple hash */
+ hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ /* remove the instance */
+ if (lyht_remove(node->parent->children_ht, &node, hash)) {
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+
+ /* add the next instance */
+ if (node->next && (node->next->schema == node->schema)) {
+ if (lyht_insert(node->parent->children_ht, &node->next, hash, NULL)) {
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+ }
+ }
+}
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
new file mode 100644
index 0000000..fd0792d
--- /dev/null
+++ b/src/tree_data_internal.h
@@ -0,0 +1,590 @@
+/**
+ * @file tree_data_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief internal functions for YANG schema trees.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_DATA_INTERNAL_H_
+#define LY_TREE_DATA_INTERNAL_H_
+
+#include "log.h"
+#include "plugins_types.h"
+#include "tree_data.h"
+
+#include <stddef.h>
+
+struct ly_path_predicate;
+struct lyd_ctx;
+struct lysc_module;
+
+#define LY_XML_SUFFIX ".xml"
+#define LY_XML_SUFFIX_LEN 4
+#define LY_JSON_SUFFIX ".json"
+#define LY_JSON_SUFFIX_LEN 5
+#define LY_LYB_SUFFIX ".lyb"
+#define LY_LYB_SUFFIX_LEN 4
+
+/**
+ * @brief Internal structure for remembering "used" instances of lists with duplicate instances allowed.
+ */
+struct lyd_dup_inst {
+ struct ly_set *inst_set;
+ uint32_t used;
+};
+
+/**
+ * @brief Update a found inst using a duplicate instance cache. Needs to be called for every "used"
+ * (that should not be considered next time) instance.
+ *
+ * @param[in,out] inst Found instance, is updated so that the same instance is not returned twice.
+ * @param[in] siblings Siblings where @p inst was found.
+ * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings,
+ struct lyd_dup_inst **dup_inst_cache);
+
+/**
+ * @brief Free duplicate instance cache.
+ *
+ * @param[in] dup_inst Duplicate instance cache to free.
+ */
+void lyd_dup_inst_free(struct lyd_dup_inst *dup_inst);
+
+/**
+ * @brief Just like ::lys_getnext() but iterates over all data instances of the schema nodes.
+ *
+ * @param[in] last Last returned data node.
+ * @param[in] sibling Data node sibling to search in.
+ * @param[in,out] slast Schema last node, set to NULL for first call and do not change afterwards.
+ * May not be set if the function is used only for any suitable node existence check (such as the existence
+ * of any choice case data).
+ * @param[in] parent Schema parent of the iterated children nodes.
+ * @param[in] module Schema module of the iterated top-level nodes.
+ * @return Next matching data node,
+ * @return NULL if last data node was already returned.
+ */
+struct lyd_node *lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling,
+ const struct lysc_node **slast, const struct lysc_node *parent, const struct lysc_module *module);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's child member,
+ * @return NULL if there is no child pointer.
+ */
+struct lyd_node **lyd_node_child_p(struct lyd_node *node);
+
+/**
+ * @brief Update node pointer to point to the first data node of a module, leave unchanged if there is none.
+ *
+ * @param[in,out] node Node pointer, may be updated.
+ * @param[in] mod Module whose data to search for.
+ */
+void lyd_first_module_sibling(struct lyd_node **node, const struct lys_module *mod);
+
+/**
+ * @brief Iterate over implemented modules for functions that accept specific modules or the whole context.
+ *
+ * @param[in] tree Data tree.
+ * @param[in] module Selected module, NULL for all.
+ * @param[in] ctx Context, NULL for selected modules.
+ * @param[in,out] i Iterator, set to 0 on first call.
+ * @param[out] first First sibling of the returned module.
+ * @return Next module.
+ * @return NULL if all modules were traversed.
+ */
+const struct lys_module *lyd_mod_next_module(struct lyd_node *tree, const struct lys_module *module,
+ const struct ly_ctx *ctx, uint32_t *i, struct lyd_node **first);
+
+/**
+ * @brief Iterate over modules for functions that want to traverse all the top-level data.
+ *
+ * @param[in,out] next Pointer to the next module data, set to first top-level sibling on first call.
+ * @param[out] first First sibling of the returned module.
+ * @return Next module.
+ * @return NULL if all modules were traversed.
+ */
+const struct lys_module *lyd_data_next_module(struct lyd_node **next, struct lyd_node **first);
+
+/**
+ * @brief Get schema node of a data node. Useful especially for opaque nodes.
+ *
+ * @param[in] node Data node to use.
+ * @return Schema node represented by data @p node, NULL if there is none.
+ */
+const struct lysc_node *lyd_node_schema(const struct lyd_node *node);
+
+/**
+ * @brief Set dflt flag for a NP container if applicable, recursively for parents.
+ *
+ * @param[in] node Node whose criteria for the dflt flag has changed.
+ */
+void lyd_cont_set_dflt(struct lyd_node *node);
+
+/**
+ * @brief Search in the given siblings (NOT recursively) for the first schema node data instance.
+ * Uses hashes - should be used whenever possible for best performance.
+ *
+ * @param[in] siblings Siblings to search in including preceding and succeeding nodes.
+ * @param[in] schema Target data node schema to find.
+ * @param[out] match Can be NULL, otherwise the found data node.
+ * @return LY_SUCCESS on success, @p match set.
+ * @return LY_ENOTFOUND if not found, @p match set to NULL.
+ * @return LY_ERR value if another error occurred.
+ */
+LY_ERR lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node *schema, struct lyd_node **match);
+
+/**
+ * @brief Check whether a node to be deleted is the root node, move it if it is.
+ *
+ * @param[in] root Root sibling.
+ * @param[in] to_del Node to be deleted.
+ * @param[in] mod If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
+ * the first top-level sibling.
+ */
+void lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod);
+
+/**
+ * @brief Try to get schema node for data with a parent based on an extension instance.
+ *
+ * @param[in] parent Parsed parent data node. Set if @p sparent is NULL.
+ * @param[in] sparent Schema parent node. Set if @p sparent is NULL.
+ * @param[in] prefix Element prefix, if any.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of @p prefix.
+ * @param[in] prefix_data Format-specific data.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] snode Found schema node, NULL if no suitable was found.
+ * @param[out] ext Optional extension instance that provided @p snode.
+ * @return LY_SUCCESS on success;
+ * @return LY_ENOT if no extension instance parsed the data;
+ * @return LY_ERR on error.
+ */
+LY_ERR ly_nested_ext_schema(const struct lyd_node *parent, const struct lysc_node *sparent, const char *prefix,
+ size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
+ const struct lysc_node **snode, struct lysc_ext_instance **ext);
+
+/**
+ * @brief Free stored prefix data.
+ *
+ * @param[in] format Format of the prefixes.
+ * @param[in] prefix_data Format-specific data to free:
+ * LY_PREF_SCHEMA - const struct lysp_module * (module used for resolving prefixes from imports)
+ * LY_PREF_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module)
+ * LY_PREF_XML - const struct ly_set * (set with defined namespaces stored as ::lyxml_ns)
+ * LY_PREF_JSON - NULL
+ */
+void ly_free_prefix_data(LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @brief Duplicate prefix data.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] format Format of the prefixes in the value.
+ * @param[in] prefix_data Prefix data to duplicate.
+ * @param[out] prefix_data_p Duplicated prefix data.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_dup_prefix_data(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *prefix_data, void **prefix_data_p);
+
+/**
+ * @brief Store used prefixes in a string.
+ *
+ * If @p prefix_data_p are non-NULL, they are treated as valid according to the @p format_p and new possible
+ * prefixes are simply added. This way it is possible to store prefix data for several strings together.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] value Value to be parsed.
+ * @param[in] value_len Length of the @p value.
+ * @param[in] format Format of the prefixes in the value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in,out] format_p Resulting format of the prefixes.
+ * @param[in,out] prefix_data_p Resulting prefix data for the value in format @p format_p.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_store_prefix_data(const struct ly_ctx *ctx, const void *value, size_t value_len, LY_VALUE_FORMAT format,
+ const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p);
+
+/**
+ * @brief Get string name of the format.
+ *
+ * @param[in] format Format whose name to get.
+ * @return Format string name.
+ */
+const char *ly_format2str(LY_VALUE_FORMAT format);
+
+/**
+ * @brief Create a term (leaf/leaf-list) node from a string value.
+ *
+ * Hash is calculated and new node flag is set.
+ *
+ * @param[in] schema Schema node of the new data node.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints [Value hints](@ref lydvalhints) from the parser regarding the value type.
+ * @param[out] incomplete Whether the value needs to be resolved.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node);
+
+/**
+ * @brief Create a term (leaf/leaf-list) node from a parsed value by duplicating it.
+ *
+ * Hash is calculated and new node flag is set.
+ *
+ * @param[in] schema Schema node of the new data node.
+ * @param[in] val Parsed value to use.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_term2(const struct lysc_node *schema, const struct lyd_value *val, struct lyd_node **node);
+
+/**
+ * @brief Create an inner (container/list/RPC/action/notification) node.
+ *
+ * Hash is calculated and new node flag is set except
+ * for list with keys, when the hash is not calculated!
+ * Also, non-presence container has its default flag set.
+ *
+ * @param[in] schema Schema node of the new data node.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node);
+
+/**
+ * @brief Create a list with all its keys (cannot be used for key-less list).
+ *
+ * Hash is calculated and new node flag is set.
+ *
+ * @param[in] schema Schema node of the new data node.
+ * @param[in] predicates Compiled key list predicates.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node);
+
+/**
+ * @brief Create a list with all its keys (cannot be used for key-less list).
+ *
+ * Hash is calculated and new node flag is set.
+ *
+ * @param[in] schema Schema node of the new data node.
+ * @param[in] keys Key list predicates.
+ * @param[in] keys_len Length of @p keys.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_len, struct lyd_node **node);
+
+/**
+ * @brief Create an anyxml/anydata node.
+ *
+ * Hash is calculated and flags are properly set based on @p is_valid.
+ *
+ * @param[in] schema Schema node of the new data node.
+ * @param[in] value Value of the any node.
+ * @param[in] value_type Value type of the value.
+ * @param[in] use_value Whether to use dynamic @p value or duplicate it.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VALUETYPE value_type,
+ ly_bool use_value, struct lyd_node **node);
+
+/**
+ * @brief Create an opaque node.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name, must be set correctly.
+ * @param[in] prefix Element prefix.
+ * @param[in] pref_len Length of @p prefix, must be set correctly.
+ * @param[in] module_key Mandatory key to reference module, can be namespace or name.
+ * @param[in] module_key_len Length of @p module_key, must be set correctly.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value and @p ns.
+ * @param[in] val_prefix_data Format-specific prefix data, param is spent (even in case the function fails):
+ * LY_PREF_SCHEMA - const struct lysp_module * (module used for resolving prefixes from imports)
+ * LY_PREF_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module)
+ * LY_PREF_XML - const struct ly_set * (set with defined namespaces stored as ::lyxml_ns)
+ * LY_PREF_JSON - NULL
+ * @param[in] hints [Hints](@ref lydhints) from the parser regarding the node/value type.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *prefix, size_t pref_len,
+ const char *module_key, size_t module_key_len, const char *value, size_t value_len, ly_bool *dynamic,
+ LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints, struct lyd_node **node);
+
+/**
+ * @brief Check the existence and create any non-existing implicit siblings, recursively for the created nodes.
+ *
+ * @param[in] parent Parent of the potential default values, NULL for top-level siblings.
+ * @param[in,out] first First sibling.
+ * @param[in] sparent Schema parent of the siblings, NULL if schema of @p parent can be used.
+ * @param[in] mod Module of the default values, NULL for nested siblings.
+ * @param[in] node_when Optional set to add nodes with "when" conditions into.
+ * @param[in] node_types Optional set to add nodes with unresolved types into.
+ * @param[in] ext_node Optional set to add nodes with extension instance node callbacks into.
+ * @param[in] impl_opts Implicit options (@ref implicitoptions).
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
+ const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_node,
+ uint32_t impl_opts, struct lyd_node **diff);
+
+/**
+ * @brief Find the next node, before which to insert the new node.
+ *
+ * @param[in] first_sibling First sibling of the nodes to consider.
+ * @param[in] new_node Node that will be inserted.
+ * @return Node to insert after.
+ * @return NULL if the new node should be first.
+ */
+struct lyd_node *lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct lyd_node *new_node);
+
+/**
+ * @brief Insert node after a sibling.
+ *
+ * Handles inserting into NP containers and key-less lists.
+ *
+ * @param[in] sibling Sibling to insert after.
+ * @param[in] node Node to insert.
+ */
+void lyd_insert_after_node(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Insert node before a sibling.
+ *
+ * Handles inserting into NP containers and key-less lists.
+ *
+ * @param[in] sibling Sibling to insert before.
+ * @param[in] node Node to insert.
+ */
+void lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Insert a node into parent/siblings. Order and hashes are fully handled.
+ *
+ * @param[in] parent Parent to insert into, NULL for top-level sibling.
+ * @param[in,out] first_sibling First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set.
+ * @param[in] node Individual node (without siblings) to insert.
+ * @param[in] last If set, do not search for the correct anchor but always insert at the end.
+ */
+void lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling, struct lyd_node *node, ly_bool last);
+
+/**
+ * @brief Insert a metadata (last) into a parent
+ *
+ * @param[in] parent Parent of the metadata.
+ * @param[in] meta Metadata (list) to be added into the @p parent.
+ * @param[in] clear_dflt Whether to clear dflt flag starting from @p parent, recursively all NP containers.
+ */
+void lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt);
+
+/**
+ * @brief Create and insert a metadata (last) into a parent.
+ *
+ * @param[in] parent Parent of the metadata, can be NULL.
+ * @param[in,out] meta Metadata list to add at its end if @p parent is NULL, returned created attribute.
+ * @param[in] mod Metadata module (with the annotation definition).
+ * @param[in] name Attribute name.
+ * @param[in] name_len Length of @p name, must be set correctly.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints [Value hints](@ref lydvalhints) from the parser regarding the value type.
+ * @param[in] ctx_node Value context node, may be NULL for metadata.
+ * @param[in] clear_dflt Whether to clear dflt flag starting from @p parent, recursively all NP containers.
+ * @param[out] incomplete Whether the value needs to be resolved.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
+ size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete);
+
+/**
+ * @brief Insert an attribute (last) into a parent
+ *
+ * @param[in] parent Parent of the attributes.
+ * @param[in] attr Attribute (list) to be added into the @p parent.
+ */
+void lyd_insert_attr(struct lyd_node *parent, struct lyd_attr *attr);
+
+/**
+ * @brief Create and insert a generic attribute (last) into a parent.
+ *
+ * @param[in] parent Parent of the attribute, can be NULL.
+ * @param[in,out] attr Attribute list to add at its end if @p parent is NULL, returned created attribute.
+ * @param[in] ctx libyang context.
+ * @param[in] name Attribute name.
+ * @param[in] name_len Length of @p name, must be set correctly.
+ * @param[in] prefix Attribute prefix.
+ * @param[in] prefix_len Attribute prefix length.
+ * @param[in] module_key Mandatory key to reference module, can be namespace or name.
+ * @param[in] module_key_len Length of @p module_key, must be set correctly.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value and @p ns.
+ * @param[in] val_prefix_data Format-specific prefix data, param is spent (even in case the function fails).
+ * @param[in] hints [Hints](@ref lydhints) from the parser regarding the node/value type.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name,
+ size_t name_len, const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len,
+ const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints);
+
+/**
+ * @brief Store and canonize the given @p value into @p val according to the schema node type rules.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in,out] val Storage for the value.
+ * @param[in] type Type of the value.
+ * @param[in] value Value to be parsed, must not be NULL.
+ * @param[in] value_len Length of the give @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints [Value hints](@ref lydvalhints) from the parser.
+ * @param[in] ctx_node Context schema node.
+ * @param[out] incomplete Optional, set if the value also needs to be resolved.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value,
+ size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ const struct lysc_node *ctx_node, ly_bool *incomplete);
+
+/**
+ * @brief Validate previously incompletely stored value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type Schema type of the value (not the stored one, but the original one).
+ * @param[in,out] val Stored value to resolve.
+ * @param[in] ctx_node Context node for the resolution.
+ * @param[in] tree Data tree for the resolution.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *val,
+ const struct lyd_node *ctx_node, const struct lyd_node *tree);
+
+/**
+ * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value coming
+ * from a schema.
+ *
+ * This function check just the type's restriction, if you want to check also the data tree context (e.g. in case of
+ * require-instance restriction), use ::lyd_value_validate().
+ *
+ * @param[in] ctx libyang context for logging (function does not log errors when @p ctx is NULL)
+ * @param[in] node Schema node for the @p value.
+ * @param[in] value String value to be checked, expected to be in JSON format.
+ * @param[in] value_len Length of the given @p value (mandatory).
+ * @param[in] format Value prefix format.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @return LY_SUCCESS on success
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @defgroup datahash Data nodes hash manipulation
+ * @ingroup datatree
+ * @{
+ */
+
+/**
+ * @brief Generate hash for the node.
+ *
+ * @param[in] node Data node to (re)generate hash value.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_hash(struct lyd_node *node);
+
+/**
+ * @brief Insert hash of the node into the hash table of its parent.
+ *
+ * @param[in] node Data node which hash will be inserted into the ::lyd_node_inner.children_ht hash table of its parent.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_insert_hash(struct lyd_node *node);
+
+/**
+ * @brief Maintain node's parent's children hash table when unlinking the node.
+ *
+ * When completely freeing data tree, it is expected to free the parent's children hash table first, at once.
+ *
+ * @param[in] node The data node being unlinked from its parent.
+ */
+void lyd_unlink_hash(struct lyd_node *node);
+
+/** @} datahash */
+
+/**
+ * @brief Append all list key predicates to path.
+ *
+ * @param[in] node Node with keys to print.
+ * @param[in,out] buffer Buffer to print to.
+ * @param[in,out] buflen Current buffer length.
+ * @param[in,out] bufused Current number of characters used in @p buffer.
+ * @param[in] is_static Whether buffer is static or can be reallocated.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_path_list_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static);
+
+/**
+ * @brief Generate a path similar to ::lyd_path() except read the parents from a set.
+ *
+ * @param[in] dnodes Set with the data nodes, from parent to the last descendant.
+ * @param[in] pathtype Type of data path to generate.
+ * @return Generated data path.
+ */
+char *lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype);
+
+/**
+ * @brief Remove an object on the specific set index keeping the order of the other objects.
+ *
+ * @param[in] set Set from which a node will be removed.
+ * @param[in] index Index of the object to remove in the \p set.
+ * @param[in] destructor Optional function to free the objects being removed.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
+
+#endif /* LY_TREE_DATA_INTERNAL_H_ */
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
new file mode 100644
index 0000000..752b181
--- /dev/null
+++ b/src/tree_data_new.c
@@ -0,0 +1,1914 @@
+/**
+ * @file tree_data_new.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Data tree new functions
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "diff.h"
+#include "hash_table.h"
+#include "in.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "path.h"
+#include "plugins.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_internal.h"
+#include "plugins_types.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "validation.h"
+#include "xml.h"
+#include "xpath.h"
+
+LY_ERR
+lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node)
+{
+ LY_ERR ret;
+ struct lyd_node_term *term;
+
+ assert(schema->nodetype & LYD_NODE_TERM);
+
+ term = calloc(1, sizeof *term);
+ LY_CHECK_ERR_RET(!term, LOGMEM(schema->module->ctx), LY_EMEM);
+
+ term->schema = schema;
+ term->prev = &term->node;
+ term->flags = LYD_NEW;
+
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+ ret = lyd_value_store(schema->module->ctx, &term->value, ((struct lysc_node_leaf *)term->schema)->type, value,
+ value_len, dynamic, format, prefix_data, hints, schema, incomplete);
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_ERR_RET(ret, free(term), ret);
+ lyd_hash(&term->node);
+
+ *node = &term->node;
+ return ret;
+}
+
+LY_ERR
+lyd_create_term2(const struct lysc_node *schema, const struct lyd_value *val, struct lyd_node **node)
+{
+ LY_ERR ret;
+ struct lyd_node_term *term;
+ struct lysc_type *type;
+
+ assert(schema->nodetype & LYD_NODE_TERM);
+ assert(val && val->realtype);
+
+ term = calloc(1, sizeof *term);
+ LY_CHECK_ERR_RET(!term, LOGMEM(schema->module->ctx), LY_EMEM);
+
+ term->schema = schema;
+ term->prev = &term->node;
+ term->flags = LYD_NEW;
+
+ type = ((struct lysc_node_leaf *)schema)->type;
+ ret = type->plugin->duplicate(schema->module->ctx, val, &term->value);
+ if (ret) {
+ LOGERR(schema->module->ctx, ret, "Value duplication failed.");
+ free(term);
+ return ret;
+ }
+ lyd_hash(&term->node);
+
+ *node = &term->node;
+ return ret;
+}
+
+LY_ERR
+lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node)
+{
+ struct lyd_node_inner *in;
+
+ assert(schema->nodetype & LYD_NODE_INNER);
+
+ in = calloc(1, sizeof *in);
+ LY_CHECK_ERR_RET(!in, LOGMEM(schema->module->ctx), LY_EMEM);
+
+ in->schema = schema;
+ in->prev = &in->node;
+ in->flags = LYD_NEW;
+ if ((schema->nodetype == LYS_CONTAINER) && !(schema->flags & LYS_PRESENCE)) {
+ in->flags |= LYD_DEFAULT;
+ }
+
+ /* do not hash list with keys, we need them for the hash */
+ if ((schema->nodetype != LYS_LIST) || (schema->flags & LYS_KEYLESS)) {
+ lyd_hash(&in->node);
+ }
+
+ *node = &in->node;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *list = NULL, *key;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert((schema->nodetype == LYS_LIST) && !(schema->flags & LYS_KEYLESS));
+
+ /* create list */
+ LY_CHECK_GOTO(ret = lyd_create_inner(schema, &list), cleanup);
+
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+
+ /* create and insert all the keys */
+ LY_ARRAY_FOR(predicates, u) {
+ LY_CHECK_GOTO(ret = lyd_create_term2(predicates[u].key, &predicates[u].value, &key), cleanup);
+ lyd_insert_node(list, NULL, key, 0);
+ }
+
+ /* hash having all the keys */
+ lyd_hash(list);
+
+ /* success */
+ *node = list;
+ list = NULL;
+
+cleanup:
+ LOG_LOCBACK(1, 0, 0, 0);
+ lyd_free_tree(list);
+ return ret;
+}
+
+LY_ERR
+lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_len, struct lyd_node **node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *expr = NULL;
+ uint32_t exp_idx = 0;
+ enum ly_path_pred_type pred_type = 0;
+ struct ly_path_predicate *predicates = NULL;
+
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+
+ /* parse keys */
+ LY_CHECK_GOTO(ret = ly_path_parse_predicate(schema->module->ctx, NULL, keys, keys_len, LY_PATH_PREFIX_OPTIONAL,
+ LY_PATH_PRED_KEYS, &expr), cleanup);
+
+ /* compile them */
+ LY_CHECK_GOTO(ret = ly_path_compile_predicate(schema->module->ctx, NULL, NULL, schema, expr, &exp_idx, LY_VALUE_JSON,
+ NULL, &predicates, &pred_type), cleanup);
+
+ /* create the list node */
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, node), cleanup);
+
+cleanup:
+ LOG_LOCBACK(1, 0, 0, 0);
+ lyxp_expr_free(schema->module->ctx, expr);
+ ly_path_predicates_free(schema->module->ctx, pred_type, predicates);
+ return ret;
+}
+
+/**
+ * @brief Convert an anydata value into a datatree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] value Anydata value.
+ * @param[in] value_type Anydata @p value type.
+ * @param[out] tree Parsed data tree.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_create_anydata_datatree(const struct ly_ctx *ctx, const void *value, LYD_ANYDATA_VALUETYPE value_type,
+ struct lyd_node **tree)
+{
+ LY_ERR r;
+ struct ly_in *in = NULL;
+ struct lyd_ctx *lydctx = NULL;
+ uint32_t parse_opts, int_opts;
+
+ *tree = NULL;
+
+ if (!value) {
+ /* empty data tree no matter the value type */
+ return LY_SUCCESS;
+ }
+
+ if (value_type == LYD_ANYDATA_STRING) {
+ /* detect format */
+ if (((char *)value)[0] == '<') {
+ value_type = LYD_ANYDATA_XML;
+ } else if (((char *)value)[0] == '{') {
+ value_type = LYD_ANYDATA_JSON;
+ } else if (!strncmp(value, "lyb", 3)) {
+ value_type = LYD_ANYDATA_LYB;
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Invalid string value of an anydata node.");
+ return LY_EINVAL;
+ }
+ }
+
+ /* create input */
+ LY_CHECK_RET(ly_in_new_memory(value, &in));
+
+ /* set options */
+ parse_opts = LYD_PARSE_ONLY | LYD_PARSE_OPAQ;
+ int_opts = LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+
+ switch (value_type) {
+ case LYD_ANYDATA_DATATREE:
+ case LYD_ANYDATA_STRING:
+ /* unreachable */
+ ly_in_free(in, 0);
+ LOGINT_RET(ctx);
+ case LYD_ANYDATA_XML:
+ r = lyd_parse_xml(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
+ break;
+ case LYD_ANYDATA_JSON:
+ r = lyd_parse_json(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
+ break;
+ case LYD_ANYDATA_LYB:
+ r = lyd_parse_lyb(ctx, NULL, NULL, tree, in, parse_opts | LYD_PARSE_STRICT, 0, int_opts, NULL, NULL, &lydctx);
+ break;
+ }
+ if (lydctx) {
+ lydctx->free(lydctx);
+ }
+ ly_in_free(in, 0);
+
+ if (r) {
+ LOGERR(ctx, LY_EINVAL, "Failed to parse anydata content into a data tree.");
+ return LY_EINVAL;
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VALUETYPE value_type, ly_bool use_value,
+ struct lyd_node **node)
+{
+ LY_ERR ret;
+ struct lyd_node *tree;
+ struct lyd_node_any *any = NULL;
+ union lyd_any_value any_val;
+
+ assert(schema->nodetype & LYD_NODE_ANY);
+
+ any = calloc(1, sizeof *any);
+ LY_CHECK_ERR_RET(!any, LOGMEM(schema->module->ctx), LY_EMEM);
+
+ any->schema = schema;
+ any->prev = &any->node;
+ any->flags = LYD_NEW;
+
+ if ((schema->nodetype == LYS_ANYDATA) && (value_type != LYD_ANYDATA_DATATREE)) {
+ /* only a data tree can be stored */
+ LY_CHECK_GOTO(ret = lyd_create_anydata_datatree(schema->module->ctx, value, value_type, &tree), error);
+ if (use_value) {
+ free((void *)value);
+ }
+ use_value = 1;
+ value = tree;
+ value_type = LYD_ANYDATA_DATATREE;
+ }
+
+ if (use_value) {
+ switch (value_type) {
+ case LYD_ANYDATA_DATATREE:
+ any->value.tree = (void *)value;
+ break;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ LY_CHECK_GOTO(ret = lydict_insert_zc(schema->module->ctx, (void *)value, &any->value.str), error);
+ break;
+ case LYD_ANYDATA_LYB:
+ any->value.mem = (void *)value;
+ break;
+ }
+ any->value_type = value_type;
+ } else {
+ any_val.str = value;
+ LY_CHECK_GOTO(ret = lyd_any_copy_value(&any->node, &any_val, value_type), error);
+ }
+ lyd_hash(&any->node);
+
+ *node = &any->node;
+ return LY_SUCCESS;
+
+error:
+ free(any);
+ return ret;
+}
+
+LY_ERR
+lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *prefix, size_t pref_len,
+ const char *module_key, size_t module_key_len, const char *value, size_t value_len, ly_bool *dynamic,
+ LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints, struct lyd_node **node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node_opaq *opaq;
+
+ assert(ctx && name && name_len && format);
+
+ if (!value_len && (!dynamic || !*dynamic)) {
+ value = "";
+ }
+
+ opaq = calloc(1, sizeof *opaq);
+ LY_CHECK_ERR_GOTO(!opaq, LOGMEM(ctx); ret = LY_EMEM, finish);
+
+ opaq->prev = &opaq->node;
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, name, name_len, &opaq->name.name), finish);
+
+ if (pref_len) {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, prefix, pref_len, &opaq->name.prefix), finish);
+ }
+ if (module_key_len) {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, module_key, module_key_len, &opaq->name.module_ns), finish);
+ }
+ if (dynamic && *dynamic) {
+ LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, (char *)value, &opaq->value), finish);
+ *dynamic = 0;
+ } else {
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, value, value_len, &opaq->value), finish);
+ }
+
+ opaq->format = format;
+ opaq->val_prefix_data = val_prefix_data;
+ opaq->hints = hints;
+ opaq->ctx = ctx;
+
+finish:
+ if (ret) {
+ lyd_free_tree(&opaq->node);
+ ly_free_prefix_data(format, val_prefix_data);
+ } else {
+ *node = &opaq->node;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const char *name, ly_bool output,
+ struct lyd_node **node)
+{
+ LY_ERR r;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct lysc_ext_instance *ext = NULL;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+
+ 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);
+
+ if (!module) {
+ module = parent->schema->module;
+ }
+
+ schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0,
+ LYS_CONTAINER | LYS_NOTIF | LYS_RPC | LYS_ACTION, output ? LYS_GETNEXT_OUTPUT : 0);
+ if (!schema && parent) {
+ r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+ strlen(name), &schema, &ext);
+ LY_CHECK_RET(r && (r != LY_ENOT), r);
+ }
+ LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Inner node (container, notif, RPC, or action) \"%s\" not found.",
+ name), LY_ENOTFOUND);
+
+ LY_CHECK_RET(lyd_create_inner(schema, &ret));
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+ if (node) {
+ *node = ret;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_ext_inner(const struct lysc_ext_instance *ext, const char *name, struct lyd_node **node)
+{
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, node, name, LY_EINVAL);
+
+ schema = lysc_ext_find_node(ext, NULL, name, 0, LYS_CONTAINER | LYS_NOTIF | LYS_RPC | LYS_ACTION, 0);
+ if (!schema) {
+ if (ext->argument) {
+ LOGERR(ctx, LY_EINVAL, "Inner node (not a list) \"%s\" not found in instance \"%s\" of extension %s.",
+ name, ext->argument, ext->def->name);
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Inner node (not a list) \"%s\" not found in instance of extension %s.",
+ name, ext->def->name);
+ }
+ return LY_ENOTFOUND;
+ }
+ LY_CHECK_RET(lyd_create_inner(schema, &ret));
+
+ *node = ret;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @param[in] ap Ordered key values of the new list instance, all must be set. For ::LY_VALUE_LYB, every value must
+ * be followed by the value length.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
+ ly_bool output, struct lyd_node **node, va_list ap)
+{
+ struct lyd_node *ret = NULL, *key;
+ const struct lysc_node *schema, *key_s;
+ struct lysc_ext_instance *ext = NULL;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+ const void *key_val;
+ uint32_t key_len;
+ LY_ERR r, rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+
+ if (!module) {
+ module = parent->schema->module;
+ }
+
+ schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, output ? LYS_GETNEXT_OUTPUT : 0);
+ if (!schema && parent) {
+ r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+ strlen(name), &schema, &ext);
+ LY_CHECK_RET(r && (r != LY_ENOT), r);
+ }
+ LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), LY_ENOTFOUND);
+
+ /* create list inner node */
+ LY_CHECK_RET(lyd_create_inner(schema, &ret));
+
+ /* create and insert all the keys */
+ for (key_s = lysc_node_child(schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
+ if (format == LY_VALUE_LYB) {
+ key_val = va_arg(ap, const void *);
+ key_len = va_arg(ap, uint32_t);
+ } else {
+ key_val = va_arg(ap, const char *);
+ key_len = key_val ? strlen((char *)key_val) : 0;
+ }
+
+ rc = lyd_create_term(key_s, key_val, key_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
+ LY_CHECK_GOTO(rc, cleanup);
+ lyd_insert_node(ret, NULL, key, 1);
+ }
+
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(ret);
+ ret = NULL;
+ } else if (node) {
+ *node = ret;
+ }
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, ly_bool output,
+ struct lyd_node **node, ...)
+{
+ LY_ERR rc;
+ va_list ap;
+
+ va_start(ap, node);
+
+ rc = _lyd_new_list(parent, module, name, LY_VALUE_JSON, output, node, ap);
+
+ va_end(ap);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list_bin(struct lyd_node *parent, const struct lys_module *module, const char *name, ly_bool output,
+ struct lyd_node **node, ...)
+{
+ LY_ERR rc;
+ va_list ap;
+
+ va_start(ap, node);
+
+ rc = _lyd_new_list(parent, module, name, LY_VALUE_LYB, output, node, ap);
+
+ va_end(ap);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list_canon(struct lyd_node *parent, const struct lys_module *module, const char *name, ly_bool output,
+ struct lyd_node **node, ...)
+{
+ LY_ERR rc;
+ va_list ap;
+
+ va_start(ap, node);
+
+ rc = _lyd_new_list(parent, module, name, LY_VALUE_CANON, output, node, ap);
+
+ va_end(ap);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_ext_list(const struct lysc_ext_instance *ext, const char *name, struct lyd_node **node, ...)
+{
+ struct lyd_node *ret = NULL, *key;
+ const struct lysc_node *schema, *key_s;
+ struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+ va_list ap;
+ const char *key_val;
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, ext, node, name, LY_EINVAL);
+
+ schema = lysc_ext_find_node(ext, NULL, name, 0, LYS_LIST, 0);
+ if (!schema) {
+ if (ext->argument) {
+ LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found in instance \"%s\" of extension %s.",
+ name, ext->argument, ext->def->name);
+ } else {
+ LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found in instance of extension %s.", name, ext->def->name);
+ }
+ return LY_ENOTFOUND;
+ }
+ /* create list inner node */
+ LY_CHECK_RET(lyd_create_inner(schema, &ret));
+
+ va_start(ap, node);
+
+ /* create and insert all the keys */
+ for (key_s = lysc_node_child(schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
+ key_val = va_arg(ap, const char *);
+
+ rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
+ NULL, &key);
+ LY_CHECK_GOTO(rc, cleanup);
+ lyd_insert_node(ret, NULL, key, 1);
+ }
+
+cleanup:
+ va_end(ap);
+ if (rc) {
+ lyd_free_tree(ret);
+ ret = NULL;
+ }
+ *node = ret;
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *keys,
+ ly_bool output, struct lyd_node **node)
+{
+ LY_ERR r;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct lysc_ext_instance *ext = NULL;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+
+ 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);
+
+ if (!module) {
+ module = parent->schema->module;
+ }
+ if (!keys) {
+ keys = "";
+ }
+
+ /* find schema node */
+ schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, output ? LYS_GETNEXT_OUTPUT : 0);
+ if (!schema && parent) {
+ r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name, strlen(name),
+ &schema, &ext);
+ LY_CHECK_RET(r && (r != LY_ENOT), r);
+ }
+ LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), LY_ENOTFOUND);
+
+ if ((schema->flags & LYS_KEYLESS) && !keys[0]) {
+ /* key-less list */
+ LY_CHECK_RET(lyd_create_inner(schema, &ret));
+ } else {
+ /* create the list node */
+ LY_CHECK_RET(lyd_create_list2(schema, keys, strlen(keys), &ret));
+ }
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+ if (node) {
+ *node = ret;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create a new term node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be ::LYS_LEAF or ::LYS_LEAFLIST.
+ * @param[in] value Value of the node being created.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Format of @p value.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
+ size_t value_len, LY_VALUE_FORMAT format, ly_bool output, struct lyd_node **node)
+{
+ LY_ERR r;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct lysc_ext_instance *ext = NULL;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+
+ 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);
+
+ if (!module) {
+ module = parent->schema->module;
+ }
+
+ schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_TERM, output ? LYS_GETNEXT_OUTPUT : 0);
+ if (!schema && parent) {
+ r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+ strlen(name), &schema, &ext);
+ LY_CHECK_RET(r && (r != LY_ENOT), r);
+ }
+ LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
+
+ LY_CHECK_RET(lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret));
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+ if (node) {
+ *node = ret;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str,
+ ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_term(parent, module, name, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_JSON, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_term_bin(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
+ size_t value_len, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_term(parent, module, name, value, value_len, LY_VALUE_LYB, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_term_canon(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str,
+ ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_term(parent, module, name, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_CANON, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_ext_term(const struct lysc_ext_instance *ext, const char *name, const char *val_str, struct lyd_node **node)
+{
+ LY_ERR rc;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, node, name, LY_EINVAL);
+
+ schema = lysc_ext_find_node(ext, NULL, name, 0, LYD_NODE_TERM, 0);
+ if (!schema) {
+ if (ext->argument) {
+ LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found in instance \"%s\" of extension %s.",
+ name, ext->argument, ext->def->name);
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found in instance of extension %s.", name, ext->def->name);
+ }
+ return LY_ENOTFOUND;
+ }
+ rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &ret);
+ LY_CHECK_RET(rc);
+
+ *node = ret;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
+ ly_bool use_value, LYD_ANYDATA_VALUETYPE value_type, ly_bool output, struct lyd_node **node)
+{
+ LY_ERR r;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct lysc_ext_instance *ext = NULL;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+
+ 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);
+
+ if (!module) {
+ module = parent->schema->module;
+ }
+
+ schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_ANY, output ? LYS_GETNEXT_OUTPUT : 0);
+ if (!schema && parent) {
+ r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+ strlen(name), &schema, &ext);
+ LY_CHECK_RET(r && (r != LY_ENOT), r);
+ }
+ LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found.", name), LY_ENOTFOUND);
+
+ LY_CHECK_RET(lyd_create_any(schema, value, value_type, use_value, &ret));
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+ if (node) {
+ *node = ret;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_ext_any(const struct lysc_ext_instance *ext, const char *name, const void *value, ly_bool use_value,
+ LYD_ANYDATA_VALUETYPE value_type, struct lyd_node **node)
+{
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
+ struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, node, name, LY_EINVAL);
+
+ schema = lysc_ext_find_node(ext, NULL, name, 0, LYD_NODE_ANY, 0);
+ if (!schema) {
+ if (ext->argument) {
+ LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found in instance \"%s\" of extension %s.",
+ name, ext->argument, ext->def->name);
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found in instance of extension %s.", name, ext->def->name);
+ }
+ return LY_ENOTFOUND;
+ }
+ LY_CHECK_RET(lyd_create_any(schema, value, value_type, use_value, &ret));
+
+ *node = ret;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char *val_str, ly_bool clear_dflt, struct lyd_meta **meta)
+{
+ const char *prefix, *tmp;
+ size_t pref_len, name_len;
+
+ 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);
+ if (!ctx) {
+ ctx = module ? module->ctx : LYD_CTX(parent);
+ }
+
+ if (parent && !parent->schema) {
+ LOGERR(ctx, LY_EINVAL, "Cannot add metadata \"%s\" to an opaque node \"%s\".", name, LYD_NAME(parent));
+ return LY_EINVAL;
+ }
+ if (meta) {
+ *meta = NULL;
+ }
+
+ /* parse the name */
+ tmp = name;
+ if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
+ LOGERR(ctx, LY_EINVAL, "Metadata name \"%s\" is not valid.", name);
+ return LY_EINVAL;
+ }
+
+ /* find the module */
+ if (prefix) {
+ module = ly_ctx_get_module_implemented2(ctx, prefix, pref_len);
+ LY_CHECK_ERR_RET(!module, LOGERR(ctx, LY_EINVAL, "Module \"%.*s\" not found.", (int)pref_len, prefix), LY_ENOTFOUND);
+ }
+
+ /* set value if none */
+ if (!val_str) {
+ val_str = "";
+ }
+
+ return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str), NULL, LY_VALUE_JSON,
+ NULL, LYD_HINT_DATA, parent ? parent->schema : NULL, clear_dflt, NULL);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, ly_bool clear_dflt, const struct lyd_attr *attr,
+ struct lyd_meta **meta)
+{
+ const struct lys_module *mod;
+
+ LY_CHECK_ARG_RET(NULL, ctx, attr, parent || meta, LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL);
+
+ if (parent && !parent->schema) {
+ LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".", ((struct lyd_node_opaq *)parent)->name);
+ return LY_EINVAL;
+ }
+ if (meta) {
+ *meta = NULL;
+ }
+
+ switch (attr->format) {
+ case LY_VALUE_XML:
+ mod = ly_ctx_get_module_implemented_ns(ctx, attr->name.module_ns);
+ if (!mod) {
+ LOGERR(ctx, LY_EINVAL, "Module with namespace \"%s\" not found.", attr->name.module_ns);
+ return LY_ENOTFOUND;
+ }
+ break;
+ case LY_VALUE_JSON:
+ mod = ly_ctx_get_module_implemented(ctx, attr->name.module_name);
+ if (!mod) {
+ LOGERR(ctx, LY_EINVAL, "Module \"%s\" not found.", attr->name.module_name);
+ return LY_ENOTFOUND;
+ }
+ break;
+ default:
+ LOGINT_RET(ctx);
+ }
+
+ return lyd_create_meta(parent, meta, mod, attr->name.name, strlen(attr->name.name), attr->value, strlen(attr->value),
+ NULL, attr->format, attr->val_prefix_data, attr->hints, parent ? parent->schema : NULL, clear_dflt, NULL);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_opaq(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
+ const char *prefix, const char *module_name, struct lyd_node **node)
+{
+ struct lyd_node *ret = NULL;
+
+ 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);
+
+ if (!ctx) {
+ ctx = LYD_CTX(parent);
+ }
+ if (!value) {
+ value = "";
+ }
+
+ LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), prefix, prefix ? strlen(prefix) : 0, module_name,
+ strlen(module_name), value, strlen(value), NULL, LY_VALUE_JSON, NULL, 0, &ret));
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 1);
+ }
+
+ if (node) {
+ *node = ret;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_opaq2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
+ const char *prefix, const char *module_ns, struct lyd_node **node)
+{
+ 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);
+
+ if (!ctx) {
+ ctx = LYD_CTX(parent);
+ }
+ if (!value) {
+ value = "";
+ }
+
+ LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), prefix, prefix ? strlen(prefix) : 0, module_ns,
+ strlen(module_ns), value, strlen(value), NULL, LY_VALUE_XML, NULL, 0, &ret));
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 1);
+ }
+
+ if (node) {
+ *node = ret;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *value,
+ struct lyd_attr **attr)
+{
+ struct lyd_attr *ret = NULL;
+ const struct ly_ctx *ctx;
+ const char *prefix, *tmp;
+ size_t pref_len, name_len, mod_len;
+
+ LY_CHECK_ARG_RET(NULL, parent, !parent->schema, name, LY_EINVAL);
+
+ ctx = LYD_CTX(parent);
+
+ /* parse the name */
+ tmp = name;
+ if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
+ LOGERR(ctx, LY_EINVAL, "Attribute name \"%s\" is not valid.", name);
+ return LY_EVALID;
+ }
+
+ if ((pref_len == 3) && !strncmp(prefix, "xml", 3)) {
+ /* not a prefix but special name */
+ name = prefix;
+ name_len += 1 + pref_len;
+ prefix = NULL;
+ pref_len = 0;
+ }
+
+ /* get the module */
+ if (module_name) {
+ mod_len = strlen(module_name);
+ } else {
+ module_name = prefix;
+ mod_len = pref_len;
+ }
+
+ /* set value if none */
+ if (!value) {
+ value = "";
+ }
+
+ LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, prefix, pref_len, module_name, mod_len, value,
+ strlen(value), NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA));
+
+ if (attr) {
+ *attr = ret;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_attr2(struct lyd_node *parent, const char *module_ns, const char *name, const char *value,
+ struct lyd_attr **attr)
+{
+ struct lyd_attr *ret = NULL;
+ const struct ly_ctx *ctx;
+ const char *prefix, *tmp;
+ size_t pref_len, name_len;
+
+ LY_CHECK_ARG_RET(NULL, parent, !parent->schema, name, LY_EINVAL);
+
+ ctx = LYD_CTX(parent);
+
+ /* parse the name */
+ tmp = name;
+ if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
+ LOGERR(ctx, LY_EINVAL, "Attribute name \"%s\" is not valid.", name);
+ return LY_EVALID;
+ }
+
+ if ((pref_len == 3) && !strncmp(prefix, "xml", 3)) {
+ /* not a prefix but special name */
+ name = prefix;
+ name_len += 1 + pref_len;
+ prefix = NULL;
+ pref_len = 0;
+ }
+
+ /* set value if none */
+ if (!value) {
+ value = "";
+ }
+ if (strchr(value, ':')) {
+ LOGWRN(ctx, "Value \"%s\" prefix will never be interpreted as an XML prefix.", value);
+ }
+
+ LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, prefix, pref_len, module_ns,
+ module_ns ? strlen(module_ns) : 0, value, strlen(value), NULL, LY_VALUE_XML, NULL, LYD_HINT_DATA));
+
+ if (attr) {
+ *attr = ret;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] value New value to set.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Format of @p value.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occured,
+ * @return LY_ERR value on other errors.
+ */
+static LY_ERR
+_lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_VALUE_FORMAT format)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type *type;
+ struct lyd_node_term *t;
+ struct lyd_node *parent;
+ struct lyd_value val;
+ ly_bool dflt_change, val_change;
+
+ assert(term && term->schema && (term->schema->nodetype & LYD_NODE_TERM));
+
+ t = (struct lyd_node_term *)term;
+ type = ((struct lysc_node_leaf *)term->schema)->type;
+
+ /* parse the new value */
+ LOG_LOCSET(term->schema, term, NULL, NULL);
+ ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL);
+ LOG_LOCBACK(term->schema ? 1 : 0, 1, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compare original and new value */
+ if (type->plugin->compare(&t->value, &val)) {
+ /* values differ, switch them */
+ type->plugin->free(LYD_CTX(term), &t->value);
+ t->value = val;
+ val_change = 1;
+ } else {
+ /* same values, free the new stored one */
+ type->plugin->free(LYD_CTX(term), &val);
+ val_change = 0;
+ }
+
+ /* always clear the default flag */
+ if (term->flags & LYD_DEFAULT) {
+ for (parent = term; parent; parent = lyd_parent(parent)) {
+ parent->flags &= ~LYD_DEFAULT;
+ }
+ dflt_change = 1;
+ } else {
+ dflt_change = 0;
+ }
+
+ if (val_change || dflt_change) {
+ /* make the node non-validated */
+ term->flags &= LYD_NEW;
+ }
+
+ if (val_change) {
+ if (term->schema->nodetype == LYS_LEAFLIST) {
+ /* leaf-list needs to be hashed again and re-inserted into parent */
+ lyd_unlink_hash(term);
+ lyd_hash(term);
+ LY_CHECK_GOTO(ret = lyd_insert_hash(term), cleanup);
+ } else if ((term->schema->flags & LYS_KEY) && term->parent) {
+ /* list needs to be updated if its key was changed */
+ assert(term->parent->schema->nodetype == LYS_LIST);
+ lyd_unlink_hash(lyd_parent(term));
+ lyd_hash(lyd_parent(term));
+ LY_CHECK_GOTO(ret = lyd_insert_hash(lyd_parent(term)), cleanup);
+ } /* else leaf that is not a key, its value is not used for its hash so it does not change */
+ }
+
+ /* retrun value */
+ if (!val_change) {
+ if (dflt_change) {
+ /* only default flag change */
+ ret = LY_EEXIST;
+ } else {
+ /* no change */
+ ret = LY_ENOT;
+ }
+ } /* else value changed, LY_SUCCESS */
+
+cleanup:
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_change_term(struct lyd_node *term, const char *val_str)
+{
+ LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+ return _lyd_change_term(term, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_JSON);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_change_term_bin(struct lyd_node *term, const void *value, size_t value_len)
+{
+ LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+ return _lyd_change_term(term, value, value_len, LY_VALUE_LYB);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_change_term_canon(struct lyd_node *term, const char *val_str)
+{
+ LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+ return _lyd_change_term(term, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_CANON);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_change_meta(struct lyd_meta *meta, const char *val_str)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_meta *m2 = NULL;
+ struct lyd_value val;
+ ly_bool val_change;
+
+ LY_CHECK_ARG_RET(NULL, meta, LY_EINVAL);
+
+ if (!val_str) {
+ val_str = "";
+ }
+
+ /* parse the new value into a new meta structure */
+ ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str, strlen(val_str),
+ NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compare original and new value */
+ if (lyd_compare_meta(meta, m2)) {
+ /* values differ, switch them */
+ val = meta->value;
+ meta->value = m2->value;
+ m2->value = val;
+ val_change = 1;
+ } else {
+ val_change = 0;
+ }
+
+ /* retrun value */
+ if (!val_change) {
+ /* no change */
+ ret = LY_ENOT;
+ } /* else value changed, LY_SUCCESS */
+
+cleanup:
+ lyd_free_meta_single(m2);
+ return ret;
+}
+
+/**
+ * @brief Update node value.
+ *
+ * @param[in] node Node to update.
+ * @param[in] value New value to set.
+ * @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[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_ERR ret = LY_SUCCESS;
+ struct lyd_node *new_any;
+
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_NOTIF:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_LIST:
+ /* if it exists, there is nothing to update */
+ *new_parent = NULL;
+ *new_node = NULL;
+ break;
+ case LYS_LEAFLIST:
+ if (!lysc_is_dup_inst_list(node->schema)) {
+ /* if it exists, there is nothing to update */
+ *new_parent = NULL;
+ *new_node = NULL;
+ break;
+ }
+ /* fallthrough */
+ case LYS_LEAF:
+ ret = _lyd_change_term(node, value, value_len, format);
+ if ((ret == LY_SUCCESS) || (ret == LY_EEXIST)) {
+ /* there was an actual change (at least of the default flag) */
+ *new_parent = node;
+ *new_node = node;
+ ret = LY_SUCCESS;
+ } else if (ret == LY_ENOT) {
+ /* no change */
+ *new_parent = NULL;
+ *new_node = NULL;
+ ret = LY_SUCCESS;
+ } /* else error */
+ break;
+ 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));
+
+ /* 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;
+ ((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;
+
+ *new_parent = node;
+ *new_node = node;
+ } else {
+ /* they are equal */
+ *new_parent = NULL;
+ *new_node = NULL;
+ }
+ lyd_free_tree(new_any);
+ break;
+ default:
+ LOGINT(LYD_CTX(node));
+ ret = LY_EINT;
+ break;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, uint32_t options)
+{
+ LY_ERR r;
+ struct ly_path_predicate *pred;
+ struct lyd_value val;
+ const struct lysc_node *schema = NULL;
+ LY_ARRAY_COUNT_TYPE u, new_count;
+ int create = 0;
+
+ assert(path);
+
+ /* go through all the compiled nodes */
+ LY_ARRAY_FOR(path, u) {
+ schema = path[u].node;
+
+ if (lysc_is_dup_inst_list(schema)) {
+ if (path[u].pred_type == LY_PATH_PREDTYPE_NONE) {
+ /* creating a new key-less list or state leaf-list instance */
+ create = 1;
+ new_count = u;
+ } else if (path[u].pred_type != LY_PATH_PREDTYPE_POSITION) {
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+ LOGVAL(schema->module->ctx, LYVE_XPATH, "Invalid predicate for %s \"%s\" in path \"%s\".",
+ lys_nodetype2str(schema->nodetype), schema->name, str_path);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EINVAL;
+ }
+ } else if ((schema->nodetype == LYS_LIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LIST)) {
+ if ((u < LY_ARRAY_COUNT(path) - 1) || !(options & LYD_NEW_PATH_OPAQ)) {
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+ LOGVAL(schema->module->ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path \"%s\".",
+ lys_nodetype2str(schema->nodetype), schema->name, str_path);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EINVAL;
+ } /* else creating an opaque list */
+ } else if ((schema->nodetype == LYS_LEAFLIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
+ r = LY_SUCCESS;
+ if (options & LYD_NEW_PATH_OPAQ) {
+ r = lyd_value_validate(NULL, schema, value, value_len, NULL, NULL, NULL);
+ }
+ if (!r) {
+ /* try to store the value */
+ LY_CHECK_RET(lyd_value_store(schema->module->ctx, &val, ((struct lysc_node_leaflist *)schema)->type,
+ value, value_len, NULL, format, NULL, LYD_HINT_DATA, schema, NULL));
+ ++((struct lysc_type *)val.realtype)->refcount;
+
+ /* store the new predicate so that it is used when searching for this instance */
+ path[u].pred_type = LY_PATH_PREDTYPE_LEAFLIST;
+ LY_ARRAY_NEW_RET(schema->module->ctx, path[u].predicates, pred, LY_EMEM);
+ pred->value = val;
+ } /* else we have opaq flag and the value is not valid, leave no predicate and then create an opaque node */
+ }
+ }
+
+ if (create) {
+ /* hide the nodes that should always be created so they are not found */
+ while (new_count < LY_ARRAY_COUNT(path)) {
+ LY_ARRAY_DECREMENT(path);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create a new node in the data tree based on a path. All node types can be created.
+ *
+ * If @p path points to a list key, the key value from the predicate is used and @p value is ignored.
+ * Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * For key-less lists and state leaf-lists, positional predicates can be used. If no preciate 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
+ * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] ext Extension instance where the node being created is defined. This argument takes effect only for absolute
+ * path or when the relative paths touches document root (top-level). In such cases the present extension instance replaces
+ * searching for the appropriate module.
+ * @param[in] path [Path](@ref howtoXPath) to create.
+ * @param[in] value Value of the new leaf/leaf-list (const char *) in ::LY_VALUE_JSON format. If creating an
+ * anyxml/anydata node, the expected type depends on @p value_type. For other node types, it should be NULL.
+ * @param[in] value_len Length of @p value in bytes. May be 0 if @p value is a zero-terminated string. Ignored when
+ * creating anyxml/anydata nodes.
+ * @param[in] value_type Anyxml/anydata node @p value type.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
+ * @param[out] new_node Optional last node created.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, const char *path,
+ const void *value, size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options,
+ struct lyd_node **new_parent, struct lyd_node **new_node)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lyxp_expr *exp = NULL;
+ struct ly_path *p = NULL;
+ struct lyd_node *nparent = NULL, *nnode = NULL, *node = NULL, *cur_parent;
+ const struct lysc_node *schema;
+ const struct lyd_value *val = NULL;
+ LY_ARRAY_COUNT_TYPE path_idx = 0, orig_count = 0;
+ LY_VALUE_FORMAT format;
+
+ assert(parent || ctx);
+ assert(path && ((path[0] == '/') || parent));
+ assert(!(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE));
+
+ if (!ctx) {
+ ctx = LYD_CTX(parent);
+ }
+ if (value && !value_len) {
+ value_len = strlen(value);
+ }
+ if (options & LYD_NEW_PATH_BIN_VALUE) {
+ format = LY_VALUE_LYB;
+ } else if (options & LYD_NEW_PATH_CANON_VALUE) {
+ format = LY_VALUE_CANON;
+ } else {
+ format = LY_VALUE_JSON;
+ }
+
+ /* parse path */
+ LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_OPTIONAL,
+ LY_PATH_PRED_SIMPLE, &exp), cleanup);
+
+ /* compile path */
+ LY_CHECK_GOTO(ret = ly_path_compile(ctx, NULL, lyd_node_schema(parent), ext, exp, options & LYD_NEW_PATH_OUTPUT ?
+ LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p), cleanup);
+
+ /* check the compiled path before searching existing nodes, it may be shortened */
+ orig_count = LY_ARRAY_COUNT(p);
+ LY_CHECK_GOTO(ret = lyd_new_path_check_find_lypath(p, path, value, value_len, format, options), cleanup);
+
+ /* try to find any existing nodes in the path */
+ if (parent) {
+ ret = ly_path_eval_partial(p, parent, &path_idx, &node);
+ if (ret == LY_SUCCESS) {
+ if (orig_count == LY_ARRAY_COUNT(p)) {
+ /* the node exists, are we supposed to update it or is it just a default? */
+ if (!(options & LYD_NEW_PATH_UPDATE) && !(node->flags & LYD_DEFAULT)) {
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists", path);
+ LOG_LOCBACK(0, 1, 0, 0);
+ ret = LY_EEXIST;
+ goto cleanup;
+ }
+
+ /* update the existing node */
+ ret = lyd_new_path_update(node, value, value_len, value_type, format, &nparent, &nnode);
+ goto cleanup;
+ } /* else we were not searching for the whole path */
+ } else if (ret == LY_EINCOMPLETE) {
+ /* some nodes were found, adjust the iterator to the next segment */
+ ++path_idx;
+ } else if (ret == LY_ENOTFOUND) {
+ /* we will create the nodes from top-level, default behavior (absolute path), or from the parent (relative path) */
+ if (lysc_data_parent(p[0].node)) {
+ node = parent;
+ }
+ } else {
+ /* error */
+ goto cleanup;
+ }
+ }
+
+ /* restore the full path for creating new nodes */
+ while (orig_count > LY_ARRAY_COUNT(p)) {
+ LY_ARRAY_INCREMENT(p);
+ }
+
+ /* create all the non-existing nodes in a loop */
+ for ( ; path_idx < LY_ARRAY_COUNT(p); ++path_idx) {
+ cur_parent = node;
+ schema = p[path_idx].node;
+
+ switch (schema->nodetype) {
+ case LYS_LIST:
+ if (lysc_is_dup_inst_list(schema)) {
+ /* create key-less list instance */
+ LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
+ } else if ((options & LYD_NEW_PATH_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ /* creating opaque list without keys */
+ LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0,
+ schema->module->name, strlen(schema->module->name), NULL, 0, NULL, LY_VALUE_JSON, NULL,
+ LYD_NODEHINT_LIST, &node), cleanup);
+ } else {
+ /* create standard list instance */
+ assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, &node), cleanup);
+ }
+ break;
+ case LYS_CONTAINER:
+ case LYS_NOTIF:
+ case LYS_RPC:
+ case LYS_ACTION:
+ LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
+ break;
+ case LYS_LEAFLIST:
+ if ((options & LYD_NEW_PATH_OPAQ) && (p[path_idx].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
+ /* we have not checked this only for dup-inst lists, otherwise it must be opaque */
+ r = LY_EVALID;
+ if (lysc_is_dup_inst_list(schema)) {
+ /* validate value */
+ r = lyd_value_validate(NULL, schema, value ? value : "", value_len, NULL, NULL, NULL);
+ }
+ if (r && (r != LY_EINCOMPLETE)) {
+ /* creating opaque leaf-list */
+ LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), value, value_len,
+ schema->module->name, strlen(schema->module->name), NULL, 0, NULL, format, NULL,
+ LYD_NODEHINT_LEAFLIST, &node), cleanup);
+ break;
+ }
+ }
+
+ /* get value to set */
+ if (p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST) {
+ val = &p[path_idx].predicates[0].value;
+ }
+
+ /* create a leaf-list instance */
+ if (val) {
+ LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
+ } else {
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA,
+ NULL, &node), cleanup);
+ }
+ break;
+ case LYS_LEAF:
+ if (lysc_is_key(schema) && cur_parent->schema) {
+ /* it must have been already created or some error will occur later */
+ lyd_find_sibling_schema(lyd_child(cur_parent), schema, &node);
+ assert(node);
+ goto next_iter;
+ }
+
+ if (options & LYD_NEW_PATH_OPAQ) {
+ if (cur_parent && !cur_parent->schema) {
+ /* always create opaque nodes for opaque parents */
+ r = LY_ENOT;
+ } else {
+ /* validate value */
+ r = lyd_value_validate(NULL, schema, value ? value : "", value_len, NULL, NULL, NULL);
+ }
+ if (r && (r != LY_EINCOMPLETE)) {
+ /* creating opaque leaf */
+ LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), value, value_len,
+ schema->module->name, strlen(schema->module->name), NULL, 0, NULL, format, NULL, 0, &node),
+ cleanup);
+ break;
+ }
+ }
+
+ /* create a leaf instance */
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL,
+ &node), cleanup);
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ LY_CHECK_GOTO(ret = lyd_create_any(schema, value, value_type, 0, &node), cleanup);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+
+ if (p[path_idx].ext) {
+ node->flags |= LYD_EXT;
+ }
+ if (cur_parent) {
+ /* connect to the parent */
+ lyd_insert_node(cur_parent, NULL, node, 0);
+ } else if (parent) {
+ /* connect to top-level siblings */
+ lyd_insert_node(NULL, &parent, node, 0);
+ }
+
+next_iter:
+ /* update remembered nodes */
+ if (!nparent) {
+ nparent = node;
+ }
+ nnode = node;
+ }
+
+cleanup:
+ lyxp_expr_free(ctx, exp);
+ if (p) {
+ while (orig_count > LY_ARRAY_COUNT(p)) {
+ LY_ARRAY_INCREMENT(p);
+ }
+ }
+ ly_path_free(ctx, p);
+ if (!ret) {
+ /* set out params only on success */
+ if (new_parent) {
+ *new_parent = nparent;
+ }
+ if (new_node) {
+ *new_node = nnode;
+ }
+ } else {
+ lyd_free_tree(nparent);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value, uint32_t options,
+ struct lyd_node **node)
+{
+ LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent,
+ !(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL);
+
+ return lyd_new_path_(parent, ctx, NULL, path, value, 0, LYD_ANYDATA_STRING, options, node, NULL);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent,
+ struct lyd_node **new_node)
+{
+ LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent,
+ !(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(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);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, const char *path, const void *value,
+ uint32_t options, struct lyd_node **node)
+{
+ const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, path, (path[0] == '/') || parent,
+ !(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL);
+
+ return lyd_new_path_(parent, ctx, ext, path, value, 0, LYD_ANYDATA_STRING, options, node, NULL);
+}
+
+LY_ERR
+lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
+ const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_node,
+ uint32_t impl_opts, struct lyd_node **diff)
+{
+ LY_ERR ret;
+ const struct lysc_node *iter = NULL;
+ struct lyd_node *node = NULL;
+ struct lyd_value **dflts;
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t getnext_opts;
+
+ assert(first && (parent || sparent || mod));
+
+ if (!sparent && parent) {
+ sparent = parent->schema;
+ }
+
+ getnext_opts = LYS_GETNEXT_WITHCHOICE;
+ if (impl_opts & LYD_IMPLICIT_OUTPUT) {
+ getnext_opts |= LYS_GETNEXT_OUTPUT;
+ }
+
+ while ((iter = lys_getnext(iter, sparent, mod ? mod->compiled : NULL, getnext_opts))) {
+ if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (iter->flags & LYS_CONFIG_R)) {
+ continue;
+ } else if ((impl_opts & LYD_IMPLICIT_NO_CONFIG) && (iter->flags & LYS_CONFIG_W)) {
+ continue;
+ }
+
+ switch (iter->nodetype) {
+ case LYS_CHOICE:
+ node = lys_getnext_data(NULL, *first, NULL, iter, NULL);
+ if (!node && ((struct lysc_node_choice *)iter)->dflt) {
+ /* create default case data */
+ LY_CHECK_RET(lyd_new_implicit_r(parent, first, &((struct lysc_node_choice *)iter)->dflt->node,
+ NULL, node_when, node_types, ext_node, impl_opts, diff));
+ } else if (node) {
+ /* create any default data in the existing case */
+ assert(node->schema->parent->nodetype == LYS_CASE);
+ LY_CHECK_RET(lyd_new_implicit_r(parent, first, node->schema->parent, NULL, node_when, node_types,
+ ext_node, impl_opts, diff));
+ }
+ break;
+ case LYS_CONTAINER:
+ if (!(iter->flags & LYS_PRESENCE) && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
+ /* create default NP container */
+ LY_CHECK_RET(lyd_create_inner(iter, &node));
+ node->flags = LYD_DEFAULT | (lysc_has_when(iter) ? LYD_WHEN_TRUE : 0);
+ lyd_insert_node(parent, first, node, 0);
+
+ if (lysc_has_when(iter) && node_when) {
+ /* remember to resolve when */
+ LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
+ }
+ if (ext_node) {
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_RET(lyd_validate_node_ext(node, ext_node));
+ }
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+ }
+
+ /* create any default children */
+ LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_when, node_types,
+ ext_node, impl_opts, diff));
+ }
+ break;
+ case LYS_LEAF:
+ if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaf *)iter)->dflt &&
+ lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
+ /* create default leaf */
+ ret = lyd_create_term2(iter, ((struct lysc_node_leaf *)iter)->dflt, &node);
+ if (ret == LY_EINCOMPLETE) {
+ if (node_types) {
+ /* remember to resolve type */
+ LY_CHECK_RET(ly_set_add(node_types, node, 1, NULL));
+ }
+ } else if (ret) {
+ return ret;
+ }
+ node->flags = LYD_DEFAULT | (lysc_has_when(iter) ? LYD_WHEN_TRUE : 0);
+ lyd_insert_node(parent, first, node, 0);
+
+ if (lysc_has_when(iter) && node_when) {
+ /* remember to resolve when */
+ LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
+ }
+ if (ext_node) {
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_RET(lyd_validate_node_ext(node, ext_node));
+ }
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+ }
+ }
+ break;
+ case LYS_LEAFLIST:
+ if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaflist *)iter)->dflts &&
+ lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
+ /* create all default leaf-lists */
+ dflts = ((struct lysc_node_leaflist *)iter)->dflts;
+ LY_ARRAY_FOR(dflts, u) {
+ ret = lyd_create_term2(iter, dflts[u], &node);
+ if (ret == LY_EINCOMPLETE) {
+ if (node_types) {
+ /* remember to resolve type */
+ LY_CHECK_RET(ly_set_add(node_types, node, 1, NULL));
+ }
+ } else if (ret) {
+ return ret;
+ }
+ node->flags = LYD_DEFAULT | (lysc_has_when(iter) ? LYD_WHEN_TRUE : 0);
+ lyd_insert_node(parent, first, node, 0);
+
+ if (lysc_has_when(iter) && node_when) {
+ /* remember to resolve when */
+ LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
+ }
+ if (ext_node) {
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_RET(lyd_validate_node_ext(node, ext_node));
+ }
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+ }
+ }
+ }
+ break;
+ default:
+ /* without defaults */
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_implicit_tree(struct lyd_node *tree, uint32_t implicit_options, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *node;
+ struct ly_set node_when = {0};
+
+ LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
+ if (diff) {
+ *diff = NULL;
+ }
+
+ LYD_TREE_DFS_BEGIN(tree, node) {
+ /* skip added default nodes */
+ if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) &&
+ (node->schema->nodetype & LYD_NODE_INNER)) {
+ LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL,
+ NULL, implicit_options, diff), cleanup);
+ }
+
+ LYD_TREE_DFS_END(tree, node);
+ }
+
+ /* resolve when and remove any invalid defaults */
+ ret = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL, 0, diff);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ ly_set_erase(&node_when, NULL);
+ if (ret && diff) {
+ lyd_free_all(*diff);
+ *diff = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t implicit_options, struct lyd_node **diff)
+{
+ const struct lys_module *mod;
+ struct lyd_node *d = NULL;
+ uint32_t i = 0;
+ LY_ERR ret = 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);
+ if (diff) {
+ *diff = NULL;
+ }
+ if (!ctx) {
+ ctx = LYD_CTX(*tree);
+ }
+
+ /* add nodes for each module one-by-one */
+ while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ LY_CHECK_GOTO(ret = lyd_new_implicit_module(tree, mod, implicit_options, diff ? &d : NULL), cleanup);
+ if (d) {
+ /* merge into one diff */
+ lyd_insert_sibling(*diff, d, diff);
+
+ d = NULL;
+ }
+ }
+
+cleanup:
+ if (ret && diff) {
+ lyd_free_all(*diff);
+ *diff = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module, uint32_t implicit_options,
+ struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *root, *d = NULL;
+ struct ly_set node_when = {0};
+
+ 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);
+ if (diff) {
+ *diff = NULL;
+ }
+
+ /* add all top-level defaults for this module */
+ LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, NULL, NULL, implicit_options, diff),
+ cleanup);
+
+ /* resolve when and remove any invalid defaults */
+ LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL,
+ 0, diff), cleanup);
+
+ /* process nested nodes */
+ LY_LIST_FOR(*tree, root) {
+ /* skip added default nodes */
+ if ((root->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) {
+ LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
+
+ if (d) {
+ /* merge into one diff */
+ lyd_insert_sibling(*diff, d, diff);
+
+ d = NULL;
+ }
+ }
+ }
+
+cleanup:
+ ly_set_erase(&node_when, NULL);
+ if (ret && diff) {
+ lyd_free_all(*diff);
+ *diff = NULL;
+ }
+ return ret;
+}
diff --git a/src/tree_edit.h b/src/tree_edit.h
new file mode 100644
index 0000000..951d95d
--- /dev/null
+++ b/src/tree_edit.h
@@ -0,0 +1,306 @@
+/**
+ * @file tree_edit.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang generic macros and functions to modify YANG schema or data trees. Intended for internal use and libyang
+ * plugins.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_EDIT_H_
+#define LY_TREE_EDIT_H_
+
+#include <stdlib.h>
+
+#ifndef LOGMEM
+#define LOGMEM(CTX)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Wrapper for realloc() call. The only difference is that if it fails to
+ * allocate the requested memory, the original memory is freed as well.
+ *
+ * @param[in] ptr Memory to reallocate.
+ * @param[in] size New size of the memory block.
+ *
+ * @return Pointer to the new memory, NULL on error.
+ */
+void *ly_realloc(void *ptr, size_t size);
+
+/**
+ * @defgroup trees_edit Trees - modification
+ * @ingroup trees
+ *
+ * Generic macros, functions, etc. to modify [schema](@ref schematree) and [data](@ref datatree) trees.
+ * @{
+ */
+
+/**
+ * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
+ *
+ * Increases the size information.
+ *
+ * This is a generic macro for ::LY_ARRAY_NEW_RET and ::LY_ARRAY_NEW_GOTO.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
+ * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[in] EACTION Action to perform in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_NEW(CTX, ARRAY, EACTION) \
+ { \
+ char *p__; \
+ if (ARRAY) { \
+ ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
+ p__ = (char *)realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
+ sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
+ if (!p__) { \
+ --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
+ LOGMEM(CTX); \
+ EACTION; \
+ } \
+ } else { \
+ p__ = (char *)malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
+ if (!p__) { \
+ LOGMEM(CTX); \
+ EACTION; \
+ } \
+ *((LY_ARRAY_COUNT_TYPE*)(p__)) = 1; \
+ } \
+ p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
+ memcpy(&(ARRAY), &p__, sizeof p__); \
+ }
+
+/**
+ * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
+ *
+ * Increases the size information.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
+ * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
+ * @param[in] RETVAL Return value for the case of error (memory allocation failure).
+ */
+#define LY_ARRAY_NEW_RET(CTX, ARRAY, NEW_ITEM, RETVAL) \
+ LY_ARRAY_NEW(CTX, ARRAY, return RETVAL); \
+ (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
+ memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
+
+/**
+ * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
+ *
+ * Increases the size information.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
+ * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
+ * @param[out] RET Variable to store error code.
+ * @param[in] GOTO Label to go in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
+ LY_ARRAY_NEW(CTX, ARRAY, RET = LY_EMEM; goto GOTO); \
+ (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
+ memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
+
+/**
+ * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
+ *
+ * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
+ * when the items are filled.
+ *
+ * This is a generic macro for ::LY_ARRAY_CREATE_RET and ::LY_ARRAY_CREATE_GOTO.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to create.
+ * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
+ * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[in] EACTION Action to perform in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_CREATE(CTX, ARRAY, SIZE, EACTION) \
+ { \
+ char *p__; \
+ if (ARRAY) { \
+ p__ = (char *)realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
+ sizeof(LY_ARRAY_COUNT_TYPE) + ((*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) + (SIZE)) * sizeof *(ARRAY))); \
+ if (!p__) { \
+ LOGMEM(CTX); \
+ EACTION; \
+ } \
+ } else { \
+ p__ = (char *)calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
+ if (!p__) { \
+ LOGMEM(CTX); \
+ EACTION; \
+ } \
+ } \
+ p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
+ memcpy(&(ARRAY), &p__, sizeof p__); \
+ if (ARRAY) { \
+ memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
+ } \
+ }
+
+/**
+ * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
+ *
+ * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
+ * when the items are filled.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to create.
+ * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
+ * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[in] RETVAL Return value for the case of error (memory allocation failure).
+ */
+#define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
+ LY_ARRAY_CREATE(CTX, ARRAY, SIZE, return RETVAL)
+
+/**
+ * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added).
+ *
+ * Does not set the count information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
+ * when the items are filled.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to create.
+ * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
+ * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[out] RET Variable to store error code.
+ * @param[in] GOTO Label to go in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
+ LY_ARRAY_CREATE(CTX, ARRAY, SIZE, RET = LY_EMEM; goto GOTO)
+
+/**
+ * @brief Increment the items counter in a ([sized array](@ref sizedarrays)).
+ *
+ * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
+ * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
+ *
+ * @param[in] ARRAY Pointer to the array to affect.
+ */
+#define LY_ARRAY_INCREMENT(ARRAY) \
+ ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
+
+/**
+ * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)).
+ *
+ * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
+ * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
+ *
+ * @param[in] ARRAY Pointer to the array to affect.
+ */
+#define LY_ARRAY_DECREMENT(ARRAY) \
+ --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
+
+/**
+ * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
+ * in case it was decremented to 0.
+ *
+ * @param[in] ARRAY Pointer to the array to affect.
+ */
+#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
+ --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
+ if (!LY_ARRAY_COUNT(ARRAY)) { \
+ LY_ARRAY_FREE(ARRAY); \
+ (ARRAY) = NULL; \
+ }
+
+/**
+ * @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
+ *
+ * The items inside the array are not freed.
+ *
+ * @param[in] ARRAY A ([sized array](@ref sizedarrays)) to be freed.
+ */
+#define LY_ARRAY_FREE(ARRAY) \
+ if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);}
+
+/**
+ * @brief Insert item into linked list.
+ *
+ * @param[in,out] LIST Linked list to add to.
+ * @param[in] NEW_ITEM New item, that will be appended to the list, must be already allocated.
+ * @param[in] LINKER name of structuring member that is used to connect items together.
+ */
+#define LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)\
+ if (!(*LIST)) { \
+ memcpy(LIST, &(NEW_ITEM), sizeof NEW_ITEM); \
+ } else { \
+ size_t offset__ = (char *)&(*LIST)->LINKER - (char *)(*LIST); \
+ char **iter__ = (char **)((size_t)(*LIST) + offset__); \
+ while (*iter__) { \
+ iter__ = (char **)((size_t)(*iter__) + offset__); \
+ } \
+ memcpy(iter__, &(NEW_ITEM), sizeof NEW_ITEM); \
+ }
+
+/**
+ * @brief Allocate and insert new item into linked list, return in case of error.
+ *
+ * This is a generic macro for ::LY_LIST_NEW_RET and ::LY_LIST_NEW_GOTO.
+ *
+ * @param[in] CTX used for logging.
+ * @param[in,out] LIST Linked list to add to.
+ * @param[out] NEW_ITEM New item that is appended to the list.
+ * @param[in] LINKER name of structure member that is used to connect items together.
+ * @param[in] EACTION Action to perform in case of error (memory allocation failure).
+ */
+#define LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, EACTION) \
+ { \
+ char *p__ = (char *)calloc(1, sizeof *NEW_ITEM); \
+ if (!p__) { \
+ LOGMEM(CTX); \
+ EACTION; \
+ } \
+ memcpy(&(NEW_ITEM), &p__, sizeof p__); \
+ LY_LIST_INSERT(LIST, NEW_ITEM, LINKER); \
+ }
+
+/**
+ * @brief Allocate and insert new item into linked list, return in case of error.
+ *
+ * @param[in] CTX used for logging.
+ * @param[in,out] LIST Linked list to add to.
+ * @param[out] NEW_ITEM New item that is appended to the list.
+ * @param[in] LINKER name of structure member that is used to connect items together.
+ * @param[in] RETVAL Return value for the case of error (memory allocation failure).
+ */
+#define LY_LIST_NEW_RET(CTX, LIST, NEW_ITEM, LINKER, RETVAL) \
+ LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, return RETVAL)
+
+/**
+ * @brief Allocate and insert new item into linked list, goto specified label in case of error.
+ *
+ * @param[in] CTX used for logging.
+ * @param[in,out] LIST Linked list to add to.
+ * @param[out] NEW_ITEM New item that is appended to the list.
+ * @param[in] LINKER name of structure member that is used to connect items together.
+ * @param[in] RET variable to store returned error type.
+ * @param[in] LABEL label to goto in case of error.
+ */
+#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, RET, LABEL) \
+ LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, RET = LY_EMEM; goto LABEL)
+
+/** @} trees_edit */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_EDIT_H_ */
diff --git a/src/tree_schema.c b/src/tree_schema.c
new file mode 100644
index 0000000..5c897bf
--- /dev/null
+++ b/src/tree_schema.c
@@ -0,0 +1,2178 @@
+/**
+ * @file tree_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema tree implementation
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "tree_schema.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "in.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_internal.h"
+#include "parser_schema.h"
+#include "path.h"
+#include "plugins_internal.h"
+#include "schema_compile.h"
+#include "schema_compile_amend.h"
+#include "schema_features.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_edit.h"
+#include "tree_schema_free.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+const char * const ly_devmod_list[] = {
+ [LYS_DEV_NOT_SUPPORTED] = "not-supported",
+ [LYS_DEV_ADD] = "add",
+ [LYS_DEV_DELETE] = "delete",
+ [LYS_DEV_REPLACE] = "replace",
+};
+
+LIBYANG_API_DEF LY_ERR
+lysc_tree_dfs_full(const struct lysc_node *root, lysc_dfs_clb dfs_clb, void *data)
+{
+ struct lysc_node *elem, *elem2;
+ const struct lysc_node_action *action;
+ const struct lysc_node_notif *notif;
+
+ LY_CHECK_ARG_RET(NULL, root, dfs_clb, LY_EINVAL);
+
+ LYSC_TREE_DFS_BEGIN(root, elem) {
+ /* schema node */
+ LY_CHECK_RET(dfs_clb(elem, data, &LYSC_TREE_DFS_continue));
+
+ LY_LIST_FOR(lysc_node_actions(elem), action) {
+ LYSC_TREE_DFS_BEGIN(action, elem2) {
+ /* action subtree */
+ LY_CHECK_RET(dfs_clb(elem2, data, &LYSC_TREE_DFS_continue));
+
+ LYSC_TREE_DFS_END(action, elem2);
+ }
+ }
+
+ LY_LIST_FOR(lysc_node_notifs(elem), notif) {
+ LYSC_TREE_DFS_BEGIN(notif, elem2) {
+ /* notification subtree */
+ LY_CHECK_RET(dfs_clb(elem2, data, &LYSC_TREE_DFS_continue));
+
+ LYSC_TREE_DFS_END(notif, elem2);
+ }
+ }
+
+ LYSC_TREE_DFS_END(root, elem);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lysc_module_dfs_full(const struct lys_module *mod, lysc_dfs_clb dfs_clb, void *data)
+{
+ const struct lysc_node *root;
+
+ LY_CHECK_ARG_RET(NULL, mod, mod->compiled, dfs_clb, LY_EINVAL);
+
+ /* schema nodes */
+ LY_LIST_FOR(mod->compiled->data, root) {
+ LY_CHECK_RET(lysc_tree_dfs_full(root, dfs_clb, data));
+ }
+
+ /* RPCs */
+ LY_LIST_FOR((const struct lysc_node *)mod->compiled->rpcs, root) {
+ LY_CHECK_RET(lysc_tree_dfs_full(root, dfs_clb, data));
+ }
+
+ /* notifications */
+ LY_LIST_FOR((const struct lysc_node *)mod->compiled->notifs, root) {
+ LY_CHECK_RET(lysc_tree_dfs_full(root, dfs_clb, data));
+ }
+
+ return LY_SUCCESS;
+}
+
+static void
+lys_getnext_into_case(const struct lysc_node_case *first_case, const struct lysc_node **last, const struct lysc_node **next)
+{
+ for ( ; first_case; first_case = (const struct lysc_node_case *)first_case->next) {
+ if (first_case->child) {
+ /* there is something to return */
+ (*next) = first_case->child;
+ return;
+ }
+ }
+
+ /* no children in choice's cases, so go to the choice's sibling instead of into it */
+ (*last) = (*next);
+ (*next) = (*next)->next;
+}
+
+/**
+ * @brief Generic getnext function for ::lys_getnext() and ::lys_getnext_ext().
+ *
+ * Gets next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
+ * be from an augment. If the @p ext is provided, the function is locked inside the schema tree defined in the
+ * extension instance.
+ *
+ * ::lys_getnext_() is supposed to be called sequentially. In the first call, the @p last parameter is usually NULL
+ * and function starts returning i) the first @p parent's child or ii) the first top level element specified in the
+ * given extension (if provided) or iii) the first top level element of the @p module.
+ * Consequent calls suppose to provide the previously returned node as the @p last parameter and still the same
+ * @p parent and @p module parameters.
+ *
+ * Without options, the function is used to traverse only the schema nodes that can be paired with corresponding
+ * data nodes in a data tree. By setting some @p options the behavior can be modified to the extent that
+ * all the schema nodes are iteratively returned.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree where the function starts processing.
+ * @param[in] module In case of iterating on top level elements, the @p parent is NULL and
+ * module must be specified.
+ * @param[in] ext The extension instance to provide a separate schema tree. To consider the top level elements in the tree,
+ * the @p parent must be NULL. Anyway, at least one of @p parent, @p module and @p ext parameters must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node that can be instantiated in a data tree, NULL in case there is no such element.
+ */
+static const struct lysc_node *
+lys_getnext_(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module,
+ const struct lysc_ext_instance *ext, uint32_t options)
+{
+ const struct lysc_node *next = NULL;
+ ly_bool action_flag = 0, notif_flag = 0;
+
+ LY_CHECK_ARG_RET(NULL, parent || module || ext, NULL);
+
+next:
+ if (!last) {
+ /* first call */
+
+ /* get know where to start */
+ if (parent) {
+ /* schema subtree */
+ next = last = lysc_node_child(parent);
+ } else {
+ /* top level data */
+ if (ext) {
+ lyplg_ext_get_storage(ext, LY_STMT_DATA_NODE_MASK, sizeof last, (const void **)&last);
+ next = last;
+ } else {
+ next = last = module->data;
+ }
+ }
+ if (!next) {
+ /* try to get action or notification */
+ goto repeat;
+ }
+ /* test if the next can be returned */
+ goto check;
+
+ } else if (last->nodetype & (LYS_RPC | LYS_ACTION)) {
+ action_flag = 1;
+ next = last->next;
+ } else if (last->nodetype == LYS_NOTIF) {
+ action_flag = notif_flag = 1;
+ next = last->next;
+ } else {
+ next = last->next;
+ }
+
+repeat:
+ if (!next) {
+ /* possibly go back to parent */
+ if (last && (last->parent != parent)) {
+ last = last->parent;
+ goto next;
+ } else if (!action_flag) {
+ action_flag = 1;
+ if (ext) {
+ lyplg_ext_get_storage(ext, LY_STMT_OP_MASK, sizeof next, (const void **)&next);
+ } else if (parent) {
+ next = (struct lysc_node *)lysc_node_actions(parent);
+ } else {
+ next = (struct lysc_node *)module->rpcs;
+ }
+ } else if (!notif_flag) {
+ notif_flag = 1;
+ if (ext) {
+ lyplg_ext_get_storage(ext, LY_STMT_NOTIFICATION, sizeof next, (const void **)&next);
+ } else if (parent) {
+ next = (struct lysc_node *)lysc_node_notifs(parent);
+ } else {
+ next = (struct lysc_node *)module->notifs;
+ }
+ } else {
+ return NULL;
+ }
+ goto repeat;
+ }
+check:
+ switch (next->nodetype) {
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ case LYS_LEAF:
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ case LYS_LIST:
+ case LYS_LEAFLIST:
+ break;
+ case LYS_CASE:
+ if (options & LYS_GETNEXT_WITHCASE) {
+ break;
+ } else {
+ /* go into */
+ lys_getnext_into_case((const struct lysc_node_case *)next, &last, &next);
+ }
+ goto repeat;
+ case LYS_CONTAINER:
+ if (!(next->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
+ if (lysc_node_child(next)) {
+ /* go into */
+ next = lysc_node_child(next);
+ } else {
+ last = next;
+ next = next->next;
+ }
+ goto repeat;
+ }
+ break;
+ case LYS_CHOICE:
+ if (options & LYS_GETNEXT_WITHCHOICE) {
+ break;
+ } else if ((options & LYS_GETNEXT_NOCHOICE) || !lysc_node_child(next)) {
+ next = next->next;
+ } else {
+ if (options & LYS_GETNEXT_WITHCASE) {
+ next = lysc_node_child(next);
+ } else {
+ /* go into */
+ lys_getnext_into_case(((struct lysc_node_choice *)next)->cases, &last, &next);
+ }
+ }
+ goto repeat;
+ case LYS_INPUT:
+ if (options & LYS_GETNEXT_OUTPUT) {
+ /* skip */
+ next = next->next;
+ } else {
+ /* go into */
+ next = lysc_node_child(next);
+ }
+ goto repeat;
+ case LYS_OUTPUT:
+ if (!(options & LYS_GETNEXT_OUTPUT)) {
+ /* skip */
+ next = next->next;
+ } else {
+ /* go into */
+ next = lysc_node_child(next);
+ }
+ goto repeat;
+ default:
+ /* we should not be here */
+ LOGINT(module ? module->mod->ctx : parent ? parent->module->ctx : ext->module->ctx);
+ return NULL;
+ }
+
+ return next;
+}
+
+LIBYANG_API_DEF const struct lysc_node *
+lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, uint32_t options)
+{
+ return lys_getnext_(last, parent, module, NULL, options);
+}
+
+LIBYANG_API_DEF const struct lysc_node *
+lys_getnext_ext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_ext_instance *ext, uint32_t options)
+{
+ return lys_getnext_(last, parent, NULL, ext, options);
+}
+
+const struct lysc_node *
+lysc_ext_find_node(const struct lysc_ext_instance *ext, const struct lys_module *module, const char *name, size_t name_len,
+ uint16_t nodetype, uint32_t options)
+{
+ const struct lysc_node *node = NULL;
+
+ LY_CHECK_ARG_RET(NULL, ext, name, NULL);
+ if (!nodetype) {
+ nodetype = LYS_NODETYPE_MASK;
+ }
+
+ if (module && (module != ext->module)) {
+ return NULL;
+ }
+
+ while ((node = lys_getnext_ext(node, NULL, ext, options))) {
+ if (!(node->nodetype & nodetype)) {
+ continue;
+ }
+
+ if (name_len) {
+ if (!ly_strncmp(node->name, name, name_len)) {
+ return node;
+ }
+ } else {
+ if (!strcmp(node->name, name)) {
+ return node;
+ }
+ }
+ }
+ return NULL;
+}
+
+LIBYANG_API_DEF const struct lysc_node *
+lys_find_child(const struct lysc_node *parent, const struct lys_module *module, const char *name, size_t name_len,
+ uint16_t nodetype, uint32_t options)
+{
+ 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);
+ if (!nodetype) {
+ nodetype = LYS_NODETYPE_MASK;
+ }
+
+ while ((node = lys_getnext(node, parent, module->compiled, options))) {
+ if (!(node->nodetype & nodetype)) {
+ continue;
+ }
+ if (node->module != module) {
+ continue;
+ }
+
+ if (name_len) {
+ if (!ly_strncmp(node->name, name, name_len)) {
+ return node;
+ }
+ } else {
+ if (!strcmp(node->name, name)) {
+ return node;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *xpath, uint32_t options,
+ struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_set xp_set;
+ struct lyxp_expr *exp = NULL;
+ 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);
+ if (!(options & LYXP_SCNODE_ALL)) {
+ options |= LYXP_SCNODE;
+ }
+ if (!ctx) {
+ ctx = ctx_node->module->ctx;
+ }
+
+ memset(&xp_set, 0, sizeof xp_set);
+
+ /* compile expression */
+ ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* atomize expression */
+ ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* transform into ly_set */
+ (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
+ LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ (*set)->size = xp_set.used;
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.scnodes[i].type == LYXP_NODE_ELEM) {
+ ret = ly_set_add(*set, xp_set.val.scnodes[i].scnode, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free(ctx, exp);
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_find_expr_atoms(const struct lysc_node *ctx_node, const struct lys_module *cur_mod, const struct lyxp_expr *expr,
+ const struct lysc_prefix *prefixes, uint32_t options, struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_set xp_set = {0};
+ 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);
+ if (!(options & LYXP_SCNODE_ALL)) {
+ options = LYXP_SCNODE;
+ }
+
+ /* atomize expression */
+ ret = lyxp_atomize(cur_mod->ctx, expr, cur_mod, LY_VALUE_SCHEMA_RESOLVED, (void *)prefixes, ctx_node, ctx_node,
+ &xp_set, options);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* transform into ly_set */
+ (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
+ LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(cur_mod->ctx); ret = LY_EMEM, cleanup);
+ (*set)->size = xp_set.used;
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if ((xp_set.val.scnodes[i].type == LYXP_NODE_ELEM) && (xp_set.val.scnodes[i].in_ctx >= LYXP_SET_SCNODE_ATOM_NODE)) {
+ assert((xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_NODE) ||
+ (xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL) ||
+ (xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX));
+ ret = ly_set_add(*set, xp_set.val.scnodes[i].scnode, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ if (ret) {
+ ly_set_free(*set, NULL);
+ *set = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *xpath, uint32_t options,
+ struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_set xp_set = {0};
+ struct lyxp_expr *exp = NULL;
+ uint32_t i;
+
+ LY_CHECK_ARG_RET(NULL, ctx || ctx_node, xpath, set, LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL);
+ if (!(options & LYXP_SCNODE_ALL)) {
+ options = LYXP_SCNODE;
+ }
+ if (!ctx) {
+ ctx = ctx_node->module->ctx;
+ }
+
+ /* compile expression */
+ ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* atomize expression */
+ ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* transform into ly_set */
+ (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
+ LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ (*set)->size = xp_set.used;
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if ((xp_set.val.scnodes[i].type == LYXP_NODE_ELEM) && (xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX)) {
+ ret = ly_set_add(*set, xp_set.val.scnodes[i].scnode, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free(ctx, exp);
+ if (ret) {
+ ly_set_free(*set, NULL);
+ *set = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_find_lypath_atoms(const struct ly_path *path, struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ LY_CHECK_ARG_RET(NULL, path, set, LY_EINVAL);
+
+ /* allocate return set */
+ LY_CHECK_RET(ly_set_new(set));
+
+ LY_ARRAY_FOR(path, u) {
+ /* add nodes from the path */
+ LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].node, 0, NULL), cleanup);
+ if (path[u].pred_type == LY_PATH_PREDTYPE_LIST) {
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ /* add all the keys in a predicate */
+ LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].predicates[v].key, 0, NULL), cleanup);
+ }
+ }
+ }
+
+cleanup:
+ if (ret) {
+ ly_set_free(*set, NULL);
+ *set = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_find_path_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path, ly_bool output,
+ struct ly_set **set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint8_t oper;
+ struct lyxp_expr *expr = NULL;
+ 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);
+
+ if (!ctx) {
+ ctx = ctx_node->module->ctx;
+ }
+
+ /* parse */
+ ret = lyxp_expr_parse(ctx, path, strlen(path), 0, &expr);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compile */
+ oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, expr, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* resolve */
+ ret = lys_find_lypath_atoms(p, set);
+
+cleanup:
+ ly_path_free(ctx, p);
+ lyxp_expr_free(ctx, expr);
+ return ret;
+}
+
+LIBYANG_API_DEF const struct lysc_node *
+lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path, ly_bool output)
+{
+ const struct lysc_node *snode = NULL;
+ struct lyxp_expr *exp = NULL;
+ struct ly_path *p = NULL;
+ 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);
+
+ if (!ctx) {
+ ctx = ctx_node->module->ctx;
+ }
+
+ /* parse */
+ ret = lyxp_expr_parse(ctx, path, strlen(path), 0, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compile */
+ oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get last node */
+ snode = p[LY_ARRAY_COUNT(p) - 1].node;
+
+cleanup:
+ ly_path_free(ctx, p);
+ lyxp_expr_free(ctx, exp);
+ return snode;
+}
+
+char *
+lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LYSC_PATH_TYPE pathtype, char *buffer,
+ size_t buflen)
+{
+ const struct lysc_node *iter, *par, *key;
+ char *path = NULL;
+ int len = 0;
+ ly_bool skip_schema;
+
+ if (buffer) {
+ LY_CHECK_ARG_RET(node->module->ctx, buflen > 1, NULL);
+ buffer[0] = '\0';
+ }
+
+ if ((pathtype == LYSC_PATH_DATA) || (pathtype == LYSC_PATH_DATA_PATTERN)) {
+ /* skip schema-only nodes */
+ skip_schema = 1;
+ } else {
+ skip_schema = 0;
+ }
+
+ for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
+ char *s;
+ const char *slash;
+
+ if (skip_schema && (iter->nodetype & (LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT))) {
+ /* schema-only node */
+ continue;
+ }
+
+ if ((pathtype == LYSC_PATH_DATA_PATTERN) && (iter->nodetype == LYS_LIST)) {
+ key = NULL;
+ while ((key = lys_getnext(key, iter, NULL, 0)) && lysc_is_key(key)) {
+ s = buffer ? strdup(buffer) : path;
+
+ /* print key predicate */
+ if (buffer) {
+ len = snprintf(buffer, buflen, "[%s='%%s']%s", key->name, s ? s : "");
+ } else {
+ len = asprintf(&path, "[%s='%%s']%s", key->name, s ? s : "");
+ }
+ free(s);
+
+ if (buffer && (buflen <= (size_t)len)) {
+ /* not enough space in buffer */
+ break;
+ }
+ }
+ }
+
+ s = buffer ? strdup(buffer) : path;
+ if (parent && (iter->parent == parent)) {
+ slash = "";
+ } else {
+ slash = "/";
+ }
+
+ if (skip_schema) {
+ par = lysc_data_parent(iter);
+ } else {
+ par = iter->parent;
+ }
+
+ if (!par || (par->module != iter->module)) {
+ /* print prefix */
+ if (buffer) {
+ len = snprintf(buffer, buflen, "%s%s:%s%s", slash, iter->module->name, iter->name, s ? s : "");
+ } else {
+ len = asprintf(&path, "%s%s:%s%s", slash, iter->module->name, iter->name, s ? s : "");
+ }
+ } else {
+ /* prefix is the same as in parent */
+ if (buffer) {
+ len = snprintf(buffer, buflen, "%s%s%s", slash, iter->name, s ? s : "");
+ } else {
+ len = asprintf(&path, "%s%s%s", slash, iter->name, s ? s : "");
+ }
+ }
+ free(s);
+
+ if (buffer && (buflen <= (size_t)len)) {
+ /* not enough space in buffer */
+ break;
+ }
+ }
+
+ if (len < 0) {
+ free(path);
+ path = NULL;
+ } else if (len == 0) {
+ if (buffer) {
+ strcpy(buffer, "/");
+ } else {
+ path = strdup("/");
+ }
+ }
+
+ if (buffer) {
+ return buffer;
+ } else {
+ return path;
+ }
+}
+
+LIBYANG_API_DEF char *
+lysc_path(const struct lysc_node *node, LYSC_PATH_TYPE pathtype, char *buffer, size_t buflen)
+{
+ return lysc_path_until(node, NULL, pathtype, buffer, buflen);
+}
+
+LY_ERR
+_lys_set_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lys_module *mod_iter;
+ const char **imp_f, *all_f[] = {"*", NULL};
+ uint32_t i;
+
+ if (mod->implemented) {
+ /* mod is already implemented, set the features */
+ r = lys_set_features(mod->parsed, features);
+ if (r == LY_EEXIST) {
+ /* no changes */
+ return LY_SUCCESS;
+ } else if (!r) {
+ /* mark the module as changed */
+ mod->to_compile = 1;
+ }
+
+ return r;
+ }
+
+ /* implement, ignore recompilation because it must always take place later */
+ r = lys_implement(mod, features, unres);
+ LY_CHECK_ERR_GOTO(r && (r != LY_ERECOMPILE), ret = r, cleanup);
+
+ if (mod->ctx->flags & LY_CTX_ALL_IMPLEMENTED) {
+ /* implement all the imports as well */
+ for (i = 0; i < unres->creating.count; ++i) {
+ mod = unres->creating.objs[i];
+ if (mod->implemented) {
+ continue;
+ }
+
+ imp_f = (mod->ctx->flags & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL;
+ r = lys_implement(mod, imp_f, unres);
+ LY_CHECK_ERR_GOTO(r && (r != LY_ERECOMPILE), ret = r, cleanup);
+ }
+ }
+
+ /* Try to find module with LYS_MOD_IMPORTED_REV flag. */
+ i = 0;
+ while ((mod_iter = ly_ctx_get_module_iter(mod->ctx, &i))) {
+ if (!strcmp(mod_iter->name, mod->name) && (mod_iter != mod) && (mod_iter->latest_revision & LYS_MOD_IMPORTED_REV)) {
+ LOGVRB("Implemented module \"%s@%s\" was not and will not be imported if the revision-date is missing"
+ " in the import statement. Instead, the revision \"%s\" is imported.", mod->name, mod->revision,
+ mod_iter->revision);
+ break;
+ }
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Check whether it may be needed to (re)compile a module from a particular dependency set
+ * and if so, add it into its dep set.
+ *
+ * Dependency set includes all modules that need to be (re)compiled in case any of the module(s)
+ * in the dep set are (re)compiled.
+ *
+ * The reason for recompilation is possible disabled nodes and updating
+ * leafref targets to point to the newly compiled modules. Using the import relation, the
+ * dependency is reflexive because of possible foreign augments and deviations, which are compiled
+ * during the target module compilation.
+ *
+ * - every module must belong to exactly one dep set
+ * - implement flag must be ignored because it can be changed during dep set compilation
+ *
+ * @param[in] mod Module to process.
+ * @param[in,out] ctx_set Set with all not-yet-processed modules.
+ * @param[in,out] dep_set Current dependency set to update.
+ * @param[in] aux_set Set of traversed non-compiled modules, should be empty on first call.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_unres_dep_sets_create_mod_r(struct lys_module *mod, struct ly_set *ctx_set, struct ly_set *dep_set,
+ struct ly_set *aux_set)
+{
+ struct lys_module *mod2;
+ struct lysp_import *imports;
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u, v;
+ ly_bool found;
+
+ if (LYS_IS_SINGLE_DEP_SET(mod)) {
+ /* is already in a separate dep set */
+ if (!lys_has_dep_mods(mod)) {
+ /* break the dep set here, no modules depend on this one */
+ return LY_SUCCESS;
+ }
+
+ if (ly_set_contains(aux_set, mod, NULL)) {
+ /* it was traversed */
+ return LY_SUCCESS;
+ }
+
+ /* add a new auxiliary module */
+ LY_CHECK_RET(ly_set_add(aux_set, mod, 1, NULL));
+ } else {
+ if (!ly_set_contains(ctx_set, mod, &i)) {
+ /* it was already processed */
+ return LY_SUCCESS;
+ }
+
+ /* remove it from the set, we are processing it now */
+ ly_set_rm_index(ctx_set, i, NULL);
+
+ /* add a new dependent module into the dep set */
+ LY_CHECK_RET(ly_set_add(dep_set, mod, 1, NULL));
+ }
+
+ /* process imports of the module and submodules */
+ imports = mod->parsed->imports;
+ LY_ARRAY_FOR(imports, u) {
+ mod2 = imports[u].module;
+ LY_CHECK_RET(lys_unres_dep_sets_create_mod_r(mod2, ctx_set, dep_set, aux_set));
+ }
+ LY_ARRAY_FOR(mod->parsed->includes, v) {
+ imports = mod->parsed->includes[v].submodule->imports;
+ LY_ARRAY_FOR(imports, u) {
+ mod2 = imports[u].module;
+ if (LYS_IS_SINGLE_DEP_SET(mod2) && !lys_has_dep_mods(mod2)) {
+ /* break the dep set here, no modules depend on this one */
+ continue;
+ }
+
+ LY_CHECK_RET(lys_unres_dep_sets_create_mod_r(imports[u].module, ctx_set, dep_set, aux_set));
+ }
+ }
+
+ /* process modules and submodules importing this module */
+ for (i = 0; i < mod->ctx->list.count; ++i) {
+ mod2 = mod->ctx->list.objs[i];
+ found = 0;
+
+ imports = mod2->parsed->imports;
+ LY_ARRAY_FOR(imports, u) {
+ if (imports[u].module == mod) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ LY_ARRAY_FOR(mod2->parsed->includes, v) {
+ imports = mod2->parsed->includes[v].submodule->imports;
+ LY_ARRAY_FOR(imports, u) {
+ if (imports[u].module == mod) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ if (found) {
+ LY_CHECK_RET(lys_unres_dep_sets_create_mod_r(mod2, ctx_set, dep_set, aux_set));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add all simple modules (that have nothing to (re)compile) into separate dep sets.
+ *
+ * @param[in,out] ctx_set Set with all not-yet-processed modules.
+ * @param[in,out] main_set Set of dependency module sets.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_unres_dep_sets_create_single(struct ly_set *ctx_set, struct ly_set *main_set)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *m;
+ uint32_t i = 0;
+ struct ly_set *dep_set = NULL;
+
+ while (i < ctx_set->count) {
+ m = ctx_set->objs[i];
+ if (LYS_IS_SINGLE_DEP_SET(m)) {
+ /* remove it from the set, we are processing it now */
+ ly_set_rm_index(ctx_set, i, NULL);
+
+ /* this module can be in a separate dep set (but there still may be modules importing this one
+ * that depend on imports of this one in case it defines groupings) */
+ LY_CHECK_GOTO(ret = ly_set_new(&dep_set), cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(dep_set, m, 1, NULL), cleanup);
+ LY_CHECK_GOTO(ret = ly_set_add(main_set, dep_set, 1, NULL), cleanup);
+ dep_set = NULL;
+ } else {
+ ++i;
+ }
+ }
+
+cleanup:
+ ly_set_free(dep_set, NULL);
+ return ret;
+}
+
+LY_ERR
+lys_unres_dep_sets_create(struct ly_ctx *ctx, struct ly_set *main_set, struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *m;
+ struct ly_set *dep_set = NULL, *ctx_set = NULL, aux_set = {0};
+ uint32_t i;
+ ly_bool found;
+
+ assert(!main_set->count);
+
+ /* start with a duplicate set of modules that we will remove from */
+ LY_CHECK_GOTO(ret = ly_set_dup(&ctx->list, NULL, &ctx_set), cleanup);
+
+ /* first create all dep sets with single modules */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create_single(ctx_set, main_set), cleanup);
+
+ if (mod && !ly_set_contains(ctx_set, mod, NULL)) {
+ /* dep set for this module has already been created, nothing else to do */
+ goto cleanup;
+ }
+
+ while (ctx_set->count) {
+ /* create new dep set */
+ LY_CHECK_GOTO(ret = ly_set_new(&dep_set), cleanup);
+
+ if (mod) {
+ /* use the module create a dep set with the rest of its dependent modules */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create_mod_r(mod, ctx_set, dep_set, &aux_set), cleanup);
+ } else {
+ /* use first ctx mod to create a dep set with the rest of its dependent modules */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create_mod_r(ctx_set->objs[0], ctx_set, dep_set, &aux_set), cleanup);
+ }
+ ly_set_erase(&aux_set, NULL);
+ assert(dep_set->count);
+
+ /* check whether there is any module that will be (re)compiled */
+ found = 0;
+ for (i = 0; i < dep_set->count; ++i) {
+ m = dep_set->objs[i];
+ if (m->to_compile) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ /* if there is, all the implemented modules need to be recompiled */
+ for (i = 0; i < dep_set->count; ++i) {
+ m = dep_set->objs[i];
+ if (m->implemented) {
+ m->to_compile = 1;
+ }
+ }
+ }
+
+ /* add the dep set into main set */
+ LY_CHECK_GOTO(ret = ly_set_add(main_set, dep_set, 1, NULL), cleanup);
+ dep_set = NULL;
+
+ if (mod) {
+ /* we need dep set only for this module */
+ break;
+ }
+ }
+
+#ifndef NDEBUG
+ LOGDBG(LY_LDGDEPSETS, "dep sets created (%" PRIu32 "):", main_set->count);
+ for (i = 0; i < main_set->count; ++i) {
+ struct ly_set *iter_set = main_set->objs[i];
+
+ LOGDBG(LY_LDGDEPSETS, "dep set #%" PRIu32 ":", i);
+ for (uint32_t j = 0; j < iter_set->count; ++j) {
+ m = iter_set->objs[j];
+ LOGDBG(LY_LDGDEPSETS, "\t%s", m->name);
+ }
+ }
+#endif
+
+cleanup:
+ assert(ret || main_set->objs);
+ ly_set_erase(&aux_set, NULL);
+ ly_set_free(dep_set, NULL);
+ ly_set_free(ctx_set, NULL);
+ return ret;
+}
+
+void
+lys_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ uint32_t i, j, idx, temp_lo = 0;
+ struct lysf_ctx fctx = {.ctx = ctx};
+ struct ly_set *dep_set;
+ LY_ERR ret;
+
+ for (i = 0; i < unres->implementing.count; ++i) {
+ fctx.mod = unres->implementing.objs[i];
+ assert(fctx.mod->implemented);
+
+ /* make the module correctly non-implemented again */
+ fctx.mod->implemented = 0;
+ lys_precompile_augments_deviations_revert(ctx, fctx.mod);
+ lysc_module_free(&fctx, fctx.mod->compiled);
+ fctx.mod->compiled = NULL;
+
+ /* should not be made implemented */
+ fctx.mod->to_compile = 0;
+ }
+
+ for (i = 0; i < unres->creating.count; ++i) {
+ fctx.mod = unres->creating.objs[i];
+
+ /* remove the module from the context */
+ ly_set_rm(&ctx->list, fctx.mod, NULL);
+
+ /* remove it also from dep sets */
+ for (j = 0; j < unres->dep_sets.count; ++j) {
+ dep_set = unres->dep_sets.objs[j];
+ if (ly_set_contains(dep_set, fctx.mod, &idx)) {
+ ly_set_rm_index(dep_set, idx, NULL);
+ break;
+ }
+ }
+
+ /* free the module */
+ lys_module_free(&fctx, fctx.mod, 1);
+ }
+
+ /* remove the extensions as well */
+ lysf_ctx_erase(&fctx);
+
+ if (unres->implementing.count) {
+ /* recompile previous context because some implemented modules are no longer implemented,
+ * we can reuse the current to_compile flags */
+ ly_temp_log_options(&temp_lo);
+ ret = lys_compile_depset_all(ctx, &ctx->unres);
+ ly_temp_log_options(NULL);
+ if (ret) {
+ LOGINT(ctx);
+ }
+ }
+}
+
+void
+lys_unres_glob_erase(struct lys_glob_unres *unres)
+{
+ uint32_t i;
+
+ for (i = 0; i < unres->dep_sets.count; ++i) {
+ ly_set_free(unres->dep_sets.objs[i], NULL);
+ }
+ ly_set_erase(&unres->dep_sets, NULL);
+ ly_set_erase(&unres->implementing, NULL);
+ ly_set_erase(&unres->creating, NULL);
+
+ assert(!unres->ds_unres.whens.count);
+ assert(!unres->ds_unres.musts.count);
+ assert(!unres->ds_unres.leafrefs.count);
+ assert(!unres->ds_unres.disabled_leafrefs.count);
+ assert(!unres->ds_unres.dflts.count);
+ assert(!unres->ds_unres.disabled.count);
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_set_implemented(struct lys_module *mod, const char **features)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_glob_unres *unres = &mod->ctx->unres;
+
+ LY_CHECK_ARG_RET(NULL, mod, LY_EINVAL);
+
+ /* implement */
+ ret = _lys_set_implemented(mod, features, unres);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!(mod->ctx->flags & LY_CTX_EXPLICIT_COMPILE)) {
+ /* create dep set for the module and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(mod->ctx, &unres->dep_sets, mod), cleanup);
+
+ /* (re)compile the whole dep set (other dep sets will have no modules marked for compilation) */
+ LY_CHECK_GOTO(ret = lys_compile_depset_all(mod->ctx, unres), cleanup);
+
+ /* unres resolved */
+ lys_unres_glob_erase(unres);
+ }
+
+cleanup:
+ if (ret) {
+ lys_unres_glob_revert(mod->ctx, unres);
+ lys_unres_glob_erase(unres);
+ }
+ return ret;
+}
+
+/**
+ * @brief Resolve (find) all imported and included modules.
+ *
+ * @param[in] pctx Parser context.
+ * @param[in] pmod Parsed module to resolve.
+ * @param[out] new_mods Set with all the newly loaded modules.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_resolve_import_include(struct lysp_ctx *pctx, struct lysp_module *pmod, struct ly_set *new_mods)
+{
+ struct lysp_import *imp;
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ pmod->parsing = 1;
+ LY_ARRAY_FOR(pmod->imports, u) {
+ imp = &pmod->imports[u];
+ if (!imp->module) {
+ LY_CHECK_RET(lys_parse_load(PARSER_CTX(pctx), imp->name, imp->rev[0] ? imp->rev : NULL, new_mods, &imp->module));
+
+ if (!imp->rev[0]) {
+ /* This module must be selected for the next similar
+ * import without revision-date to avoid incorrect
+ * derived identities in the ::lys_module.identities.
+ */
+ imp->module->latest_revision |= LYS_MOD_IMPORTED_REV;
+ }
+ }
+ /* check for importing the same module twice */
+ for (v = 0; v < u; ++v) {
+ if (imp->module == pmod->imports[v].module) {
+ LOGWRN(PARSER_CTX(pctx), "Single revision of the module \"%s\" imported twice.", imp->name);
+ }
+ }
+ }
+ LY_CHECK_RET(lysp_load_submodules(pctx, pmod, new_mods));
+
+ pmod->parsing = 0;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Generate path of the given paresed node.
+ *
+ * @param[in] node Schema path of this node will be generated.
+ * @param[in] parent Build relative path only until this parent is found. If NULL, the full absolute path is printed.
+ * @return NULL in case of memory allocation error, path of the node otherwise.
+ * In case the @p buffer is NULL, the returned string is dynamically allocated and caller is responsible to free it.
+ */
+static char *
+lysp_path_until(const struct lysp_node *node, const struct lysp_node *parent, const struct lysp_module *pmod)
+{
+ const struct lysp_node *iter, *par;
+ char *path = NULL, *s;
+ const char *slash;
+ int len = 0;
+
+ for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
+ if (parent && (iter->parent == parent)) {
+ slash = "";
+ } else {
+ slash = "/";
+ }
+
+ s = path;
+ par = iter->parent;
+ if (!par) {
+ /* print prefix */
+ len = asprintf(&path, "%s%s:%s%s", slash, pmod->mod->name, iter->name, s ? s : "");
+ } else {
+ /* prefix is the same as in parent */
+ len = asprintf(&path, "%s%s%s", slash, iter->name, s ? s : "");
+ }
+ free(s);
+ }
+
+ if (len < 0) {
+ free(path);
+ path = NULL;
+ } else if (len == 0) {
+ path = strdup("/");
+ }
+
+ return path;
+}
+
+/**
+ * @brief Build log path for a parsed extension instance.
+ *
+ * @param[in] pcxt Parse context.
+ * @param[in] ext Parsed extension instance.
+ * @param[out] path Generated path.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_resolve_ext_instance_log_path(const struct lysp_ctx *pctx, const struct lysp_ext_instance *ext, char **path)
+{
+ char *buf = NULL;
+ uint32_t used = 0, size = 0;
+
+ if (ext->parent_stmt & LY_STMT_NODE_MASK) {
+ /* parsed node path */
+ buf = lysp_path_until(ext->parent, NULL, PARSER_CUR_PMOD(pctx));
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ size = used = strlen(buf);
+
+ /* slash */
+ size += 1;
+ buf = realloc(buf, size + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ used += sprintf(buf + used, "/");
+ } else {
+ /* module */
+ size += 1 + strlen(PARSER_CUR_PMOD(pctx)->mod->name) + 1;
+ buf = realloc(buf, size + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ used += sprintf(buf + used, "/%s:", PARSER_CUR_PMOD(pctx)->mod->name);
+ }
+
+ /* extension name */
+ size += 12 + strlen(ext->name) + 2;
+ buf = realloc(buf, size + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ used += sprintf(buf + used, "{extension='%s'}", ext->name);
+
+ /* extension argument */
+ if (ext->argument) {
+ size += 1 + strlen(ext->argument);
+ buf = realloc(buf, size + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(PARSER_CTX(pctx)), LY_EMEM);
+ used += sprintf(buf + used, "/%s", ext->argument);
+ }
+
+ *path = buf;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Resolve (find) all extension instance records and finish their parsing.
+ *
+ * @param[in] pctx Parse context with all the parsed extension instances.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_resolve_ext_instance_records(struct lysp_ctx *pctx)
+{
+ LY_ERR r;
+ struct lysf_ctx fctx = {.ctx = PARSER_CTX(pctx)};
+ struct lysp_ext_instance *exts, *ext;
+ const struct lys_module *mod;
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u;
+ char *path = NULL;
+
+ /* first finish parsing all extension instances ... */
+ for (i = 0; i < pctx->ext_inst.count; ++i) {
+ exts = pctx->ext_inst.objs[i];
+ LY_ARRAY_FOR(exts, u) {
+ ext = &exts[u];
+
+ /* find the extension definition */
+ LY_CHECK_RET(lysp_ext_find_definition(PARSER_CTX(pctx), ext, &mod, &ext->def));
+
+ /* resolve the argument, if needed */
+ LY_CHECK_RET(lysp_ext_instance_resolve_argument(PARSER_CTX(pctx), ext));
+
+ /* find the extension record, if any */
+ ext->record = lyplg_ext_record_find(mod->name, mod->revision, ext->def->name);
+ }
+ }
+
+ /* ... then call the parse callback */
+ for (i = 0; i < pctx->ext_inst.count; ++i) {
+ exts = pctx->ext_inst.objs[i];
+ u = 0;
+ while (u < LY_ARRAY_COUNT(exts)) {
+ ext = &exts[u];
+ if (!ext->record || !ext->record->plugin.parse) {
+ goto next_iter;
+ }
+
+ /* set up log path */
+ if ((r = lysp_resolve_ext_instance_log_path(pctx, ext, &path))) {
+ return r;
+ }
+ LOG_LOCSET(NULL, NULL, path, NULL);
+
+ /* parse */
+ r = ext->record->plugin.parse(pctx, ext);
+
+ LOG_LOCBACK(0, 0, 1, 0);
+ free(path);
+
+ if (r == LY_ENOT) {
+ /* instance should be ignored, remove it */
+ lysp_ext_instance_free(&fctx, ext);
+ LY_ARRAY_DECREMENT(exts);
+ if (u < LY_ARRAY_COUNT(exts)) {
+ /* replace by the last item */
+ *ext = exts[LY_ARRAY_COUNT(exts)];
+ } /* else if there are no more items, leave the empty array, we are not able to free it */
+ continue;
+ } else if (r) {
+ /* error */
+ return r;
+ }
+
+next_iter:
+ ++u;
+ }
+ }
+
+ 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)
+{
+ LY_ERR ret;
+ struct lysp_submodule *submod = NULL, *latest_sp;
+ struct lysp_yang_ctx *yangctx = NULL;
+ struct lysp_yin_ctx *yinctx = NULL;
+ struct lysp_ctx *pctx;
+ struct lysf_ctx fctx = {.ctx = ctx};
+
+ 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);
+ pctx = (struct lysp_ctx *)yinctx;
+ break;
+ case LYS_IN_YANG:
+ ret = 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;
+ break;
+ }
+ LY_CHECK_GOTO(ret, error);
+ assert(submod);
+
+ /* make sure that the newest revision is at position 0 */
+ lysp_sort_revisions(submod->revs);
+
+ /* decide the latest revision */
+ latest_sp = (struct lysp_submodule *)ly_ctx_get_submodule2_latest(submod->mod, submod->name);
+ if (latest_sp) {
+ if (submod->revs) {
+ if (!latest_sp->revs) {
+ /* latest has no revision, so mod is anyway newer */
+ submod->latest_revision = latest_sp->latest_revision;
+ /* the latest_sp is zeroed later when the new module is being inserted into the context */
+ } else if (strcmp(submod->revs[0].date, latest_sp->revs[0].date) > 0) {
+ submod->latest_revision = latest_sp->latest_revision;
+ /* the latest_sp is zeroed later when the new module is being inserted into the context */
+ } else {
+ latest_sp = NULL;
+ }
+ } else {
+ latest_sp = NULL;
+ }
+ } else {
+ submod->latest_revision = 1;
+ }
+
+ if (custom_check) {
+ LY_CHECK_GOTO(ret = custom_check(ctx, NULL, submod, check_data), error);
+ }
+
+ if (latest_sp) {
+ latest_sp->latest_revision = 0;
+ }
+
+ 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);
+
+ 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;
+}
+
+/**
+ * @brief Add ietf-netconf metadata to the parsed module. Operation, filter, and select are added.
+ *
+ * @param[in] pctx Parse context.
+ * @param[in] mod Parsed module to add to.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
+{
+ struct lysp_ext_instance *extp;
+ struct lysp_stmt *stmt;
+ struct lysp_import *imp;
+
+ /*
+ * 1) edit-config's operation
+ */
+ LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
+ LY_CHECK_ERR_RET(!extp, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "operation", 0, &extp->argument));
+ extp->format = LY_VALUE_SCHEMA;
+ extp->prefix_data = mod;
+ extp->parent = mod;
+ extp->parent_stmt = LY_STMT_MODULE;
+ extp->flags = LYS_INTERNAL;
+
+ extp->child = stmt = calloc(1, sizeof *extp->child);
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enumeration", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_TYPE;
+
+ stmt->child = calloc(1, sizeof *stmt->child);
+ stmt = stmt->child;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "merge", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ stmt->next = calloc(1, sizeof *stmt->child);
+ stmt = stmt->next;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "replace", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ stmt->next = calloc(1, sizeof *stmt->child);
+ stmt = stmt->next;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "create", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ stmt->next = calloc(1, sizeof *stmt->child);
+ stmt = stmt->next;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "delete", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ stmt->next = calloc(1, sizeof *stmt->child);
+ stmt = stmt->next;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "remove", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ /*
+ * 2) filter's type
+ */
+ LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
+ LY_CHECK_ERR_RET(!extp, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "type", 0, &extp->argument));
+ extp->format = LY_VALUE_SCHEMA;
+ extp->prefix_data = mod;
+ extp->parent = mod;
+ extp->parent_stmt = LY_STMT_MODULE;
+ extp->flags = LYS_INTERNAL;
+
+ extp->child = stmt = calloc(1, sizeof *extp->child);
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enumeration", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_TYPE;
+
+ stmt->child = calloc(1, sizeof *stmt->child);
+ stmt = stmt->child;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "subtree", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ stmt->next = calloc(1, sizeof *stmt->child);
+ stmt = stmt->next;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "xpath", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_ENUM;
+
+ /* if-feature for enum allowed only for YANG 1.1 modules */
+ if (mod->version >= LYS_VERSION_1_1) {
+ stmt->child = calloc(1, sizeof *stmt->child);
+ stmt = stmt->child;
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "if-feature", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "xpath", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_IF_FEATURE;
+ }
+
+ /*
+ * 3) filter's select
+ */
+ LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
+ LY_CHECK_ERR_RET(!extp, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "select", 0, &extp->argument));
+ extp->format = LY_VALUE_SCHEMA;
+ extp->prefix_data = mod;
+ extp->parent = mod;
+ extp->parent_stmt = LY_STMT_MODULE;
+ extp->flags = LYS_INTERNAL;
+
+ extp->child = stmt = calloc(1, sizeof *extp->child);
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "yang_:xpath1.0", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_TYPE;
+
+ if (LY_ARRAY_COUNT(mod->exts) == 3) {
+ /* first extension instances */
+ assert(pctx->main_ctx == pctx);
+ LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
+ }
+
+ /* 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));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "md_", 0, &imp->prefix));
+ imp->flags = LYS_INTERNAL;
+
+ LY_ARRAY_NEW_RET(mod->mod->ctx, mod->imports, imp, LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "ietf-yang-types", 0, &imp->name));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "yang_", 0, &imp->prefix));
+ imp->flags = LYS_INTERNAL;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add ietf-netconf-with-defaults "default" metadata to the parsed module.
+ *
+ * @param[in] pctx Parse context.
+ * @param[in] mod Parsed module to add to.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lysp_add_internal_ietf_netconf_with_defaults(struct lysp_ctx *pctx, struct lysp_module *mod)
+{
+ struct lysp_ext_instance *extp;
+ struct lysp_stmt *stmt;
+ struct lysp_import *imp;
+
+ /* add new extension instance */
+ LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
+
+ /* fill in the extension instance fields */
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "default", 0, &extp->argument));
+ extp->format = LY_VALUE_SCHEMA;
+ extp->prefix_data = mod;
+ extp->parent = mod;
+ extp->parent_stmt = LY_STMT_MODULE;
+ extp->flags = LYS_INTERNAL;
+
+ extp->child = stmt = calloc(1, sizeof *extp->child);
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "boolean", 0, &stmt->arg));
+ stmt->format = LY_VALUE_SCHEMA;
+ stmt->prefix_data = mod;
+ stmt->kw = LY_STMT_TYPE;
+
+ if (LY_ARRAY_COUNT(mod->exts) == 1) {
+ /* first extension instance */
+ assert(pctx->main_ctx == pctx);
+ LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
+ }
+
+ /* create new import for the used prefix */
+ 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));
+ LY_CHECK_RET(lydict_insert(mod->mod->ctx, "md_", 0, &imp->prefix));
+ imp->flags = LYS_INTERNAL;
+
+ return LY_SUCCESS;
+}
+
+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)
+{
+ 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};
+ char *filename, *rev, *dot;
+ size_t len;
+ ly_bool module_created = 0;
+
+ assert(ctx && in && new_mods);
+
+ if (module) {
+ *module = NULL;
+ }
+
+ mod = calloc(1, sizeof *mod);
+ LY_CHECK_ERR_RET(!mod, LOGMEM(ctx), LY_EMEM);
+ mod->ctx = ctx;
+
+ /* parse */
+ switch (format) {
+ case LYS_IN_YIN:
+ ret = yin_parse_module(&yinctx, in, mod);
+ pctx = (struct lysp_ctx *)yinctx;
+ break;
+ case LYS_IN_YANG:
+ ret = yang_parse_module(&yangctx, in, mod);
+ pctx = (struct lysp_ctx *)yangctx;
+ break;
+ default:
+ LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
+ ret = LY_EINVAL;
+ break;
+ }
+ LY_CHECK_GOTO(ret, 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);
+ }
+
+ /* decide the latest revision */
+ latest = ly_ctx_get_module_latest(ctx, mod->name);
+ if (latest) {
+ if (mod->revision) {
+ if (!latest->revision) {
+ /* latest has no revision, so mod is anyway newer */
+ mod->latest_revision = latest->latest_revision & (LYS_MOD_LATEST_REV | LYS_MOD_LATEST_SEARCHDIRS);
+ /* the latest is zeroed later when the new module is being inserted into the context */
+ } else if (strcmp(mod->revision, latest->revision) > 0) {
+ mod->latest_revision = latest->latest_revision & (LYS_MOD_LATEST_REV | LYS_MOD_LATEST_SEARCHDIRS);
+ /* the latest is zeroed later when the new module is being inserted into the context */
+ } else {
+ latest = NULL;
+ }
+ } else {
+ latest = NULL;
+ }
+ } else {
+ mod->latest_revision = LYS_MOD_LATEST_REV;
+ }
+
+ if (custom_check) {
+ LY_CHECK_GOTO(ret = custom_check(ctx, mod->parsed, NULL, check_data), cleanup);
+ }
+
+ /* check whether it is not already in the context in the same revision */
+ mod_dup = ly_ctx_get_module(ctx, mod->name, mod->revision);
+ if (mod_dup) {
+ /* nothing to do */
+ LOGVRB("Module \"%s@%s\" is already present in the context.", mod_dup->name,
+ mod_dup->revision ? mod_dup->revision : "<none>");
+ goto cleanup;
+ }
+
+ /* check whether there is not a namespace collision */
+ mod_dup = ly_ctx_get_module_latest_ns(ctx, mod->ns);
+ 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;
+ goto cleanup;
+ }
+
+ switch (in->type) {
+ case LY_IN_FILEPATH:
+ /* check that name and revision match filename */
+ filename = strrchr(in->method.fpath.filepath, '/');
+ if (!filename) {
+ filename = in->method.fpath.filepath;
+ } else {
+ filename++;
+ }
+ rev = strchr(filename, '@');
+ dot = strrchr(filename, '.');
+
+ /* name */
+ len = strlen(mod->name);
+ if (strncmp(filename, mod->name, len) ||
+ ((rev && (rev != &filename[len])) || (!rev && (dot != &filename[len])))) {
+ LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
+ }
+ if (rev) {
+ len = dot - ++rev;
+ if (!mod->parsed->revs || (len != LY_REV_SIZE - 1) || strncmp(mod->parsed->revs[0].date, rev, len)) {
+ LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
+ mod->parsed->revs ? mod->parsed->revs[0].date : "none");
+ }
+ }
+
+ break;
+ case LY_IN_FD:
+ case LY_IN_FILE:
+ case LY_IN_MEMORY:
+ /* nothing special to do */
+ break;
+ case LY_IN_ERROR:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ lys_parser_fill_filepath(ctx, in, &mod->filepath);
+
+ if (latest) {
+ latest->latest_revision &= ~(LYS_MOD_LATEST_REV | LYS_MOD_LATEST_SEARCHDIRS);
+ }
+
+ /* 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);
+ } else if (!strcmp(mod->name, "ietf-netconf-with-defaults")) {
+ LY_CHECK_GOTO(ret = lysp_add_internal_ietf_netconf_with_defaults(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;
+
+ /* add into context */
+ ret = ly_set_add(&ctx->list, mod, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ ctx->change_count++;
+
+ /* resolve includes and all imports */
+ LY_CHECK_GOTO(ret = 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);
+
+ /* 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);
+
+ /* compile features */
+ LY_CHECK_GOTO(ret = lys_compile_feature_iffeatures(mod->parsed), cleanup);
+
+ /* compile identities */
+ LY_CHECK_GOTO(ret = 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 */
+ struct ly_err_item *e = ly_err_last(ctx);
+
+ if (e && (!e->path || !strncmp(e->path, "Line ", ly_strlen_const("Line ")))) {
+ LOGERR(ctx, ret, "Parsing module \"%s\" failed.", mod->name);
+ }
+ }
+ }
+ if (!module_created) {
+ fctx.mod = mod;
+ lys_module_free(&fctx, mod, 0);
+ lysf_ctx_erase(&fctx);
+
+ mod = mod_dup;
+ }
+
+ if (format == LYS_IN_YANG) {
+ lysp_yang_ctx_free(yangctx);
+ } else {
+ lysp_yin_ctx_free(yinctx);
+ }
+
+ if (!ret && module) {
+ *module = mod;
+ }
+ return ret;
+}
+
+static LYS_INFORMAT
+lys_parse_get_format(const struct ly_in *in, LYS_INFORMAT format)
+{
+ if (!format && (in->type == LY_IN_FILEPATH)) {
+ /* unknown format - try to detect it from filename's suffix */
+ const char *path = in->method.fpath.filepath;
+ size_t len = strlen(path);
+
+ /* ignore trailing whitespaces */
+ for ( ; len > 0 && isspace(path[len - 1]); len--) {}
+
+ if ((len >= LY_YANG_SUFFIX_LEN + 1) &&
+ !strncmp(&path[len - LY_YANG_SUFFIX_LEN], LY_YANG_SUFFIX, LY_YANG_SUFFIX_LEN)) {
+ format = LYS_IN_YANG;
+ } else if ((len >= LY_YIN_SUFFIX_LEN + 1) &&
+ !strncmp(&path[len - LY_YIN_SUFFIX_LEN], LY_YIN_SUFFIX, LY_YIN_SUFFIX_LEN)) {
+ format = LYS_IN_YIN;
+ } /* else still unknown */
+ }
+
+ return format;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features, struct lys_module **module)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *mod;
+
+ if (module) {
+ *module = NULL;
+ }
+ LY_CHECK_ARG_RET(NULL, ctx, in, LY_EINVAL);
+
+ format = lys_parse_get_format(in, format);
+ LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
+
+ /* remember input position */
+ in->func_start = in->current;
+
+ /* parse */
+ ret = lys_parse_in(ctx, in, format, NULL, NULL, &ctx->unres.creating, &mod);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* implement */
+ ret = _lys_set_implemented(mod, features, &ctx->unres);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!(ctx->flags & LY_CTX_EXPLICIT_COMPILE)) {
+ /* create dep set for the module and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(ctx, &ctx->unres.dep_sets, mod), cleanup);
+
+ /* (re)compile the whole dep set (other dep sets will have no modules marked for compilation) */
+ LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup);
+
+ /* unres resolved */
+ lys_unres_glob_erase(&ctx->unres);
+ }
+
+cleanup:
+ if (ret) {
+ lys_unres_glob_revert(ctx, &ctx->unres);
+ lys_unres_glob_erase(&ctx->unres);
+ } else if (module) {
+ *module = mod;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct lys_module **module)
+{
+ LY_ERR ret;
+ struct ly_in *in = NULL;
+
+ LY_CHECK_ARG_RET(ctx, data, format != LYS_IN_UNKNOWN, LY_EINVAL);
+
+ LY_CHECK_ERR_RET(ret = ly_in_new_memory(data, &in), LOGERR(ctx, ret, "Unable to create input handler."), ret);
+
+ ret = lys_parse(ctx, in, format, NULL, module);
+ ly_in_free(in, 0);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_module **module)
+{
+ LY_ERR ret;
+ struct ly_in *in = NULL;
+
+ LY_CHECK_ARG_RET(ctx, fd > -1, format != LYS_IN_UNKNOWN, LY_EINVAL);
+
+ LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, &in), LOGERR(ctx, ret, "Unable to create input handler."), ret);
+
+ ret = lys_parse(ctx, in, format, NULL, module);
+ ly_in_free(in, 0);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, struct lys_module **module)
+{
+ LY_ERR ret;
+ struct ly_in *in = NULL;
+
+ LY_CHECK_ARG_RET(ctx, path, format != LYS_IN_UNKNOWN, LY_EINVAL);
+
+ LY_CHECK_ERR_RET(ret = ly_in_new_filepath(path, 0, &in),
+ LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", path), ret);
+
+ ret = lys_parse(ctx, in, format, NULL, module);
+ ly_in_free(in, 0);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lys_search_localfile(const char * const *searchpaths, ly_bool cwd, const char *name, const char *revision,
+ char **localfile, LYS_INFORMAT *format)
+{
+ LY_ERR ret = LY_EMEM;
+ size_t len, flen, match_len = 0, dir_len;
+ ly_bool implicit_cwd = 0;
+ char *wd, *wn = NULL;
+ DIR *dir = NULL;
+ struct dirent *file;
+ char *match_name = NULL;
+ LYS_INFORMAT format_aux, match_format = 0;
+ struct ly_set *dirs;
+ struct stat st;
+
+ LY_CHECK_ARG_RET(NULL, localfile, LY_EINVAL);
+
+ /* start to fill the dir fifo with the context's search path (if set)
+ * and the current working directory */
+ LY_CHECK_RET(ly_set_new(&dirs));
+
+ len = strlen(name);
+ if (cwd) {
+ wd = get_current_dir_name();
+ if (!wd) {
+ LOGMEM(NULL);
+ goto cleanup;
+ } else {
+ /* add implicit current working directory (./) to be searched,
+ * this directory is not searched recursively */
+ ret = ly_set_add(dirs, wd, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ implicit_cwd = 1;
+ }
+ }
+ if (searchpaths) {
+ for (uint64_t i = 0; searchpaths[i]; i++) {
+ /* check for duplicities with the implicit current working directory */
+ if (implicit_cwd && !strcmp(dirs->objs[0], searchpaths[i])) {
+ implicit_cwd = 0;
+ continue;
+ }
+ wd = strdup(searchpaths[i]);
+ if (!wd) {
+ LOGMEM(NULL);
+ goto cleanup;
+ } else {
+ ret = ly_set_add(dirs, wd, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ }
+ wd = NULL;
+
+ /* start searching */
+ while (dirs->count) {
+ free(wd);
+ free(wn); wn = NULL;
+
+ dirs->count--;
+ wd = (char *)dirs->objs[dirs->count];
+ dirs->objs[dirs->count] = NULL;
+ LOGVRB("Searching for \"%s\" in \"%s\".", name, wd);
+
+ if (dir) {
+ closedir(dir);
+ }
+ dir = opendir(wd);
+ dir_len = strlen(wd);
+ if (!dir) {
+ LOGWRN(NULL, "Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
+ } else {
+ while ((file = readdir(dir))) {
+ if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
+ /* skip . and .. */
+ continue;
+ }
+ free(wn);
+ if (asprintf(&wn, "%s/%s", wd, file->d_name) == -1) {
+ LOGMEM(NULL);
+ goto cleanup;
+ }
+ if (stat(wn, &st) == -1) {
+ LOGWRN(NULL, "Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
+ file->d_name, wd, strerror(errno));
+ continue;
+ }
+ if (S_ISDIR(st.st_mode) && (dirs->count || !implicit_cwd)) {
+ /* we have another subdirectory in searchpath to explore,
+ * subdirectories are not taken into account in current working dir (dirs->set.g[0]) */
+ ret = ly_set_add(dirs, wn, 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* continue with the next item in current directory */
+ wn = NULL;
+ continue;
+ } else if (!S_ISREG(st.st_mode)) {
+ /* not a regular file (note that we see the target of symlinks instead of symlinks */
+ continue;
+ }
+
+ /* here we know that the item is a file which can contain a module */
+ if (strncmp(name, file->d_name, len) ||
+ ((file->d_name[len] != '.') && (file->d_name[len] != '@'))) {
+ /* different filename than the module we search for */
+ continue;
+ }
+
+ /* get type according to filename suffix */
+ flen = strlen(file->d_name);
+ if ((flen >= LY_YANG_SUFFIX_LEN + 1) &&
+ !strcmp(&file->d_name[flen - LY_YANG_SUFFIX_LEN], LY_YANG_SUFFIX)) {
+ format_aux = LYS_IN_YANG;
+ } else if ((flen >= LY_YIN_SUFFIX_LEN + 1) &&
+ !strcmp(&file->d_name[flen - LY_YIN_SUFFIX_LEN], LY_YIN_SUFFIX)) {
+ format_aux = LYS_IN_YIN;
+ } else {
+ /* not supportde suffix/file format */
+ continue;
+ }
+
+ if (revision) {
+ /* we look for the specific revision, try to get it from the filename */
+ if (file->d_name[len] == '@') {
+ /* check revision from the filename */
+ if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
+ /* another revision */
+ continue;
+ } else {
+ /* exact revision */
+ free(match_name);
+ match_name = wn;
+ wn = NULL;
+ match_len = dir_len + 1 + len;
+ match_format = format_aux;
+ goto success;
+ }
+ } else {
+ /* continue trying to find exact revision match, use this only if not found */
+ free(match_name);
+ match_name = wn;
+ wn = NULL;
+ match_len = dir_len + 1 + len;
+ match_format = format_aux;
+ continue;
+ }
+ } else {
+ /* remember the revision and try to find the newest one */
+ if (match_name) {
+ if ((file->d_name[len] != '@') ||
+ lysp_check_date(NULL, &file->d_name[len + 1],
+ flen - ((format_aux == LYS_IN_YANG) ? LY_YANG_SUFFIX_LEN : LY_YIN_SUFFIX_LEN) - len - 1, NULL)) {
+ continue;
+ } else if ((match_name[match_len] == '@') &&
+ (strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
+ continue;
+ }
+ free(match_name);
+ }
+
+ match_name = wn;
+ wn = NULL;
+ match_len = dir_len + 1 + len;
+ match_format = format_aux;
+ continue;
+ }
+ }
+ }
+ }
+
+success:
+ (*localfile) = match_name;
+ match_name = NULL;
+ if (format) {
+ (*format) = match_format;
+ }
+ ret = LY_SUCCESS;
+
+cleanup:
+ free(wn);
+ free(wd);
+ if (dir) {
+ closedir(dir);
+ }
+ free(match_name);
+ ly_set_free(dirs, free);
+
+ return ret;
+}
diff --git a/src/tree_schema.h b/src/tree_schema.h
new file mode 100644
index 0000000..c57a0fc
--- /dev/null
+++ b/src/tree_schema.h
@@ -0,0 +1,2248 @@
+/**
+ * @file tree_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang representation of YANG schema trees.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_SCHEMA_H_
+#define LY_TREE_SCHEMA_H_
+
+#define PCRE2_CODE_UNIT_WIDTH 8
+
+#include <pcre2.h>
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "log.h"
+#include "tree.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_ctx;
+struct ly_path;
+struct ly_set;
+struct lys_module;
+struct lysc_node;
+struct lyxp_expr;
+
+/**
+ * @page howtoSchema YANG Modules
+ *
+ * To be able to work with YANG data instances, libyang has to represent YANG data models. All the processed modules are stored
+ * in libyang [context](@ref howtoContext) and loaded using [parser functions](@ref howtoSchemaParsers). It means, that there is
+ * no way to create/change YANG module programmatically. However, all the YANG model definitions are available and can be examined
+ * through the C structures. All the context's modules together form YANG Schema for the data being instantiated.
+ *
+ * Any YANG module is represented as ::lys_module. In fact, the module is represented in two different formats. As ::lys_module.parsed,
+ * there is a parsed schema reflecting the source YANG module. It is exactly what is read from the input. This format is good for
+ * converting from one format to another (YANG to YIN and vice versa), but it is not very useful for validating/manipulating YANG
+ * data. Therefore, there is ::lys_module.compiled storing the compiled YANG module. It is based on the parsed module, but all the
+ * references are resolved. It means that, for example, there are no `grouping`s or `typedef`s since they are supposed to be placed instead of
+ * `uses` or `type` references. This split also means, that the YANG module is fully validated after compilation of the parsed
+ * representation of the module. YANG submodules are available only in the parsed representation. When a submodule is compiled, it
+ * is fully integrated into its main module.
+ *
+ * The context can contain even modules without the compiled representation. Such modules are still useful as imports of other
+ * modules. The grouping or typedef definition can be even compiled into the importing modules. This is actually the main
+ * difference between the imported and implemented modules in the libyang context. The implemented modules are compiled while the
+ * imported modules are only parsed.
+ *
+ * By default, the module is implemented (and compiled) in case it is explicitly loaded or referenced in another module as
+ * target of leafref, augment or deviation. This behavior can be changed via context options ::LY_CTX_ALL_IMPLEMENTED, when
+ * all the modules in the context are marked as implemented (note the problem with multiple revisions of a single module),
+ * or by ::LY_CTX_REF_IMPLEMENTED option, extending the set of references making the module implemented by when, must and
+ * default statements.
+ *
+ * All modules with deviation definition are always marked as implemented. The imported (not implemented) module can be set implemented by ::lys_set_implemented(). But
+ * the implemented module cannot be changed back to just imported module. Note also that only one revision of a specific module
+ * can be implemented in a single context. The imported modules are used only as a
+ * source of definitions for types and groupings for uses statements. The data in such modules are ignored - caller
+ * is not allowed to create the data (including instantiating identities) defined in the model via data parsers,
+ * the default nodes are not added into any data tree and mandatory nodes are not checked in the data trees.
+ *
+ * The compiled schema tree nodes are able to hold private objects (::lysc_node.priv as a pointer to a structure,
+ * function, variable, ...) used by a caller application unless ::LY_CTX_SET_PRIV_PARSED is set, in that case
+ * the ::lysc_node.priv pointers are used by libyang.
+ * Note that the object is not freed by libyang when the context is being destroyed. So the caller is responsible
+ * for freeing the provided structure after the context is destroyed or the private pointer is set to NULL in
+ * appropriate schema nodes where the object was previously set. Also ::lysc_tree_dfs_full() can be useful to manage
+ * the private data.
+ *
+ * Despite all the schema structures and their members are available as part of the libyang API and callers can use
+ * it to navigate through the schema tree structure or to obtain various information, we recommend to use the following
+ * macros for the specific actions.
+ * - ::LYSC_TREE_DFS_BEGIN and ::LYSC_TREE_DFS_END to traverse the schema tree (depth-first).
+ * - ::LY_LIST_FOR and ::LY_ARRAY_FOR as described on @ref howtoStructures page.
+ *
+ * Further information about modules handling can be found on the following pages:
+ * - @subpage howtoSchemaParsers
+ * - @subpage howtoSchemaFeatures
+ * - @subpage howtoPlugins
+ * - @subpage howtoSchemaPrinters
+ *
+ * \note There are many functions to access information from the schema trees. Details are available in
+ * the [Schema Tree module](@ref schematree).
+ *
+ * For information about difference between implemented and imported modules, see the
+ * [context description](@ref howtoContext).
+ *
+ * Functions List (not assigned to above subsections)
+ * --------------------------------------------------
+ * - ::lys_getnext()
+ * - ::lys_nodetype2str()
+ * - ::lys_set_implemented()
+ *
+ * - ::lysc_has_when()
+ * - ::lysc_owner_module()
+ *
+ * - ::lysc_node_child()
+ * - ::lysc_node_actions()
+ * - ::lysc_node_notifs()
+ *
+ * - ::lysp_node_child()
+ * - ::lysp_node_actions()
+ * - ::lysp_node_notifs()
+ * - ::lysp_node_groupings()
+ * - ::lysp_node_typedefs()
+ */
+
+/**
+ * @page howtoSchemaFeatures YANG Features
+ *
+ * YANG feature statement is an important part of the language which can significantly affect the meaning of the schemas.
+ * Modifying features may have similar effects as loading/removing schema from the context so it is limited to context
+ * preparation period before working with data. YANG features, respectively their use in if-feature
+ * statements, are evaluated as part of schema compilation so a feature-specific compiled schema tree is generated
+ * as a result.
+ *
+ * To enable any features, they must currently be specified when implementing a new schema with ::lys_parse() or
+ * ::ly_ctx_load_module(). To later examine what the status of a feature is, check its ::LYS_FENABLED flag or
+ * search for it first with ::lys_feature_value(). Lastly, to evaluate compiled if-features, use ::lysc_iffeature_value().
+ *
+ * To iterate over all features of a particular YANG module, use ::lysp_feature_next().
+ *
+ * Note, that the feature's state can affect some of the output formats (e.g. Tree format).
+ *
+ * Functions List
+ * --------------
+ * - ::lys_feature_value()
+ * - ::lysc_iffeature_value()
+ * - ::lysp_feature_next()
+ */
+
+/**
+ * @ingroup trees
+ * @defgroup schematree Schema Tree
+ * @{
+ *
+ * Data structures and functions to manipulate and access schema tree.
+ */
+
+/* *INDENT-OFF* */
+
+/**
+ * @brief Macro to iterate via all elements in a schema (sub)tree including input and output.
+ * Note that __actions__ and __notifications__ of traversed nodes __are ignored__! To traverse
+ * on all the nodes including those, use ::lysc_tree_dfs_full() instead.
+ *
+ * This is the opening part to the #LYSC_TREE_DFS_END - they always have to be used together.
+ *
+ * The function follows deep-first search algorithm:
+ * <pre>
+ * 1
+ * / \
+ * 2 4
+ * / / \
+ * 3 5 6
+ * </pre>
+ *
+ * Use the same parameters for #LYSC_TREE_DFS_BEGIN and #LYSC_TREE_DFS_END. While
+ * START can be any of the lysc_node* types (including lysc_node_action and lysc_node_notif),
+ * ELEM variable must be of the struct lysc_node* type.
+ *
+ * To skip a particular subtree, instead of the continue statement, set LYSC_TREE_DFS_continue
+ * variable to non-zero value.
+ *
+ * Use with opening curly bracket '{' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+#define LYSC_TREE_DFS_BEGIN(START, ELEM) \
+ { ly_bool LYSC_TREE_DFS_continue = 0; struct lysc_node *LYSC_TREE_DFS_next; \
+ for ((ELEM) = (LYSC_TREE_DFS_next) = (struct lysc_node *)(START); \
+ (ELEM); \
+ (ELEM) = (LYSC_TREE_DFS_next), LYSC_TREE_DFS_continue = 0)
+
+/**
+ * @brief Macro to iterate via all elements in a (sub)tree. This is the closing part
+ * to the #LYSC_TREE_DFS_BEGIN - they always have to be used together.
+ *
+ * Use the same parameters for #LYSC_TREE_DFS_BEGIN and #LYSC_TREE_DFS_END. While
+ * START can be a pointer to any of the lysc_node* types (including lysc_node_action and lysc_node_notif),
+ * ELEM variable must be pointer to the lysc_node type.
+ *
+ * Use with closing curly bracket '}' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+#define LYSC_TREE_DFS_END(START, ELEM) \
+ /* select element for the next run - children first */ \
+ if (LYSC_TREE_DFS_continue) { \
+ (LYSC_TREE_DFS_next) = NULL; \
+ } else { \
+ (LYSC_TREE_DFS_next) = (struct lysc_node *)lysc_node_child(ELEM); \
+ } \
+ if (!(LYSC_TREE_DFS_next)) { \
+ /* no children, try siblings */ \
+ _LYSC_TREE_DFS_NEXT(START, ELEM, LYSC_TREE_DFS_next); \
+ } \
+ while (!(LYSC_TREE_DFS_next)) { \
+ /* parent is already processed, go to its sibling */ \
+ (ELEM) = (ELEM)->parent; \
+ _LYSC_TREE_DFS_NEXT(START, ELEM, LYSC_TREE_DFS_next); \
+ } }
+
+/**
+ * @brief Helper macro for #LYSC_TREE_DFS_END, should not be used directly!
+ */
+#define _LYSC_TREE_DFS_NEXT(START, ELEM, NEXT) \
+ if ((ELEM) == (struct lysc_node *)(START)) { \
+ /* we are done, no next element to process */ \
+ break; \
+ } \
+ (NEXT) = (ELEM)->next;
+
+/* *INDENT-ON* */
+
+#define LY_REV_SIZE 11 /**< revision data string length (including terminating NULL byte) */
+
+/**
+ * @defgroup schemanodetypes Schema Node Types
+ * Values of the ::lysp_node.nodetype and ::lysc_node.nodetype members.
+ * @{
+ */
+#define LYS_UNKNOWN 0x0000 /**< uninitalized unknown statement node */
+#define LYS_CONTAINER 0x0001 /**< container statement node */
+#define LYS_CHOICE 0x0002 /**< choice statement node */
+#define LYS_LEAF 0x0004 /**< leaf statement node */
+#define LYS_LEAFLIST 0x0008 /**< leaf-list statement node */
+#define LYS_LIST 0x0010 /**< list statement node */
+#define LYS_ANYXML 0x0020 /**< anyxml statement node */
+#define LYS_ANYDATA 0x0060 /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
+#define LYS_CASE 0x0080 /**< case statement node */
+
+#define LYS_RPC 0x0100 /**< RPC statement node */
+#define LYS_ACTION 0x0200 /**< action statement node */
+#define LYS_NOTIF 0x0400 /**< notification statement node */
+
+#define LYS_USES 0x0800 /**< uses statement node */
+#define LYS_INPUT 0x1000 /**< RPC/action input node */
+#define LYS_OUTPUT 0x2000 /**< RPC/action output node */
+#define LYS_GROUPING 0x4000
+#define LYS_AUGMENT 0x8000
+
+#define LYS_NODETYPE_MASK 0xffff /**< Mask for nodetypes, the value is limited for 16 bits */
+/** @} schemanodetypes */
+
+/**
+ * @brief YANG import-stmt
+ */
+struct lysp_import {
+ struct lys_module *module; /**< pointer to the imported module
+ (mandatory, but resolved when the referring module is completely parsed) */
+ const char *name; /**< name of the imported module (mandatory) */
+ const char *prefix; /**< prefix for the data from the imported schema (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< LYS_INTERNAL value (@ref snodeflags) */
+ char rev[LY_REV_SIZE]; /**< revision-date of the imported module */
+};
+
+/**
+ * @brief YANG include-stmt
+ */
+struct lysp_include {
+ struct lysp_submodule *submodule;/**< pointer to the parsed submodule structure
+ (mandatory, but resolved when the referring module is completely parsed) */
+ const char *name; /**< name of the included submodule (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ char rev[LY_REV_SIZE]; /**< revision-date of the included submodule */
+ ly_bool injected; /**< flag to mark includes copied into main module from submodules,
+ only for backward compatibility with YANG 1.0, which does not require the
+ main module to include all submodules. */
+};
+
+/**
+ * @brief YANG extension-stmt
+ */
+struct lysp_ext {
+ const char *name; /**< extension name */
+ const char *argname; /**< argument name, NULL if not specified */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< LYS_STATUS_* and LYS_YINELEM_* values (@ref snodeflags) */
+
+ struct lysc_ext *compiled; /**< pointer to the compiled extension definition.
+ The extension definition is compiled only if there is compiled extension instance,
+ otherwise this pointer remains NULL. The compiled extension definition is shared
+ among all extension instances. */
+};
+
+/**
+ * @brief YANG feature-stmt
+ */
+struct lysp_feature {
+ const char *name; /**< feature name (mandatory) */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysc_iffeature *iffeatures_c; /**< compiled if-features */
+ struct lysp_feature **depfeatures; /**< list of pointers to other features depending on this one
+ ([sized array](@ref sizedarrays)) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values and
+ LYS_FENABLED are allowed */
+};
+
+/**
+ * @brief Compiled YANG if-feature-stmt
+ */
+struct lysc_iffeature {
+ uint8_t *expr; /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
+ struct lysp_feature **features; /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Qualified name (optional prefix followed by an identifier).
+ */
+struct lysp_qname {
+ const char *str; /**< qualified name string */
+ const struct lysp_module *mod; /**< module to resolve any prefixes found in the string, it must be
+ stored explicitly because of deviations/refines */
+};
+
+/**
+ * @brief YANG identity-stmt
+ */
+struct lysp_ident {
+ const char *name; /**< identity name (mandatory), including possible prefix */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ const char **bases; /**< list of base identifiers ([sized array](@ref sizedarrays)) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
+};
+
+/**
+ * @brief Covers restrictions: range, length, pattern, must
+ */
+struct lysp_restr {
+#define LYSP_RESTR_PATTERN_ACK 0x06
+#define LYSP_RESTR_PATTERN_NACK 0x15
+ struct lysp_qname arg; /**< The restriction expression/value (mandatory);
+ in case of pattern restriction, the first byte has a special meaning:
+ 0x06 (ACK) for regular match and 0x15 (NACK) for invert-match */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief YANG revision-stmt
+ */
+struct lysp_revision {
+ char date[LY_REV_SIZE]; /**< revision date (madatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Enumeration/Bit value definition
+ */
+struct lysp_type_enum {
+ const char *name; /**< name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ int64_t value; /**< enum's value or bit's position */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ and LYS_SET_VALUE
+ values are allowed */
+};
+
+/**
+ * @brief YANG type-stmt
+ *
+ * Some of the items in the structure may be mandatory, but it is necessary to resolve the type's base type first
+ */
+struct lysp_type {
+ const char *name; /**< name of the type (mandatory) */
+ struct lysp_restr *range; /**< allowed values range - numerical, decimal64 */
+ struct lysp_restr *length; /**< allowed length of the value - string, binary */
+ struct lysp_restr *patterns; /**< list of patterns ([sized array](@ref sizedarrays)) - string */
+ struct lysp_type_enum *enums; /**< list of enum-stmts ([sized array](@ref sizedarrays)) - enum */
+ struct lysp_type_enum *bits; /**< list of bit-stmts ([sized array](@ref sizedarrays)) - bits */
+ struct lyxp_expr *path; /**< parsed path - leafref */
+ const char **bases; /**< list of base identifiers ([sized array](@ref sizedarrays)) - identityref */
+ struct lysp_type *types; /**< list of sub-types ([sized array](@ref sizedarrays)) - union */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+
+ const struct lysp_module *pmod; /**< (sub)module where the type is defined (needed for deviations) */
+ struct lysc_type *compiled; /**< pointer to the compiled custom type, not used for built-in types */
+
+ uint8_t fraction_digits; /**< number of fraction digits - decimal64 */
+ uint8_t require_instance; /**< require-instance flag - leafref, instance */
+ uint16_t flags; /**< [schema node flags](@ref spnodeflags) */
+};
+
+/**
+ * @brief YANG typedef-stmt
+ */
+struct lysp_tpdf {
+ const char *name; /**< name of the newly defined type (mandatory) */
+ const char *units; /**< units of the newly defined type */
+ struct lysp_qname dflt; /**< default value of the newly defined type, it may or may not be a qualified name */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysp_type type; /**< base type from which the typedef is derived (mandatory) */
+ uint16_t flags; /**< [schema node flags](@ref spnodeflags) */
+};
+
+/**
+ * @brief YANG when-stmt
+ */
+struct lysp_when {
+ const char *cond; /**< specified condition (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief YANG refine-stmt
+ */
+struct lysp_refine {
+ const char *nodeid; /**< target descendant schema nodeid (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ const char *presence; /**< presence description */
+ struct lysp_qname *dflts; /**< list of default values ([sized array](@ref sizedarrays)) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint, 0 means unbounded */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+};
+
+/**
+ * @ingroup schematree
+ * @defgroup deviatetypes Deviate types
+ *
+ * Type of the deviate operation (used as ::lysp_deviate.mod)
+ *
+ * @{
+ */
+#define LYS_DEV_NOT_SUPPORTED 1 /**< deviate type not-supported */
+#define LYS_DEV_ADD 2 /**< deviate type add */
+#define LYS_DEV_DELETE 3 /**< deviate type delete */
+#define LYS_DEV_REPLACE 4 /**< deviate type replace */
+/** @} deviatetypes */
+
+/**
+ * @brief Generic deviate structure to get type and cast to lysp_deviate_* structure
+ */
+struct lysp_deviate {
+ uint8_t mod; /**< [type](@ref deviatetypes) of the deviate modification */
+ struct lysp_deviate *next; /**< next deviate structure in the list */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysp_deviate_add {
+ uint8_t mod; /**< [type](@ref deviatetypes) of the deviate modification */
+ struct lysp_deviate *next; /**< next deviate structure in the list */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ const char *units; /**< units of the values */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_qname *uniques; /**< list of uniques specifications ([sized array](@ref sizedarrays)) */
+ struct lysp_qname *dflts; /**< list of default values ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint, 0 means unbounded */
+};
+
+struct lysp_deviate_del {
+ uint8_t mod; /**< [type](@ref deviatetypes) of the deviate modification */
+ struct lysp_deviate *next; /**< next deviate structure in the list */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ const char *units; /**< units of the values */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_qname *uniques; /**< list of uniques specifications ([sized array](@ref sizedarrays)) */
+ struct lysp_qname *dflts; /**< list of default values ([sized array](@ref sizedarrays)) */
+};
+
+struct lysp_deviate_rpl {
+ uint8_t mod; /**< [type](@ref deviatetypes) of the deviate modification */
+ struct lysp_deviate *next; /**< next deviate structure in the list */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysp_type *type; /**< type of the node */
+ const char *units; /**< units of the values */
+ struct lysp_qname dflt; /**< default value */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint, 0 means unbounded */
+};
+
+struct lysp_deviation {
+ const char *nodeid; /**< target absolute schema nodeid (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_deviate *deviates; /**< list of deviate specifications (linked list) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @ingroup snodeflags
+ * @defgroup spnodeflags Parsed schema nodes flags
+ *
+ * Various flags for parsed schema nodes (used as ::lysp_node.flags).
+ *
+ * 1 - container 6 - anydata/anyxml 11 - output 16 - grouping 21 - enum
+ * 2 - choice 7 - case 12 - feature 17 - uses 22 - type
+ * 3 - leaf 8 - notification 13 - identity 18 - refine 23 - stmt
+ * 4 - leaflist 9 - rpc 14 - extension 19 - augment
+ * 5 - list 10 - input 15 - typedef 20 - deviate
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2
+ * bit name 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 LYS_CONFIG_W |x|x|x|x|x|x| | | | | | | | | | | |x| |x| | | |
+ * LYS_SET_BASE | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 LYS_CONFIG_R |x|x|x|x|x|x| | | | | | | | | | | |x| |x| | | |
+ * LYS_SET_BIT | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 3 LYS_STATUS_CURR |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| | |
+ * LYS_SET_ENUM | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| | |
+ * LYS_SET_FRDIGITS | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| | |
+ * LYS_SET_LENGTH | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 6 LYS_MAND_TRUE | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
+ * LYS_SET_PATH | | | | | | | | | | | | | | | | | | | | | |x| |
+ * LYS_FENABLED | | | | | | | | | | | |x| | | | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 7 LYS_MAND_FALSE | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
+ * LYS_ORDBY_USER | | | |x|x| | | | | | | | | | | | | | | | | | |
+ * LYS_SET_PATTERN | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | | | | | | | | | |
+ * LYS_YINELEM_TRUE | | | | | | | | | | | | | |x| | | | | | | | | |
+ * LYS_SET_RANGE | | | | | | | | | | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 9 LYS_YINELEM_FALSE| | | | | | | | | | | | | |x| | | | | | | | | |
+ * LYS_SET_TYPE | | | | | | | | | | | | | | | | | | | | | |x| |
+ * LYS_SINGLEQUOTED | | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 10 LYS_SET_VALUE | | | | | | | | | | | | | | | | | | | | |x| | |
+ * LYS_SET_REQINST | | | | | | | | | | | | | | | | | | | | | |x| |
+ * LYS_SET_MIN | | | |x|x| | | | | | | | | | | | |x| |x| | | |
+ * LYS_DOUBLEQUOTED | | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 11 LYS_SET_MAX | | | |x|x| | | | | | | | | | | | |x| |x| | | |
+ * LYS_USED_GRP | | | | | | | | | | | | | | | |x| | | | | | | |
+ * LYS_YIN_ATTR | | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 12 LYS_YIN_ARGUMENT | | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 13 LYS_INTERNAL |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * @ingroup snodeflags
+ * @defgroup scnodeflags Compiled schema nodes flags
+ *
+ * Various flags for compiled schema nodes (used as ::lysc_node.flags).
+ *
+ * 1 - container 6 - anydata/anyxml 11 - identity
+ * 2 - choice 7 - case 12 - extension
+ * 3 - leaf 8 - notification 13 - bitenum
+ * 4 - leaflist 9 - rpc/action 14 - when
+ * 5 - list 10 - feature
+ *
+ * 1 1 1 1 1
+ * bit name 1 2 3 4 5 6 7 8 9 0 1 2 3 4
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 LYS_CONFIG_W |x|x|x|x|x|x|x| | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 LYS_CONFIG_R |x|x|x|x|x|x|x| | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 3 LYS_STATUS_CURR |x|x|x|x|x|x|x|x|x|x|x|x|x|x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x|x|x|x|x|x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x|x|x|x|x|x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 6 LYS_MAND_TRUE |x|x|x|x|x|x| | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 7 LYS_ORDBY_USER | | | |x|x| | | | | | | | | |
+ * LYS_MAND_FALSE | |x|x| | |x| | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
+ * LYS_PRESENCE |x| | | | | | | | | | | | | |
+ * LYS_UNIQUE | | |x| | | | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 9 LYS_KEY | | |x| | | | | | | | | | | |
+ * LYS_DISABLED | | | | | | | | | | | | |x| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 10 LYS_SET_DFLT | | |x|x| | |x| | | | | | | |
+ * LYS_IS_ENUM | | | | | | | | | | | | |x| |
+ * LYS_KEYLESS | | | | |x| | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 11 LYS_SET_UNITS | | |x|x| | | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 12 LYS_SET_CONFIG |x|x|x|x|x|x| | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 13 LYS_IS_INPUT |x|x|x|x|x|x|x| | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 14 LYS_IS_OUTPUT |x|x|x|x|x|x|x| | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 15 LYS_IS_NOTIF |x|x|x|x|x|x|x| | | | | | | |
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * @defgroup snodeflags Schema nodes flags
+ *
+ * Various flags for schema nodes ([parsed](@ref spnodeflags) as well as [compiled](@ref scnodeflags)).
+ *
+ * @{
+ */
+#define LYS_CONFIG_W 0x01 /**< config true; */
+#define LYS_CONFIG_R 0x02 /**< config false; */
+#define LYS_CONFIG_MASK 0x03 /**< mask for config value */
+#define LYS_STATUS_CURR 0x04 /**< status current; */
+#define LYS_STATUS_DEPRC 0x08 /**< status deprecated; */
+#define LYS_STATUS_OBSLT 0x10 /**< status obsolete; */
+#define LYS_STATUS_MASK 0x1C /**< mask for status value */
+#define LYS_MAND_TRUE 0x20 /**< mandatory true; applicable only to ::lysp_node_choice/::lysc_node_choice,
+ ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata.
+ The ::lysc_node_leaflist and ::lysc_node_leaflist have this flag in case that min-elements > 0.
+ The ::lysc_node_container has this flag if it is not a presence container and it has at least one
+ child with LYS_MAND_TRUE. */
+#define LYS_MAND_FALSE 0x40 /**< mandatory false; applicable only to ::lysp_node_choice/::lysc_node_choice,
+ ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata.
+ This flag is present only in case the mandatory false statement was explicitly specified. */
+#define LYS_MAND_MASK 0x60 /**< mask for mandatory values */
+#define LYS_PRESENCE 0x80 /**< flag for presence property of a container, but it is not only for explicit presence
+ containers, but also for NP containers with some meaning, applicable only to
+ ::lysc_node_container */
+#define LYS_UNIQUE 0x80 /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
+#define LYS_KEY 0x0100 /**< flag for leafs being a key of a list, applicable only to ::lysc_node_leaf */
+#define LYS_KEYLESS 0x0200 /**< flag for list without any key, applicable only to ::lysc_node_list */
+#define LYS_DISABLED 0x0100 /**< internal flag for a disabled statement, used only for bits/enums */
+#define LYS_FENABLED 0x20 /**< feature enabled flag, applicable only to ::lysp_feature. */
+#define LYS_ORDBY_SYSTEM 0x80 /**< ordered-by system configuration lists, applicable only to
+ ::lysc_node_leaflist/::lysp_node_leaflist and ::lysc_node_list/::lysp_node_list */
+#define LYS_ORDBY_USER 0x40 /**< ordered-by user configuration lists, applicable only to
+ ::lysc_node_leaflist/::lysp_node_leaflist and ::lysc_node_list/::lysp_node_list;
+ is always set for state leaf-lists, and key-less lists */
+#define LYS_ORDBY_MASK 0x60 /**< mask for ordered-by values */
+#define LYS_YINELEM_TRUE 0x80 /**< yin-element true for extension's argument */
+#define LYS_YINELEM_FALSE 0x0100 /**< yin-element false for extension's argument */
+#define LYS_YINELEM_MASK 0x0180 /**< mask for yin-element value */
+#define LYS_USED_GRP 0x0400 /**< internal flag for validating not-instantiated groupings
+ (resp. do not validate again the instantiated groupings). */
+#define LYS_SET_VALUE 0x0200 /**< value attribute is set */
+#define LYS_SET_MIN 0x0200 /**< min attribute is set */
+#define LYS_SET_MAX 0x0400 /**< max attribute is set */
+
+#define LYS_SET_BASE 0x0001 /**< type's flag for present base substatement */
+#define LYS_SET_BIT 0x0002 /**< type's flag for present bit substatement */
+#define LYS_SET_ENUM 0x0004 /**< type's flag for present enum substatement */
+#define LYS_SET_FRDIGITS 0x0008 /**< type's flag for present fraction-digits substatement */
+#define LYS_SET_LENGTH 0x0010 /**< type's flag for present length substatement */
+#define LYS_SET_PATH 0x0020 /**< type's flag for present path substatement */
+#define LYS_SET_PATTERN 0x0040 /**< type's flag for present pattern substatement */
+#define LYS_SET_RANGE 0x0080 /**< type's flag for present range substatement */
+#define LYS_SET_TYPE 0x0100 /**< type's flag for present type substatement */
+#define LYS_SET_REQINST 0x0200 /**< type's flag for present require-instance substatement */
+#define LYS_SET_DFLT 0x0200 /**< flag to mark leaf/leaflist with own (or refined) default value, not a default value taken from its type, and default
+ cases of choice. This information is important for refines, since it is prohibited to make leafs
+ with default statement mandatory. In case the default leaf value is taken from type, it is thrown
+ away when it is refined to be mandatory node. Similarly it is used for deviations to distinguish
+ between own default or the default values taken from the type. */
+#define LYS_SET_UNITS 0x0400 /**< flag to know if the leaf's/leaflist's units are their own (flag set) or it is taken from the type. */
+#define LYS_SET_CONFIG 0x0800 /**< flag to know if the config property was set explicitly (flag set) or it is inherited. */
+
+#define LYS_SINGLEQUOTED 0x0100 /**< flag for single-quoted argument of an extension instance's substatement, only when the source is YANG */
+#define LYS_DOUBLEQUOTED 0x0200 /**< flag for double-quoted argument of an extension instance's substatement, only when the source is YANG */
+
+#define LYS_YIN_ATTR 0x0400 /**< flag to identify YIN attribute parsed as extension's substatement, only when the source is YIN */
+#define LYS_YIN_ARGUMENT 0x0800 /**< flag to identify statement representing extension's argument, only when the source is YIN */
+
+#define LYS_INTERNAL 0x1000 /**< flag to identify internal parsed statements that should not be printed */
+
+#define LYS_IS_ENUM 0x0200 /**< flag to simply distinguish type in struct lysc_type_bitenum_item */
+
+#define LYS_IS_INPUT 0x1000 /**< flag for nodes that are in the subtree of an input statement */
+
+#define LYS_IS_OUTPUT 0x2000 /**< flag for nodes that are in the subtree of an output statement */
+
+#define LYS_IS_NOTIF 0x4000 /**< flag for nodes that are in the subtree of a notification statement */
+
+#define LYS_FLAGS_COMPILED_MASK 0xff /**< mask for flags that maps to the compiled structures */
+/** @} snodeflags */
+
+/**
+ * @brief Generic YANG data node
+ */
+struct lysp_node {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< [type of the node](@ref schemanodetypes) (mandatory) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)),
+ must be qname because of refines */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Extension structure of the lysp_node for YANG container
+ */
+struct lysp_node_container {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_CONTAINER */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* container */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_when *when; /**< when statement */
+ const char *presence; /**< presence description */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+ struct lysp_node_action *actions;/**< list of actions (linked list) */
+ struct lysp_node_notif *notifs; /**< list of notifications (linked list) */
+};
+
+struct lysp_node_leaf {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_LEAF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* leaf */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_when *when; /**< when statement */
+ struct lysp_type type; /**< type of the leaf node (mandatory) */
+ const char *units; /**< units of the leaf's type */
+ struct lysp_qname dflt; /**< default value, it may or may not be a qualified name */
+};
+
+struct lysp_node_leaflist {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_LEAFLIST */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* leaf-list */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_when *when; /**< when statement */
+ struct lysp_type type; /**< type of the leaf node (mandatory) */
+ const char *units; /**< units of the leaf's type */
+ struct lysp_qname *dflts; /**< list of default values ([sized array](@ref sizedarrays)), they may or
+ may not be qualified names */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint, 0 means unbounded */
+};
+
+struct lysp_node_list {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_LIST */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* list */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_when *when; /**< when statement */
+ const char *key; /**< keys specification */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+ struct lysp_node_action *actions;/**< list of actions (linked list) */
+ struct lysp_node_notif *notifs; /**< list of notifications (linked list) */
+ struct lysp_qname *uniques; /**< list of unique specifications ([sized array](@ref sizedarrays)) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint, 0 means unbounded */
+};
+
+struct lysp_node_choice {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_CHOICE */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* choice */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+ struct lysp_when *when; /**< when statement */
+ struct lysp_qname dflt; /**< default case */
+};
+
+struct lysp_node_case {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_CASE */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* case */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+ struct lysp_when *when; /**< when statement */
+};
+
+struct lysp_node_anydata {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_ANYXML or LYS_ANYDATA */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* anyxml/anydata */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_when *when; /**< when statement */
+};
+
+struct lysp_node_uses {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_USES */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ const char *name; /**< grouping name reference (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* uses */
+ struct lysp_refine *refines; /**< list of uses's refines ([sized array](@ref sizedarrays)) */
+ struct lysp_node_augment *augments; /**< list of augments (linked list) */
+ struct lysp_when *when; /**< when statement */
+};
+
+/**
+ * @brief YANG input-stmt and output-stmt
+ */
+struct lysp_node_action_inout {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_INPUT or LYS_OUTPUT */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node *next; /**< NULL */
+ const char *name; /**< empty string */
+ const char *dsc; /**< ALWAYS NULL, compatibility member with ::lysp_node */
+ const char *ref; /**< ALWAYS NULL, compatibility member with ::lysp_node */
+ struct lysp_qname *iffeatures; /**< ALWAYS NULL, compatibility member with ::lysp_node */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* inout */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+};
+
+/**
+ * @brief YANG rpc-stmt and action-stmt
+ */
+struct lysp_node_action {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_RPC or LYS_ACTION */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lysp_node_action *next; /**< pointer to the next action (NULL if there is no one) */
+ const char *name; /**< grouping name reference (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* action */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+
+ struct lysp_node_action_inout input; /**< RPC's/Action's input */
+ struct lysp_node_action_inout output; /**< RPC's/Action's output */
+};
+
+/**
+ * @brief YANG notification-stmt
+ */
+struct lysp_node_notif {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_NOTIF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
+ struct lysp_node_notif *next; /**< pointer to the next notification (NULL if there is no one) */
+ const char *name; /**< grouping name reference (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* notif */
+ struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+};
+
+/**
+ * @brief YANG grouping-stmt
+ */
+struct lysp_node_grp {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent;/**< parent node (NULL if this is a top-level grouping) */
+ uint16_t nodetype; /**< LYS_GROUPING */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
+ struct lysp_node_grp *next; /**< pointer to the next grouping (NULL if there is no one) */
+ const char *name; /**< grouping name (mandatory) */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< ALWAYS NULL, compatibility member with ::lysp_node */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ /* grp */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+ struct lysp_node_action *actions; /**< list of actions (linked list) */
+ struct lysp_node_notif *notifs; /**< list of notifications (linked list) */
+};
+
+/**
+ * @brief YANG uses-augment-stmt and augment-stmt (compatible with struct lysp_node )
+ */
+struct lysp_node_augment {
+ union {
+ struct lysp_node node; /**< implicit cast for the members compatible with ::lysp_node */
+
+ struct {
+ struct lysp_node *parent;/**< parent node (NULL if this is a top-level augment) */
+ uint16_t nodetype; /**< LYS_AUGMENT */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
+ struct lysp_node_augment *next; /**< pointer to the next augment (NULL if there is no one) */
+ const char *nodeid; /**< target schema nodeid (mandatory) - absolute for global augments, descendant for uses's augments */
+ const char *dsc; /**< description statement */
+ const char *ref; /**< reference statement */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ };
+ }; /**< common part corresponding to ::lysp_node */
+
+ struct lysp_node *child; /**< list of data nodes (linked list) */
+ struct lysp_when *when; /**< when statement */
+ struct lysp_node_action *actions;/**< list of actions (linked list) */
+ struct lysp_node_notif *notifs; /**< list of notifications (linked list) */
+};
+
+/**
+ * @brief supported YANG schema version values
+ */
+typedef enum LYS_VERSION {
+ LYS_VERSION_UNDEF = 0, /**< no specific version, YANG 1.0 as default */
+ LYS_VERSION_1_0 = 1, /**< YANG 1 (1.0) */
+ LYS_VERSION_1_1 = 2 /**< YANG 1.1 */
+} LYS_VERSION;
+
+/**
+ * @brief Printable YANG schema tree structure representing YANG module.
+ *
+ * Simple structure corresponding to the YANG format. The schema is only syntactically validated.
+ */
+struct lysp_module {
+ struct lys_module *mod; /**< covering module structure */
+
+ struct lysp_revision *revs; /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
+ in the list is always the last (newest) revision of the module */
+ struct lysp_import *imports; /**< list of imported modules ([sized array](@ref sizedarrays)) */
+ struct lysp_include *includes; /**< list of included submodules ([sized array](@ref sizedarrays)) */
+ struct lysp_ext *extensions; /**< list of extension statements ([sized array](@ref sizedarrays)) */
+ struct lysp_feature *features; /**< list of feature definitions ([sized array](@ref sizedarrays)) */
+ struct lysp_ident *identities; /**< list of identities ([sized array](@ref sizedarrays)) */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *data; /**< list of module's top-level data nodes (linked list) */
+ struct lysp_node_augment *augments; /**< list of augments (linked list) */
+ struct lysp_node_action *rpcs; /**< list of RPCs (linked list) */
+ struct lysp_node_notif *notifs; /**< list of notifications (linked list) */
+ struct lysp_deviation *deviations; /**< list of deviations ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+
+ uint8_t version; /**< yang-version (LYS_VERSION values) */
+ uint8_t parsing : 1; /**< flag for circular check */
+ uint8_t is_submod : 1; /**< always 0 */
+};
+
+struct lysp_submodule {
+ struct lys_module *mod; /**< belongs to parent module (submodule - mandatory) */
+
+ struct lysp_revision *revs; /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
+ in the list is always the last (newest) revision of the module */
+ struct lysp_import *imports; /**< list of imported modules ([sized array](@ref sizedarrays)) */
+ struct lysp_include *includes; /**< list of included submodules ([sized array](@ref sizedarrays)) */
+ struct lysp_ext *extensions; /**< list of extension statements ([sized array](@ref sizedarrays)) */
+ struct lysp_feature *features; /**< list of feature definitions ([sized array](@ref sizedarrays)) */
+ struct lysp_ident *identities; /**< list of identities ([sized array](@ref sizedarrays)) */
+ struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
+ struct lysp_node_grp *groupings; /**< list of groupings (linked list) */
+ struct lysp_node *data; /**< list of module's top-level data nodes (linked list) */
+ struct lysp_node_augment *augments; /**< list of augments (linked list) */
+ struct lysp_node_action *rpcs; /**< list of RPCs (linked list) */
+ struct lysp_node_notif *notifs; /**< list of notifications (linked list) */
+ struct lysp_deviation *deviations; /**< list of deviations ([sized array](@ref sizedarrays)) */
+ struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+
+ uint8_t version; /**< yang-version (LYS_VERSION values) */
+ uint8_t parsing : 1; /**< flag for circular check */
+ uint8_t is_submod : 1; /**< always 1 */
+
+ uint8_t latest_revision : 2; /**< flag to mark the latest available revision:
+ 1 - the latest revision in searchdirs was not searched yet and this is the
+ latest revision in the current context
+ 2 - searchdirs were searched and this is the latest available revision */
+ const char *name; /**< name of the module (mandatory) */
+ const char *filepath; /**< path, if the schema was read from a file, NULL in case of reading from memory */
+ const char *prefix; /**< submodule belongsto prefix of main module (mandatory) */
+ const char *org; /**< party/company responsible for the module */
+ const char *contact; /**< contact information for the module */
+ const char *dsc; /**< description of the module */
+ const char *ref; /**< cross-reference for the module */
+};
+
+/**
+ * @brief Get the parsed module or submodule name.
+ *
+ * @param[in] PMOD Parsed module or submodule.
+ * @return Module or submodule name.
+ */
+#define LYSP_MODULE_NAME(PMOD) (PMOD->is_submod ? ((struct lysp_submodule *)PMOD)->name : ((struct lysp_module *)PMOD)->mod->name)
+
+/**
+ * @brief Compiled prefix data pair mapping of prefixes to modules. In case the format is ::LY_VALUE_SCHEMA_RESOLVED,
+ * the expected prefix data is a sized array of these structures.
+ */
+struct lysc_prefix {
+ char *prefix; /**< used prefix */
+ const struct lys_module *mod; /**< mapping to a module */
+};
+
+/**
+ * @brief Compiled YANG extension-stmt
+ *
+ * Note that the compiled extension definition is created only in case the extension is instantiated. It is not available
+ * from the compiled schema, but from the parsed extension definition which is being searched when an extension instance
+ * is being compiled.
+ */
+struct lysc_ext {
+ const char *name; /**< extension name */
+ const char *argname; /**< argument name, NULL if not specified */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_ext *plugin; /**< Plugin implementing the specific extension */
+ struct lys_module *module; /**< module structure */
+ uint32_t refcount; /**< unused, always 1 */
+ uint16_t flags; /**< LYS_STATUS_* value (@ref snodeflags) */
+};
+
+/**
+ * @brief YANG when-stmt
+ */
+struct lysc_when {
+ struct lyxp_expr *cond; /**< XPath when condition */
+ struct lysc_node *context; /**< context node for evaluating the expression, NULL if the context is root node */
+ struct lysc_prefix *prefixes; /**< compiled used prefixes in the condition */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint32_t refcount; /**< reference counter since some of the when statements are shared among several nodes */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS is allowed */
+};
+
+/**
+ * @brief YANG identity-stmt
+ */
+struct lysc_ident {
+ const char *name; /**< identity name (mandatory, no prefix) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lys_module *module; /**< module structure */
+ struct lysc_ident **derived; /**< list of (pointers to the) derived identities ([sized array](@ref sizedarrays))
+ It also contains references to identities located in unimplemented modules. */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
+};
+
+/**
+ * @defgroup ifftokens if-feature expression tokens
+ * Tokens of if-feature expression used in ::lysc_iffeature.expr.
+ *
+ * @{
+ */
+#define LYS_IFF_NOT 0x00 /**< operand "not" */
+#define LYS_IFF_AND 0x01 /**< operand "and" */
+#define LYS_IFF_OR 0x02 /**< operand "or" */
+#define LYS_IFF_F 0x03 /**< feature */
+/** @} ifftokens */
+
+/**
+ * @brief Compiled YANG revision statement
+ */
+struct lysc_revision {
+ char date[LY_REV_SIZE]; /**< revision-date (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_range {
+ struct lysc_range_part {
+ union { /**< min boundary */
+ int64_t min_64; /**< for int8, int16, int32, int64 and decimal64 ( >= LY_TYPE_DEC64) */
+ uint64_t min_u64; /**< for uint8, uint16, uint32, uint64, string and binary ( < LY_TYPE_DEC64) */
+ };
+ union { /**< max boundary */
+ int64_t max_64; /**< for int8, int16, int32, int64 and decimal64 ( >= LY_TYPE_DEC64) */
+ uint64_t max_u64; /**< for uint8, uint16, uint32, uint64, string and binary ( < LY_TYPE_DEC64) */
+ };
+ } *parts; /**< compiled range expression ([sized array](@ref sizedarrays)) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_pattern {
+ const char *expr; /**< original, not compiled, regular expression */
+ pcre2_code *code; /**< compiled regular expression */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint32_t inverted : 1; /**< invert-match flag */
+ uint32_t refcount : 31; /**< reference counter */
+};
+
+struct lysc_must {
+ struct lyxp_expr *cond; /**< XPath when condition */
+ struct lysc_prefix *prefixes; /**< compiled used prefixes in the condition */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_type {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing, it may be accessed concurrently when
+ creating/freeing data node values that reference it (instance-identifier) */
+};
+
+struct lysc_type_num {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_range *range; /**< Optional range limitation */
+};
+
+struct lysc_type_dec {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ uint8_t fraction_digits; /**< fraction digits specification */
+ struct lysc_range *range; /**< Optional range limitation */
+};
+
+struct lysc_type_str {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_range *length; /**< Optional length limitation */
+ struct lysc_pattern **patterns; /**< Optional list of pointers to pattern limitations ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_type_bitenum_item {
+ const char *name; /**< enumeration identifier */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+
+ union {
+ int32_t value; /**< integer value associated with the enumeration */
+ uint32_t position; /**< non-negative integer value associated with the bit */
+ };
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ and LYS_IS_ENUM values
+ are allowed */
+};
+
+struct lysc_type_enum {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_type_bitenum_item *enums; /**< enumerations list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_bits {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_type_bitenum_item *bits; /**< bits list ([sized array](@ref sizedarrays)), mandatory (at least 1 item),
+ the items are ordered by their position value. */
+};
+
+struct lysc_type_leafref {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lyxp_expr *path; /**< parsed target path, compiled path cannot be stored because of type sharing */
+ struct lysc_prefix *prefixes; /**< resolved prefixes used in the path */
+ const struct lys_module *cur_mod;/**< unused, not needed */
+ struct lysc_type *realtype; /**< pointer to the real (first non-leafref in possible leafrefs chain) type. */
+ uint8_t require_instance; /**< require-instance flag */
+};
+
+struct lysc_type_identityref {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_ident **bases; /**< list of pointers to the base identities ([sized array](@ref sizedarrays)),
+ mandatory (at least 1 item) */
+};
+
+struct lysc_type_instanceid {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ uint8_t require_instance; /**< require-instance flag */
+};
+
+struct lysc_type_union {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_type **types; /**< list of types in the union ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_bin {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyplg_type *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_range *length; /**< Optional length limitation */
+};
+
+/**
+ * @brief Maximum number of hashes stored in a schema node.
+ */
+#define LYS_NODE_HASH_COUNT 4
+
+/**
+ * @brief Compiled YANG data node
+ */
+struct lysc_node {
+ uint16_t nodetype; /**< [type of the node](@ref schemanodetypes) (mandatory) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+};
+
+struct lysc_node_action_inout {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_INPUT or LYS_OUTPUT */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent;/**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (output node for input, NULL for output) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node (input and output node pointing to each other) */
+ const char *name; /**< "input" or "output" */
+ const char *dsc; /**< ALWAYS NULL, compatibility member with ::lysc_node */
+ const char *ref; /**< ALWAYS NULL, compatibility member with ::lysc_node */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /** private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_node_action {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_RPC or LYS_ACTION */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node - RPC) */
+ struct lysc_node_action *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node_action *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< action/RPC name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /** private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)),
+ the action/RPC nodes do not contain the when statement on their own, but they can
+ inherit it from the parent's uses. */
+ struct lysc_node_action_inout input; /**< RPC's/action's input */
+ struct lysc_node_action_inout output; /**< RPC's/action's output */
+
+};
+
+struct lysc_node_notif {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_NOTIF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node_notif *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node_notif *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< Notification name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /** private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)),
+ the notification nodes do not contain the when statement on their own, but they can
+ inherit it from the parent's uses. */
+};
+
+struct lysc_node_container {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_CONTAINER */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+ struct lysc_node_action *actions;/**< first of actions nodes (linked list) */
+ struct lysc_node_notif *notifs; /**< first of notifications nodes (linked list) */
+};
+
+struct lysc_node_case {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_CASE */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser, unused */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< name of the case, including the implicit case */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_node *child; /**< first child node of the case (linked list). Note that all the children of all the sibling cases are linked
+ each other as siblings with the parent pointer pointing to appropriate case node. */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_node_choice {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_CHOICE */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser, unused */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_node_case *cases; /**< list of the cases (linked list). Note that all the children of all the cases are linked each other
+ as siblings. Their parent pointers points to the specific case they belongs to, so distinguish the
+ case is simple. */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+ struct lysc_node_case *dflt; /**< default case of the choice, only a pointer into the cases array. */
+};
+
+struct lysc_node_leaf {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_LEAF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+ struct lysc_type *type; /**< type of the leaf node (mandatory) */
+
+ const char *units; /**< units of the leaf's type */
+ struct lyd_value *dflt; /**< default value, use ::lyd_value_get_canonical() to get the canonical string */
+};
+
+struct lysc_node_leaflist {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_LEAFLIST */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+ struct lysc_type *type; /**< type of the leaf node (mandatory) */
+
+ const char *units; /**< units of the leaf's type */
+ struct lyd_value **dflts; /**< list ([sized array](@ref sizedarrays)) of default values, use
+ ::lyd_value_get_canonical() to get the canonical strings */
+
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint */
+
+};
+
+struct lysc_node_list {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_LIST */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+ struct lysc_node_action *actions;/**< first of actions nodes (linked list) */
+ struct lysc_node_notif *notifs; /**< first of notifications nodes (linked list) */
+
+ struct lysc_node_leaf ***uniques;/**< list of sized arrays of pointers to the unique nodes ([sized array](@ref sizedarrays)) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint */
+};
+
+struct lysc_node_anydata {
+ union {
+ struct lysc_node node; /**< implicit cast for the members compatible with ::lysc_node */
+
+ struct {
+ uint16_t nodetype; /**< LYS_ANYXML or LYS_ANYDATA */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
+ struct lys_module *module; /**< module structure */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ const char *dsc; /**< description */
+ const char *ref; /**< reference */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private arbitrary user data, not used by libyang unless ::LY_CTX_SET_PRIV_PARSED is set */
+ };
+ };
+
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Compiled YANG schema tree structure representing YANG module.
+ *
+ * Semantically validated YANG schema tree for data tree parsing.
+ * Contains only the necessary information for the data validation.
+ */
+struct lysc_module {
+ struct lys_module *mod; /**< covering module structure */
+
+ struct lysc_node *data; /**< list of module's top-level data nodes (linked list) */
+ struct lysc_node_action *rpcs; /**< first of actions nodes (linked list) */
+ struct lysc_node_notif *notifs; /**< first of notifications nodes (linked list) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Examine whether a node is user-ordered list or leaf-list.
+ *
+ * @param[in] lysc_node Schema node to examine.
+ * @return Boolean value whether the @p node is user-ordered or not.
+ */
+#define lysc_is_userordered(lysc_node) \
+ ((!lysc_node || !(lysc_node->nodetype & (LYS_LEAFLIST | LYS_LIST)) || !(lysc_node->flags & LYS_ORDBY_USER)) ? 0 : 1)
+
+/**
+ * @brief Examine whether a node is a list's key.
+ *
+ * @param[in] lysc_node Schema node to examine.
+ * @return Boolean value whether the @p node is a key or not.
+ */
+#define lysc_is_key(lysc_node) \
+ ((!lysc_node || (lysc_node->nodetype != LYS_LEAF) || !(lysc_node->flags & LYS_KEY)) ? 0 : 1)
+
+/**
+ * @brief Examine whether a node is a non-presence container.
+ *
+ * @param[in] lysc_node Schema node to examine.
+ * @return Boolean value whether the @p node is a NP container or not.
+ */
+#define lysc_is_np_cont(lysc_node) \
+ ((!lysc_node || (lysc_node->nodetype != LYS_CONTAINER) || (lysc_node->flags & LYS_PRESENCE)) ? 0 : 1)
+
+/**
+ * @brief Examine whether a node is a key-less list or a non-configuration leaf-list.
+ *
+ * @param[in] lysc_node Schema node to examine.
+ * @return Boolean value whether the @p node is a list with duplicate instances allowed.
+ */
+#define lysc_is_dup_inst_list(lysc_node) \
+ ((lysc_node && (((lysc_node->nodetype == LYS_LIST) && (lysc_node->flags & LYS_KEYLESS)) || \
+ ((lysc_node->nodetype == LYS_LEAFLIST) && !(lysc_node->flags & LYS_CONFIG_W)))) ? 1 : 0)
+
+/**
+ * @brief Get nearest @p schema parent (including the node itself) that can be instantiated in data.
+ *
+ * @param[in] schema Schema node to get the nearest data node for.
+ * @return Schema data node, NULL if top-level (in data).
+ */
+LIBYANG_API_DECL const struct lysc_node *lysc_data_node(const struct lysc_node *schema);
+
+/**
+ * @brief Same as ::lysc_data_node() but never returns the node itself.
+ */
+#define lysc_data_parent(SCHEMA) lysc_data_node((SCHEMA) ? (SCHEMA)->parent : NULL)
+
+/**
+ * @brief Check whether the schema node data instance existence depends on any when conditions.
+ * This node and any direct parent choice and case schema nodes are also examined for when conditions.
+ *
+ * Be careful, this function is not recursive and checks only conditions that apply to this node directly.
+ * Meaning if there are any conditions associated with any data parent instance of @p node, they are not returned.
+ *
+ * @param[in] node Schema node to examine.
+ * @return When condition associated with the node data instance, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysc_when *lysc_has_when(const struct lysc_node *node);
+
+/**
+ * @brief Get the owner module of the schema node. It is the module of the top-level node. Generally,
+ * in case of augments it is the target module, recursively, otherwise it is the module where the node is defined.
+ *
+ * @param[in] node Schema node to examine.
+ * @return Module owner of the node.
+ */
+LIBYANG_API_DECL const struct lys_module *lysc_owner_module(const struct lysc_node *node);
+
+/**
+ * @brief Get the groupings linked list of the given (parsed) schema node.
+ * Decides the node's type and in case it has a groupings array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's groupings linked list if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysp_node_grp *lysp_node_groupings(const struct lysp_node *node);
+
+/**
+ * @brief Get the typedefs sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a typedefs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's typedefs sized array if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysp_tpdf *lysp_node_typedefs(const struct lysp_node *node);
+
+/**
+ * @brief Get the actions/RPCs linked list of the given (parsed) schema node.
+ * Decides the node's type and in case it has a actions/RPCs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's actions/RPCs linked list if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysp_node_action *lysp_node_actions(const struct lysp_node *node);
+
+/**
+ * @brief Get the Notifications linked list of the given (parsed) schema node.
+ * Decides the node's type and in case it has a Notifications array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's Notifications linked list if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysp_node_notif *lysp_node_notifs(const struct lysp_node *node);
+
+/**
+ * @brief Get the children linked list of the given (parsed) schema node.
+ * Decides the node's type and in case it has a children list, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's children linked list if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysp_node *lysp_node_child(const struct lysp_node *node);
+
+/**
+ * @brief Get the actions/RPCs linked list of the given (compiled) schema node.
+ * Decides the node's type and in case it has a actions/RPCs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's actions/RPCs linked list if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysc_node_action *lysc_node_actions(const struct lysc_node *node);
+
+/**
+ * @brief Get the Notifications linked list of the given (compiled) schema node.
+ * Decides the node's type and in case it has a Notifications array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's Notifications linked list if any, NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysc_node_notif *lysc_node_notifs(const struct lysc_node *node);
+
+/**
+ * @brief Get the children linked list of the given (compiled) schema node.
+ *
+ * Note that ::LYS_CHOICE has only ::LYS_CASE children.
+ * Also, ::LYS_RPC and ::LYS_ACTION have the first child ::LYS_INPUT, its sibling is ::LYS_OUTPUT.
+ *
+ * @param[in] node Node to examine.
+ * @return Children linked list if any,
+ * @return NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lysc_node *lysc_node_child(const struct lysc_node *node);
+
+/**
+ * @brief Get the must statements list if present in the @p node
+ *
+ * @param[in] node Node to examine.
+ * @return Pointer to the list of must restrictions ([sized array](@ref sizedarrays))
+ * @return NULL if there is no must statement in the node, no matter if it is not even allowed or just present
+ */
+LIBYANG_API_DECL struct lysc_must *lysc_node_musts(const struct lysc_node *node);
+
+/**
+ * @brief Get the when statements list if present in the @p node
+ *
+ * @param[in] node Node to examine.
+ * @return Pointer to the list of pointers to when statements ([sized array](@ref sizedarrays))
+ * @return NULL if there is no when statement in the node, no matter if it is not even allowed or just present
+ */
+LIBYANG_API_DECL struct lysc_when **lysc_node_when(const struct lysc_node *node);
+
+/**
+ * @brief Callback to be called for every schema node in a DFS traversal.
+ *
+ * @param[in] node Current node.
+ * @param[in] data Arbitrary user data.
+ * @param[out] dfs_continue Set to true if the current subtree should be skipped and continue with siblings instead.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value to terminate DFS and return this value.
+ */
+typedef LY_ERR (*lysc_dfs_clb)(struct lysc_node *node, void *data, ly_bool *dfs_continue);
+
+/**
+ * @brief DFS traversal of all the schema nodes in a (sub)tree including any actions and nested notifications.
+ *
+ * Node with children, actions, and notifications is traversed in this order:
+ * 1) each child subtree;
+ * 2) each action subtree;
+ * 3) each notification subtree.
+ *
+ * For algorithm illustration or traversal with actions and notifications skipped, see ::LYSC_TREE_DFS_BEGIN.
+ *
+ * @param[in] root Schema root to fully traverse.
+ * @param[in] dfs_clb Callback to call for each node.
+ * @param[in] data Arbitrary user data passed to @p dfs_clb.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value returned by @p dfs_clb.
+ */
+LIBYANG_API_DECL LY_ERR lysc_tree_dfs_full(const struct lysc_node *root, lysc_dfs_clb dfs_clb, void *data);
+
+/**
+ * @brief DFS traversal of all the schema nodes in a module including RPCs and notifications.
+ *
+ * For more details, see ::lysc_tree_dfs_full().
+ *
+ * @param[in] mod Module to fully traverse.
+ * @param[in] dfs_clb Callback to call for each node.
+ * @param[in] data Arbitrary user data passed to @p dfs_clb.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value returned by @p dfs_clb.
+ */
+LIBYANG_API_DECL LY_ERR lysc_module_dfs_full(const struct lys_module *mod, lysc_dfs_clb dfs_clb, void *data);
+
+/**
+ * @brief Get how the if-feature statement currently evaluates.
+ *
+ * @param[in] iff Compiled if-feature statement to evaluate.
+ * @return LY_SUCCESS if the statement evaluates to true,
+ * @return LY_ENOT if it evaluates to false,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lysc_iffeature_value(const struct lysc_iffeature *iff);
+
+/**
+ * @brief Get how the if-feature statement is evaluated for certain identity.
+ *
+ * The function can be called even if the identity does not contain
+ * if-features, in which case ::LY_SUCCESS is returned.
+ *
+ * @param[in] ident Compiled identity statement to evaluate.
+ * @return LY_SUCCESS if the statement evaluates to true,
+ * @return LY_ENOT if it evaluates to false,
+ * @return LY_ERR on error.
+ */
+LIBYANG_API_DECL LY_ERR lys_identity_iffeature_value(const struct lysc_ident *ident);
+
+/**
+ * @brief Get the next feature in the module or submodules.
+ *
+ * @param[in] last Last returned feature.
+ * @param[in] pmod Parsed module and submodules whose features to iterate over.
+ * @param[in,out] idx Submodule index, set to 0 on first call.
+ * @return Next found feature, NULL if the last has already been returned.
+ */
+LIBYANG_API_DECL struct lysp_feature *lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod,
+ uint32_t *idx);
+
+/**
+ * @defgroup findxpathoptions Atomize XPath options
+ * Options to modify behavior of ::lys_find_xpath() and ::lys_find_xpath_atoms() searching for schema nodes in schema tree.
+ * @{
+ */
+#define LYS_FIND_XP_SCHEMA 0x08 /**< Apply node access restrictions defined for 'when' and 'must' evaluation. */
+#define LYS_FIND_XP_OUTPUT 0x10 /**< Search RPC/action output nodes instead of input ones. */
+#define LYS_FIND_NO_MATCH_ERROR 0x40 /**< Return error if a path segment matches no nodes, otherwise only warning
+ is printed. */
+/** @} findxpathoptions */
+
+/**
+ * @brief Get all the schema nodes that are required for @p xpath to be evaluated (atoms).
+ *
+ * @param[in] ctx libyang context to use. May be NULL if @p ctx_node is set.
+ * @param[in] ctx_node XPath schema context node. Use NULL for the root node.
+ * @param[in] xpath Data XPath expression filtering the matching nodes. ::LY_VALUE_JSON prefix format is expected.
+ * @param[in] options Whether to apply some node access restrictions, see @ref findxpathoptions.
+ * @param[out] set Set of found atoms (schema nodes).
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *xpath,
+ uint32_t options, struct ly_set **set);
+
+/**
+ * @brief Get all the schema nodes that are required for @p expr to be evaluated (atoms).
+ *
+ * @param[in] ctx_node XPath schema context node. Use NULL for the root node.
+ * @param[in] cur_mod Current module for the expression (where it was "instantiated").
+ * @param[in] expr Parsed expression to use.
+ * @param[in] prefixes Sized array of compiled prefixes.
+ * @param[in] options Whether to apply some node access restrictions, see @ref findxpathoptions.
+ * @param[out] set Set of found atoms (schema nodes).
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lys_find_expr_atoms(const struct lysc_node *ctx_node, const struct lys_module *cur_mod,
+ const struct lyxp_expr *expr, const struct lysc_prefix *prefixes, uint32_t options, struct ly_set **set);
+
+/**
+ * @brief Evaluate an @p xpath expression on schema nodes.
+ *
+ * @param[in] ctx libyang context to use for absolute @p xpath. May be NULL if @p ctx_node is set.
+ * @param[in] ctx_node XPath schema context node for relative @p xpath. Use NULL for the root node.
+ * @param[in] xpath Data XPath expression filtering the matching nodes. ::LY_VALUE_JSON prefix format is expected.
+ * @param[in] options Whether to apply some node access restrictions, see @ref findxpathoptions.
+ * @param[out] set Set of found schema nodes.
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LIBYANG_API_DECL LY_ERR lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *xpath,
+ uint32_t options, struct ly_set **set);
+
+/**
+ * @brief Get all the schema nodes that are required for @p path to be evaluated (atoms).
+ *
+ * @param[in] path Compiled path to use.
+ * @param[out] set Set of found atoms (schema nodes).
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lys_find_lypath_atoms(const struct ly_path *path, struct ly_set **set);
+
+/**
+ * @brief Get all the schema nodes that are required for @p path to be evaluated (atoms).
+ *
+ * @param[in] ctx libyang context to use for absolute @p path. May be NULL if @p ctx_node is set.
+ * @param[in] ctx_node XPath schema context node for relative @p path. Use NULL for the root node.
+ * @param[in] path JSON path to examine.
+ * @param[in] output Search operation output instead of input.
+ * @param[out] set Set of found atoms (schema nodes).
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lys_find_path_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path,
+ ly_bool output, struct ly_set **set);
+
+/**
+ * @brief Get a schema node based on the given data path (JSON format, see @ref howtoXPath).
+ *
+ * @param[in] ctx libyang context to use for absolute @p path. May be NULL if @p ctx_node is set.
+ * @param[in] ctx_node XPath schema context node for relative @p path. Use NULL for the root node.
+ * @param[in] path JSON path of the node to get.
+ * @param[in] output Search operation output instead of input.
+ * @return Found schema node or NULL.
+ */
+LIBYANG_API_DECL const struct lysc_node *lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
+ const char *path, ly_bool output);
+
+/**
+ * @brief Types of the different schema paths.
+ */
+typedef enum {
+ LYSC_PATH_LOG, /**< Descriptive path format used in log messages */
+ LYSC_PATH_DATA, /**< Similar to ::LYSC_PATH_LOG except that schema-only nodes (choice, case) are skipped */
+ LYSC_PATH_DATA_PATTERN /**< Similar to ::LYSC_PATH_DATA but there are predicates for all list keys added with
+ "%s" where their values should be so that they can be printed there */
+} LYSC_PATH_TYPE;
+
+/**
+ * @brief Generate path of the given node in the requested format.
+ *
+ * @param[in] node Schema path of this node will be generated.
+ * @param[in] pathtype Format of the path to generate.
+ * @param[in,out] buffer Prepared buffer of the @p buflen length to store the generated path.
+ * If NULL, memory for the complete path is allocated.
+ * @param[in] buflen Size of the provided @p buffer.
+ * @return NULL in case of memory allocation error, path of the node otherwise.
+ * In case the @p buffer is NULL, the returned string is dynamically allocated and caller is responsible to free it.
+ */
+LIBYANG_API_DECL char *lysc_path(const struct lysc_node *node, LYSC_PATH_TYPE pathtype, char *buffer, size_t buflen);
+
+/**
+ * @brief Available YANG schema tree structures representing YANG module.
+ */
+struct lys_module {
+ struct ly_ctx *ctx; /**< libyang context of the module (mandatory) */
+ const char *name; /**< name of the module (mandatory) */
+ const char *revision; /**< revision of the module (if present) */
+ const char *ns; /**< namespace of the module (module - mandatory) */
+ const char *prefix; /**< module prefix or submodule belongsto prefix of main module (mandatory) */
+ const char *filepath; /**< path, if the schema was read from a file, NULL in case of reading from memory */
+ const char *org; /**< party/company responsible for the module */
+ const char *contact; /**< contact information for the module */
+ const char *dsc; /**< description of the module */
+ const char *ref; /**< cross-reference for the module */
+
+ struct lysp_module *parsed; /**< Simply parsed (unresolved) YANG schema tree */
+ struct lysc_module *compiled; /**< Compiled and fully validated YANG schema tree for data parsing.
+ Available only for implemented modules. */
+
+ struct lysc_ident *identities; /**< List of compiled identities of the module ([sized array](@ref sizedarrays))
+ also contains the disabled identities when their if-feature(s) are evaluated to \"false\",
+ and also the list is filled even if the module is not implemented.
+ The list is located here because it avoids problems when the module became implemented in
+ future (no matter if implicitly via augment/deviate or explicitly via
+ ::lys_set_implemented()). Note that if the module is not implemented (compiled), the
+ identities cannot be instantiated in data (in identityrefs). */
+
+ struct lys_module **augmented_by;/**< List of modules that augment this module ([sized array](@ref sizedarrays)) */
+ struct lys_module **deviated_by; /**< List of modules that deviate this module ([sized array](@ref sizedarrays)) */
+
+ ly_bool implemented; /**< flag if the module is implemented, not just imported */
+ ly_bool to_compile; /**< flag marking a module that was changed but not (re)compiled, see
+ ::LY_CTX_EXPLICIT_COMPILE. */
+ uint8_t latest_revision; /**< Flag to mark the latest available revision, see [latest_revision options](@ref latestrevflags). */
+};
+
+/**
+ * @defgroup latestrevflags Options for ::lys_module.latest_revision.
+ *
+ * Various information bits of ::lys_module.latest_revision.
+ *
+ * @{
+ */
+#define LYS_MOD_LATEST_REV 0x01 /**< This is the latest revision of the module in the current context. */
+#define LYS_MOD_LATEST_SEARCHDIRS 0x02 /**< This is the latest revision of the module found in searchdirs. */
+#define LYS_MOD_IMPORTED_REV 0x04 /**< This is the module revision used when importing the module without
+ an explicit revision-date. It is used for all such imports regardless of
+ any changes made in the context. */
+#define LYS_MOD_LATEST_IMPCLB 0x08 /**< This is the latest revision of the module obtained from import callback. */
+/** @} latestrevflags */
+
+/**
+ * @brief Get the current real status of the specified feature in the module.
+ *
+ * If the feature is enabled, but some of its if-features are false, the feature is considered
+ * disabled.
+ *
+ * @param[in] module Module where the feature is defined.
+ * @param[in] feature Name of the feature to inspect.
+ * @return LY_SUCCESS if the feature is enabled,
+ * @return LY_ENOT if the feature is disabled,
+ * @return LY_ENOTFOUND if the feature was not found.
+ */
+LIBYANG_API_DECL LY_ERR lys_feature_value(const struct lys_module *module, const char *feature);
+
+/**
+ * @brief Get next schema (sibling) node element in the schema order that can be instantiated in a data tree.
+ * Returned node may be from an augment.
+ *
+ * ::lys_getnext() is supposed to be called sequentially. In the first call, the @p last parameter is usually NULL
+ * and function starts returning 1) the first @p parent child (if it is set) or 2) the first top level element of
+ * @p module. Consequent calls should provide the previously returned node as @p last and the same @p parent and
+ * @p module parameters.
+ *
+ * Without options, the function is used to traverse only the schema nodes that can be paired with corresponding
+ * data nodes in a data tree. By setting some @p options the behavior can be modified to the extent that
+ * all the schema nodes are iteratively returned.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree to iterate over. If set, @p module is ignored.
+ * @param[in] module Module of the top level elements to iterate over. If @p parent is NULL, it must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node, NULL in case there are no more.
+ */
+LIBYANG_API_DECL const struct lysc_node *lys_getnext(const struct lysc_node *last, const struct lysc_node *parent,
+ const struct lysc_module *module, uint32_t options);
+
+/**
+ * @brief Get next schema (sibling) node element in the schema order of an extension that can be instantiated in
+ * a data tree.
+ *
+ * It is just ::lys_getnext() for extensions.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree to iterate over. If set, @p ext is ignored.
+ * @param[in] ext Extension instance with schema nodes to iterate over. If @p parent is NULL, it must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node, NULL in case there are no more.
+ */
+LIBYANG_API_DECL const struct lysc_node *lys_getnext_ext(const struct lysc_node *last, const struct lysc_node *parent,
+ const struct lysc_ext_instance *ext, uint32_t options);
+
+/**
+ * @defgroup sgetnextflags Options for ::lys_getnext() and ::lys_getnext_ext().
+ *
+ * Various options setting behavior of ::lys_getnext() and ::lys_getnext_ext().
+ *
+ * @{
+ */
+#define LYS_GETNEXT_WITHCHOICE 0x01 /**< ::lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
+#define LYS_GETNEXT_NOCHOICE 0x02 /**< ::lys_getnext() option to ignore (kind of conditional) nodes within choice node */
+#define LYS_GETNEXT_WITHCASE 0x04 /**< ::lys_getnext() option to allow returning #LYS_CASE nodes instead of looking into them */
+#define LYS_GETNEXT_INTONPCONT 0x08 /**< ::lys_getnext() option to look into non-presence container, instead of returning container itself */
+#define LYS_GETNEXT_OUTPUT 0x10 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
+ provided by default */
+/** @} sgetnextflags */
+
+/**
+ * @brief Get child node according to the specified criteria.
+ *
+ * @param[in] parent Optional parent of the node to find. If not specified, the module's top-level nodes are searched.
+ * @param[in] module module of the node to find. It is also limitation for the children node of the given parent.
+ * @param[in] name Name of the node to find.
+ * @param[in] name_len Optional length of the name in case it is not NULL-terminated string.
+ * @param[in] nodetype Optional criteria (to speedup) specifying nodetype(s) of the node to find.
+ * Used as a bitmask, so multiple nodetypes can be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Found node if any.
+ */
+LIBYANG_API_DECL const struct lysc_node *lys_find_child(const struct lysc_node *parent, const struct lys_module *module,
+ const char *name, size_t name_len, uint16_t nodetype, uint32_t options);
+
+/**
+ * @brief Make the specific module implemented.
+ *
+ * If the module is already implemented but with a different set of features, the whole context is recompiled.
+ *
+ * @param[in] mod Module to make implemented. It is not an error
+ * to provide already implemented module, it just does nothing.
+ * @param[in] features Optional array specifying the enabled features terminated with NULL overriding any previous
+ * feature setting. The feature string '*' enables all the features and array of length 1 with only the terminating
+ * NULL explicitly disables all the features. In case the parameter is NULL, the features are untouched - left disabled
+ * in a newly implemented module or with the current features settings in case the module is already implemented.
+ * @return LY_SUCCESS on success.
+ * @return LY_EDENIED in case the context contains some other revision of the same module which is already implemented.
+ * @return LY_ERR on other errors during module compilation.
+ */
+LIBYANG_API_DECL LY_ERR lys_set_implemented(struct lys_module *mod, const char **features);
+
+/**
+ * @brief Stringify schema nodetype.
+ *
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+LIBYANG_API_DECL const char *lys_nodetype2str(uint16_t nodetype);
+
+/**
+ * @brief Getter for original XPath expression from a parsed expression.
+ *
+ * @param[in] path Parsed expression.
+ * @return Original string expression.
+ */
+LIBYANG_API_DECL const char *lyxp_get_expr(const struct lyxp_expr *path);
+
+/** @} schematree */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_SCHEMA_H_ */
diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c
new file mode 100644
index 0000000..bbdd676
--- /dev/null
+++ b/src/tree_schema_common.c
@@ -0,0 +1,2617 @@
+/**
+ * @file tree_schema_common.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Parsing and validation common functions for schema trees
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "hash_table.h"
+#include "in.h"
+#include "in_internal.h"
+#include "log.h"
+#include "parser_schema.h"
+#include "schema_compile.h"
+#include "schema_features.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+LY_ERR
+lysp_check_prefix(struct lysp_ctx *ctx, struct lysp_import *imports, const char *module_prefix, const char **value)
+{
+ struct lysp_import *i;
+
+ if (module_prefix && (&module_prefix != value) && !strcmp(module_prefix, *value)) {
+ LOGVAL_PARSER(ctx, LYVE_REFERENCE, "Prefix \"%s\" already used as module prefix.", *value);
+ return LY_EEXIST;
+ }
+ LY_ARRAY_FOR(imports, struct lysp_import, i) {
+ if (i->prefix && (&i->prefix != value) && !strcmp(i->prefix, *value)) {
+ LOGVAL_PARSER(ctx, LYVE_REFERENCE, "Prefix \"%s\" already used to import \"%s\" module.", *value, i->name);
+ return LY_EEXIST;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_date(struct lysp_ctx *ctx, const char *date, size_t date_len, const char *stmt)
+{
+ struct tm tm, tm_;
+ char *r;
+
+ LY_CHECK_ARG_RET(PARSER_CTX(ctx), date, LY_EINVAL);
+
+ if (date_len != LY_REV_SIZE - 1) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid length %" PRIu32 " of a date.", (uint32_t)date_len);
+ return LY_EINVAL;
+ }
+
+ /* check format: YYYY-MM-DD */
+ for (uint8_t i = 0; i < date_len; i++) {
+ if ((i == 4) || (i == 7)) {
+ if (date[i] != '-') {
+ goto error;
+ }
+ } else if (!isdigit(date[i])) {
+ goto error;
+ }
+ }
+
+ /* check content, e.g. 2018-02-31 */
+ memset(&tm, 0, sizeof tm);
+ r = strptime(date, "%Y-%m-%d", &tm);
+ if (!r || (r != &date[LY_REV_SIZE - 1])) {
+ goto error;
+ }
+ memcpy(&tm_, &tm, sizeof tm);
+
+ /* DST may move the hour back resulting in a different day */
+ tm_.tm_hour = 1;
+
+ mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */
+ if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */
+ /* checking days is enough, since other errors
+ * have been checked by strptime() */
+ goto error;
+ }
+
+ return LY_SUCCESS;
+
+error:
+ if (stmt) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, date_len, date, stmt);
+ }
+ return LY_EINVAL;
+}
+
+void
+lysp_sort_revisions(struct lysp_revision *revs)
+{
+ LY_ARRAY_COUNT_TYPE i, r;
+ struct lysp_revision rev;
+
+ for (i = 1, r = 0; i < LY_ARRAY_COUNT(revs); i++) {
+ if (strcmp(revs[i].date, revs[r].date) > 0) {
+ r = i;
+ }
+ }
+
+ if (r) {
+ /* the newest revision is not on position 0, switch them */
+ memcpy(&rev, &revs[0], sizeof rev);
+ memcpy(&revs[0], &revs[r], sizeof rev);
+ memcpy(&revs[r], &rev, sizeof rev);
+ }
+}
+
+LY_ERR
+lysp_check_enum_name(struct lysp_ctx *ctx, const char *name, size_t name_len)
+{
+ if (!name_len) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Enum name must not be zero-length.");
+ return LY_EVALID;
+ } else if (isspace(name[0]) || isspace(name[name_len - 1])) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Enum name must not have any leading or trailing whitespaces (\"%.*s\").",
+ (int)name_len, name);
+ return LY_EVALID;
+ } else {
+ for (size_t u = 0; u < name_len; ++u) {
+ if (iscntrl(name[u])) {
+ LOGWRN(PARSER_CTX(ctx), "Control characters in enum name should be avoided (\"%.*s\", character number %d).",
+ (int)name_len, name, u + 1);
+ break;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Learn built-in type from its name.
+ *
+ * @param[in] name Type name.
+ * @param[in] len Length of @p name.
+ * @return Built-in data type, ::LY_TYPE_UNKNOWN if none matches.
+ */
+static LY_DATA_TYPE
+lysp_type_str2builtin(const char *name, size_t len)
+{
+ if (len >= 4) { /* otherwise it does not match any built-in type */
+ if (name[0] == 'b') {
+ if (name[1] == 'i') {
+ if ((len == 6) && !strncmp(&name[2], "nary", 4)) {
+ return LY_TYPE_BINARY;
+ } else if ((len == 4) && !strncmp(&name[2], "ts", 2)) {
+ return LY_TYPE_BITS;
+ }
+ } else if ((len == 7) && !strncmp(&name[1], "oolean", 6)) {
+ return LY_TYPE_BOOL;
+ }
+ } else if (name[0] == 'd') {
+ if ((len == 9) && !strncmp(&name[1], "ecimal64", 8)) {
+ return LY_TYPE_DEC64;
+ }
+ } else if (name[0] == 'e') {
+ if ((len == 5) && !strncmp(&name[1], "mpty", 4)) {
+ return LY_TYPE_EMPTY;
+ } else if ((len == 11) && !strncmp(&name[1], "numeration", 10)) {
+ return LY_TYPE_ENUM;
+ }
+ } else if (name[0] == 'i') {
+ if (name[1] == 'n') {
+ if ((len == 4) && !strncmp(&name[2], "t8", 2)) {
+ return LY_TYPE_INT8;
+ } else if (len == 5) {
+ if (!strncmp(&name[2], "t16", 3)) {
+ return LY_TYPE_INT16;
+ } else if (!strncmp(&name[2], "t32", 3)) {
+ return LY_TYPE_INT32;
+ } else if (!strncmp(&name[2], "t64", 3)) {
+ return LY_TYPE_INT64;
+ }
+ } else if ((len == 19) && !strncmp(&name[2], "stance-identifier", 17)) {
+ return LY_TYPE_INST;
+ }
+ } else if ((len == 11) && !strncmp(&name[1], "dentityref", 10)) {
+ return LY_TYPE_IDENT;
+ }
+ } else if (name[0] == 'l') {
+ if ((len == 7) && !strncmp(&name[1], "eafref", 6)) {
+ return LY_TYPE_LEAFREF;
+ }
+ } else if (name[0] == 's') {
+ if ((len == 6) && !strncmp(&name[1], "tring", 5)) {
+ return LY_TYPE_STRING;
+ }
+ } else if (name[0] == 'u') {
+ if (name[1] == 'n') {
+ if ((len == 5) && !strncmp(&name[2], "ion", 3)) {
+ return LY_TYPE_UNION;
+ }
+ } else if ((name[1] == 'i') && (name[2] == 'n') && (name[3] == 't')) {
+ if ((len == 5) && (name[4] == '8')) {
+ return LY_TYPE_UINT8;
+ } else if (len == 6) {
+ if (!strncmp(&name[4], "16", 2)) {
+ return LY_TYPE_UINT16;
+ } else if (!strncmp(&name[4], "32", 2)) {
+ return LY_TYPE_UINT32;
+ } else if (!strncmp(&name[4], "64", 2)) {
+ return LY_TYPE_UINT64;
+ }
+ }
+ }
+ }
+ }
+
+ return LY_TYPE_UNKNOWN;
+}
+
+/**
+ * @brief Find a typedef in a sized array.
+ *
+ * @param[in] name Typedef name.
+ * @param[in] typedefs Sized array of typedefs.
+ * @return Found typedef, NULL if none.
+ */
+static const struct lysp_tpdf *
+lysp_typedef_match(const char *name, const struct lysp_tpdf *typedefs)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(typedefs, u) {
+ if (!strcmp(name, typedefs[u].name)) {
+ /* match */
+ return &typedefs[u];
+ }
+ }
+ return NULL;
+}
+
+LY_ERR
+lysp_type_find(const char *id, struct lysp_node *start_node, const struct lysp_module *start_module,
+ const struct lysc_ext_instance *ext, LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node)
+{
+ const char *str, *name;
+ struct lysp_tpdf *typedefs;
+ const struct lysp_tpdf *ext_typedefs;
+ const struct lys_module *mod;
+ const struct lysp_module *local_module;
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ assert(id);
+ assert(start_module);
+ assert(tpdf);
+ assert(node);
+
+ *node = NULL;
+ str = strchr(id, ':');
+ if (str) {
+ mod = ly_resolve_prefix(start_module->mod->ctx, id, str - id, LY_VALUE_SCHEMA, (void *)start_module);
+ local_module = mod ? mod->parsed : NULL;
+ name = str + 1;
+ *type = LY_TYPE_UNKNOWN;
+ } else {
+ local_module = start_module;
+ name = id;
+
+ /* check for built-in types */
+ *type = lysp_type_str2builtin(name, strlen(name));
+ if (*type) {
+ *tpdf = NULL;
+ return LY_SUCCESS;
+ }
+ }
+ LY_CHECK_RET(!local_module, LY_ENOTFOUND);
+
+ if (local_module == start_module) {
+ if (start_node) {
+ /* search typedefs in parent's nodes */
+ for (*node = start_node; *node; *node = (*node)->parent) {
+ *tpdf = lysp_typedef_match(name, lysp_node_typedefs(*node));
+ if (*tpdf) {
+ /* match */
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ if (ext) {
+ /* search typedefs directly in the extension */
+ lyplg_ext_parsed_get_storage(ext, LY_STMT_TYPEDEF, sizeof ext_typedefs, (const void **)&ext_typedefs);
+ if ((*tpdf = lysp_typedef_match(name, ext_typedefs))) {
+ /* match */
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ /* go to main module if in submodule */
+ local_module = local_module->mod->parsed;
+
+ /* search in top-level typedefs */
+ if (local_module->typedefs) {
+ LY_ARRAY_FOR(local_module->typedefs, u) {
+ if (!strcmp(name, local_module->typedefs[u].name)) {
+ /* match */
+ *tpdf = &local_module->typedefs[u];
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ /* search in all submodules' typedefs */
+ LY_ARRAY_FOR(local_module->includes, u) {
+ typedefs = local_module->includes[u].submodule->typedefs;
+ LY_ARRAY_FOR(typedefs, v) {
+ if (!strcmp(name, typedefs[v].name)) {
+ /* match */
+ *tpdf = &typedefs[v];
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Insert @p name to hash table and if @p name has already
+ * been added, then log an error.
+ *
+ * This function is used to detect duplicate names.
+ *
+ * @param[in,out] ctx Context to log the error.
+ * @param[in,out] ht Hash table with top-level names.
+ * @param[in] name Inserted top-level identifier.
+ * @param[in] statement The name of the statement type from which
+ * @p name originated (eg typedef, feature, ...).
+ * @param[in] err_detail Optional error specification.
+ * @return LY_ERR, but LY_EEXIST is mapped to LY_EVALID.
+ */
+static LY_ERR
+lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct hash_table *ht,
+ const char *name, const char *statement, const char *err_detail)
+{
+ LY_ERR ret;
+ uint32_t hash;
+
+ hash = dict_hash(name, strlen(name));
+ ret = lyht_insert(ht, &name, hash, NULL);
+ if (ret == LY_EEXIST) {
+ if (err_detail) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT2, name, statement, err_detail);
+ } else {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, name, statement);
+ }
+ ret = LY_EVALID;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Check name of a new type to avoid name collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @param[in] node Schema node where the type is being defined, NULL in case of a top-level typedef.
+ * @param[in] tpdf Typedef definition to check.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ * typedefs are checked, caller is supposed to free the table.
+ * @return LY_EVALID in case of collision, LY_SUCCESS otherwise.
+ */
+static LY_ERR
+lysp_check_dup_typedef(struct lysp_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
+ struct hash_table *tpdfs_global)
+{
+ struct lysp_node *parent;
+ uint32_t hash;
+ size_t name_len;
+ const char *name;
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lysp_tpdf *typedefs;
+
+ assert(ctx);
+ assert(tpdf);
+
+ name = tpdf->name;
+ name_len = strlen(name);
+
+ if (lysp_type_str2builtin(name, name_len)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of typedef statement - name collision with a built-in type.", name);
+ return LY_EVALID;
+ }
+
+ /* check locally scoped typedefs (avoid name shadowing) */
+ if (node) {
+ typedefs = lysp_node_typedefs(node);
+ LY_ARRAY_FOR(typedefs, u) {
+ if (&typedefs[u] == tpdf) {
+ break;
+ }
+ if (!strcmp(name, typedefs[u].name)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of typedef statement - name collision with sibling type.", name);
+ return LY_EVALID;
+ }
+ }
+ /* search typedefs in parent's nodes */
+ for (parent = node->parent; parent; parent = parent->parent) {
+ if (lysp_typedef_match(name, lysp_node_typedefs(parent))) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of typedef statement - name collision with another scoped type.", name);
+ return LY_EVALID;
+ }
+ }
+ }
+
+ /* check collision with the top-level typedefs */
+ if (node) {
+ hash = dict_hash(name, name_len);
+ if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of typedef statement - scoped type collide with a top-level type.", name);
+ return LY_EVALID;
+ }
+ } else {
+ LY_CHECK_RET(lysp_check_dup_ht_insert(ctx, tpdfs_global, name, "typedef",
+ "name collision with another top-level type"));
+ /* it is not necessary to test collision with the scoped types - in lysp_check_typedefs, all the
+ * top-level typedefs are inserted into the tables before the scoped typedefs, so the collision
+ * is detected in the first branch few lines above */
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compare identifiers.
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lysp_id_cmp(void *val1, void *val2, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ char *id1, *id2;
+
+ id1 = *(char **)val1;
+ id2 = *(char **)val2;
+
+ return strcmp(id1, id2) == 0 ? 1 : 0;
+}
+
+LY_ERR
+lysp_check_dup_typedefs(struct lysp_ctx *ctx, struct lysp_module *mod)
+{
+ struct hash_table *ids_global;
+ const struct lysp_tpdf *typedefs;
+ LY_ARRAY_COUNT_TYPE u, v;
+ uint32_t i;
+ LY_ERR ret = LY_SUCCESS;
+
+ /* check name collisions - typedefs and groupings */
+ ids_global = lyht_new(LYHT_MIN_SIZE, sizeof(char *), lysp_id_cmp, NULL, 1);
+ LY_ARRAY_FOR(mod->typedefs, v) {
+ ret = lysp_check_dup_typedef(ctx, NULL, &mod->typedefs[v], ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ LY_ARRAY_FOR(mod->includes, v) {
+ LY_ARRAY_FOR(mod->includes[v].submodule->typedefs, u) {
+ ret = lysp_check_dup_typedef(ctx, NULL, &mod->includes[v].submodule->typedefs[u], ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ for (i = 0; i < ctx->tpdfs_nodes.count; ++i) {
+ typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[i]);
+ LY_ARRAY_FOR(typedefs, u) {
+ ret = lysp_check_dup_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[i], &typedefs[u], ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyht_free(ids_global);
+ return ret;
+}
+
+static const struct lysp_node_grp *
+lysp_grouping_match(const char *name, struct lysp_node *node)
+{
+ const struct lysp_node_grp *groupings, *grp_iter;
+
+ groupings = lysp_node_groupings(node);
+ LY_LIST_FOR(groupings, grp_iter) {
+ if (!strcmp(name, grp_iter->name)) {
+ /* match */
+ return grp_iter;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Check name of a new grouping to avoid name collisions.
+ *
+ * @param[in] ctx Parser context, module where the grouping is being defined is taken from here.
+ * @param[in] node Schema node where the grouping is being defined, NULL in case of a top-level grouping.
+ * @param[in] grp Grouping definition to check.
+ * @param[in,out] grps_global Initialized hash table to store temporary data between calls. When the module's
+ * groupings are checked, caller is supposed to free the table.
+ * @return LY_EVALID in case of collision, LY_SUCCESS otherwise.
+ */
+static LY_ERR
+lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const struct lysp_node_grp *grp,
+ struct hash_table *grps_global)
+{
+ struct lysp_node *parent;
+ uint32_t hash;
+ size_t name_len;
+ const char *name;
+ const struct lysp_node_grp *groupings, *grp_iter;
+
+ assert(ctx);
+ assert(grp);
+
+ name = grp->name;
+ name_len = strlen(name);
+
+ /* check locally scoped groupings (avoid name shadowing) */
+ if (node) {
+ groupings = lysp_node_groupings(node);
+ LY_LIST_FOR(groupings, grp_iter) {
+ if (grp_iter == grp) {
+ break;
+ }
+ if (!strcmp(name, grp_iter->name)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of grouping statement - name collision with sibling grouping.", name);
+ return LY_EVALID;
+ }
+ }
+ /* search grouping in parent's nodes */
+ for (parent = node->parent; parent; parent = parent->parent) {
+ if (lysp_grouping_match(name, parent)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of grouping statement - name collision with another scoped grouping.", name);
+ return LY_EVALID;
+ }
+ }
+ }
+
+ /* check collision with the top-level groupings */
+ if (node) {
+ hash = dict_hash(name, name_len);
+ if (!lyht_find(grps_global, &name, hash, NULL)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of grouping statement - scoped grouping collide with a top-level grouping.", name);
+ return LY_EVALID;
+ }
+ } else {
+ LY_CHECK_RET(lysp_check_dup_ht_insert(ctx, grps_global, name, "grouping",
+ "name collision with another top-level grouping"));
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_dup_groupings(struct lysp_ctx *ctx, struct lysp_module *mod)
+{
+ struct hash_table *ids_global;
+ const struct lysp_node_grp *groupings, *grp_iter;
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t i;
+ LY_ERR ret = LY_SUCCESS;
+
+ ids_global = lyht_new(LYHT_MIN_SIZE, sizeof(char *), lysp_id_cmp, NULL, 1);
+ LY_LIST_FOR(mod->groupings, grp_iter) {
+ ret = lysp_check_dup_grouping(ctx, NULL, grp_iter, ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ LY_ARRAY_FOR(mod->includes, u) {
+ LY_LIST_FOR(mod->includes[u].submodule->groupings, grp_iter) {
+ ret = lysp_check_dup_grouping(ctx, NULL, grp_iter, ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ for (i = 0; i < ctx->grps_nodes.count; ++i) {
+ groupings = lysp_node_groupings((struct lysp_node *)ctx->grps_nodes.objs[i]);
+ LY_LIST_FOR(groupings, grp_iter) {
+ ret = lysp_check_dup_grouping(ctx, (struct lysp_node *)ctx->grps_nodes.objs[i], grp_iter, ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyht_free(ids_global);
+ return ret;
+}
+
+static ly_bool
+ly_ptrequal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ void *ptr1 = *((void **)val1_p), *ptr2 = *((void **)val2_p);
+
+ return ptr1 == ptr2 ? 1 : 0;
+}
+
+LY_ERR
+lysp_check_dup_features(struct lysp_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct hash_table *ht;
+ struct lysp_feature *f;
+ LY_ERR ret = LY_SUCCESS;
+
+ ht = lyht_new(LYHT_MIN_SIZE, sizeof(void *), ly_ptrequal_cb, NULL, 1);
+ LY_CHECK_RET(!ht, LY_EMEM);
+
+ /* add all module features into a hash table */
+ LY_ARRAY_FOR(mod->features, struct lysp_feature, f) {
+ ret = lysp_check_dup_ht_insert(ctx, ht, f->name, "feature",
+ "name collision with another top-level feature");
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* add all submodule features into a hash table */
+ LY_ARRAY_FOR(mod->includes, u) {
+ LY_ARRAY_FOR(mod->includes[u].submodule->features, struct lysp_feature, f) {
+ ret = lysp_check_dup_ht_insert(ctx, ht, f->name, "feature",
+ "name collision with another top-level feature");
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyht_free(ht);
+ return ret;
+}
+
+LY_ERR
+lysp_check_dup_identities(struct lysp_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct hash_table *ht;
+ struct lysp_ident *i;
+ LY_ERR ret = LY_SUCCESS;
+
+ ht = lyht_new(LYHT_MIN_SIZE, sizeof(void *), ly_ptrequal_cb, NULL, 1);
+ LY_CHECK_RET(!ht, LY_EMEM);
+
+ /* add all module identities into a hash table */
+ LY_ARRAY_FOR(mod->identities, struct lysp_ident, i) {
+ ret = lysp_check_dup_ht_insert(ctx, ht, i->name, "identity",
+ "name collision with another top-level identity");
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* add all submodule identities into a hash table */
+ LY_ARRAY_FOR(mod->includes, u) {
+ LY_ARRAY_FOR(mod->includes[u].submodule->identities, struct lysp_ident, i) {
+ ret = lysp_check_dup_ht_insert(ctx, ht, i->name, "identity",
+ "name collision with another top-level identity");
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyht_free(ht);
+ 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 *filename, *dot, *rev, *name;
+ uint8_t latest_revision;
+ size_t len;
+ 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) {
+ /* check that name and revision match filename */
+ filename = strrchr(info->path, '/');
+ if (!filename) {
+ filename = info->path;
+ } else {
+ filename++;
+ }
+ /* name */
+ len = strlen(name);
+ rev = strchr(filename, '@');
+ dot = strrchr(info->path, '.');
+ if (strncmp(filename, name, len) ||
+ ((rev && (rev != &filename[len])) || (!rev && (dot != &filename[len])))) {
+ LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, name);
+ }
+ /* revision */
+ if (rev) {
+ len = dot - ++rev;
+ if (!revs || (len != LY_REV_SIZE - 1) || strncmp(revs[0].date, rev, len)) {
+ LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
+ revs ? revs[0].date : "none");
+ }
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse a (sub)module from a local file and add into the context.
+ *
+ * This function does not check the presence of the (sub)module in context, it should be done before calling this function.
+ *
+ * @param[in] ctx libyang context where to work.
+ * @param[in] name Name of the (sub)module to load.
+ * @param[in] revision Optional revision of the (sub)module to load, if NULL the newest revision is being loaded.
+ * @param[in] main_ctx Parser context of the main module in case of loading submodule.
+ * @param[in] main_name Main module name in case of loading submodule.
+ * @param[in] required Module is required so error (even if the input file not found) are important. If 0, there is some
+ * backup and it is actually ok if the input data are not found. However, parser reports errors even in this case.
+ * @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] result Parsed YANG schema tree of the requested module (struct lys_module*) or submodule (struct lysp_submodule*).
+ * If it is a module, it is already in the context!
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lys_parse_localfile(struct ly_ctx *ctx, const char *name, const char *revision, struct lysp_ctx *main_ctx,
+ const char *main_name, ly_bool required, struct ly_set *new_mods, void **result)
+{
+ struct ly_in *in;
+ char *filepath = NULL;
+ LYS_INFORMAT format;
+ void *mod = NULL;
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_load_module_check_data check_data = {0};
+
+ LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name,
+ revision, &filepath, &format));
+ if (!filepath) {
+ if (required) {
+ LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.", name, revision ? "@" : "",
+ revision ? revision : "");
+ }
+ return LY_ENOTFOUND;
+ }
+
+ LOGVRB("Loading schema from \"%s\" file.", filepath);
+
+ /* 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;
+ if (main_ctx) {
+ ret = lys_parse_submodule(ctx, in, format, main_ctx, lysp_load_module_check, &check_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);
+
+ }
+ ly_in_free(in, 1);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ *result = mod;
+
+ /* success */
+
+cleanup:
+ free(filepath);
+ return ret;
+}
+
+/**
+ * @brief Load module from searchdirs or from callback.
+ *
+ * @param[in] ctx libyang context where to work.
+ * @param[in] name Name of module to load.
+ * @param[in] revision Revision of module to load.
+ * @param[in] mod_latest Module with the latest revision found in context, otherwise set to NULL.
+ * @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] mod Loaded module.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+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)
+{
+ 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 ly_in *in;
+
+ *mod = NULL;
+
+ if (mod_latest && (!ctx->imp_clb || (mod_latest->latest_revision & LYS_MOD_LATEST_IMPCLB)) &&
+ ((ctx->flags & LY_CTX_DISABLE_SEARCHDIRS) || (mod_latest->latest_revision & LYS_MOD_LATEST_SEARCHDIRS))) {
+ /* we are not able to find a newer revision */
+ return LY_SUCCESS;
+ }
+
+ if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+search_clb:
+ /* check there is a callback and should be called */
+ 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);
+ ly_in_free(in, 0);
+ if (module_data_free) {
+ module_data_free((void *)module_data, ctx->imp_clb_data);
+ }
+ }
+ }
+ if (*mod && !revision) {
+ /* we got the latest revision module from the callback */
+ (*mod)->latest_revision |= LYS_MOD_LATEST_IMPCLB;
+ } else if (!*mod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_file;
+ }
+ } else {
+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);
+ }
+ if (*mod && !revision) {
+ /* we got the latest revision module in the searchdirs */
+ (*mod)->latest_revision |= LYS_MOD_LATEST_IMPCLB;
+ } else if (!*mod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_clb;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get module without revision according to priorities.
+ *
+ * 1. Search for the module with LYS_MOD_IMPORTED_REV.
+ * 2. Search for the implemented module.
+ * 3. Search for the latest module in the context.
+ *
+ * @param[in] ctx libyang context where module is searched.
+ * @param[in] name Name of the searched module.
+ * @return Found module from context or NULL.
+ */
+static struct lys_module *
+lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
+{
+ struct lys_module *mod, *mod_impl;
+ uint32_t index;
+
+ /* Try to find module with LYS_MOD_IMPORTED_REV flag. */
+ index = 0;
+ while ((mod = ly_ctx_get_module_iter(ctx, &index))) {
+ if (!strcmp(mod->name, name) && (mod->latest_revision & LYS_MOD_IMPORTED_REV)) {
+ break;
+ }
+ }
+
+ /* Try to find the implemented module. */
+ mod_impl = ly_ctx_get_module_implemented(ctx, name);
+ if (mod && mod_impl) {
+ LOGVRB("Implemented module \"%s@%s\" is not used for import, revision \"%s\" is imported instead.",
+ mod_impl->name, mod_impl->revision, mod->revision);
+ return mod;
+ } else if (mod_impl) {
+ return mod_impl;
+ }
+
+ /* Try to find the latest module in the current context. */
+ mod = ly_ctx_get_module_latest(ctx, name);
+
+ return mod;
+}
+
+/**
+ * @brief Check if a circular dependency exists between modules.
+ *
+ * @param[in] ctx libyang context for log an error.
+ * @param[in,out] mod Examined module which is set to NULL
+ * if the circular dependency is detected.
+ * @return LY_SUCCESS if no circular dependecy is detected,
+ * otherwise LY_EVALID.
+ */
+static LY_ERR
+lys_check_circular_dependency(struct ly_ctx *ctx, struct lys_module **mod)
+{
+ if ((*mod) && (*mod)->parsed->parsing) {
+ LOGVAL(ctx, LYVE_REFERENCE, "A circular dependency (import) for module \"%s\".", (*mod)->name);
+ *mod = NULL;
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_parse_load(struct ly_ctx *ctx, const char *name, const char *revision, struct ly_set *new_mods,
+ struct lys_module **mod)
+{
+ struct lys_module *mod_latest = NULL;
+
+ assert(mod && new_mods);
+
+ /*
+ * Try to get the module from the context.
+ */
+ if (revision) {
+ /* Get the specific revision. */
+ *mod = ly_ctx_get_module(ctx, name, revision);
+ } else {
+ /* Get the requested module in a suitable revision in the context. */
+ *mod = lys_get_module_without_revision(ctx, name);
+ if (*mod && !(*mod)->implemented && !((*mod)->latest_revision & LYS_MOD_IMPORTED_REV)) {
+ /* Let us now search with callback and searchpaths to check
+ * if there is newer revision outside the context.
+ */
+ mod_latest = *mod;
+ *mod = NULL;
+ }
+ }
+
+ 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.
+ */
+ if (!*mod) {
+ LOGVRB("Newer revision than \"%s@%s\" not found, using this as the latest revision.",
+ mod_latest->name, mod_latest->revision);
+ assert(mod_latest->latest_revision & LYS_MOD_LATEST_REV);
+ mod_latest->latest_revision |= LYS_MOD_LATEST_SEARCHDIRS;
+ *mod = mod_latest;
+ } else if (*mod && !revision && ((*mod)->latest_revision & LYS_MOD_LATEST_REV)) {
+ (*mod)->latest_revision |= LYS_MOD_LATEST_SEARCHDIRS;
+ }
+ }
+
+ /* Checking the circular dependence of imported modules. */
+ LY_CHECK_RET(lys_check_circular_dependency(ctx, mod));
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_stringchar(struct lysp_ctx *ctx, uint32_t c)
+{
+ if (!is_yangutf8char(c)) {
+ LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, c);
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_identifierchar(struct lysp_ctx *ctx, uint32_t c, ly_bool first, uint8_t *prefix)
+{
+ if (first || (prefix && ((*prefix) == 1))) {
+ if (!is_yangidentstartchar(c)) {
+ if ((c < UCHAR_MAX) && isprint(c)) {
+ if (ctx) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '%c' (0x%04x).", (char)c, c);
+ }
+ } else {
+ if (ctx) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character 0x%04x.", c);
+ }
+ }
+ return LY_EVALID;
+ }
+ if (prefix) {
+ if (first) {
+ (*prefix) = 0;
+ } else {
+ (*prefix) = 2;
+ }
+ }
+ } else if ((c == ':') && prefix && ((*prefix) == 0)) {
+ (*prefix) = 1;
+ } else if (!is_yangidentchar(c)) {
+ if (ctx) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier character '%c' (0x%04x).", (char)c, c);
+ }
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Try to find the parsed submodule in main module for the given include record.
+ *
+ * @param[in] pctx main parser context
+ * @param[in] inc The include record with missing parsed submodule. According to include info try to find
+ * the corresponding parsed submodule in main module's includes.
+ * @return LY_SUCCESS - the parsed submodule was found and inserted into the @p inc record
+ * @return LY_ENOT - the parsed module was not found.
+ * @return LY_EVALID - YANG rule violation
+ */
+static LY_ERR
+lysp_main_pmod_get_submodule(struct lysp_ctx *pctx, struct lysp_include *inc)
+{
+ LY_ARRAY_COUNT_TYPE i;
+ struct lysp_module *main_pmod = PARSER_CUR_PMOD(pctx)->mod->parsed;
+
+ LY_ARRAY_FOR(main_pmod->includes, i) {
+ if (strcmp(main_pmod->includes[i].name, inc->name)) {
+ continue;
+ }
+
+ if (inc->rev[0] && strncmp(inc->rev, main_pmod->includes[i].rev, LY_REV_SIZE)) {
+ LOGVAL(PARSER_CTX(pctx), LYVE_REFERENCE,
+ "Submodule %s includes different revision (%s) of the submodule %s:%s included by the main module %s.",
+ ((struct lysp_submodule *)PARSER_CUR_PMOD(pctx))->name, inc->rev,
+ main_pmod->includes[i].name, main_pmod->includes[i].rev, main_pmod->mod->name);
+ return LY_EVALID;
+ }
+
+ inc->submodule = main_pmod->includes[i].submodule;
+ return inc->submodule ? LY_SUCCESS : LY_ENOT;
+ }
+
+ if (main_pmod->version == LYS_VERSION_1_1) {
+ LOGVAL(PARSER_CTX(pctx), LYVE_REFERENCE,
+ "YANG 1.1 requires all submodules to be included from main module. "
+ "But submodule \"%s\" includes submodule \"%s\" which is not included by main module \"%s\".",
+ ((struct lysp_submodule *)PARSER_CUR_PMOD(pctx))->name, inc->name, main_pmod->mod->name);
+ return LY_EVALID;
+ } else {
+ return LY_ENOT;
+ }
+}
+
+/**
+ * @brief Try to find the parsed submodule in currenlty parsed modules for the given include record.
+ *
+ * @param[in] pctx main parser context
+ * @param[in] inc The include record with missing parsed submodule.
+ * @return LY_SUCCESS - the parsed submodule was found and inserted into the @p inc record
+ * @return LY_ENOT - the parsed module was not found.
+ * @return LY_EVALID - YANG rule violation
+ */
+static LY_ERR
+lysp_parsed_mods_get_submodule(struct lysp_ctx *pctx, struct lysp_include *inc)
+{
+ uint32_t i;
+ struct lysp_submodule *submod;
+
+ for (i = 0; i < pctx->parsed_mods->count - 1; ++i) {
+ submod = pctx->parsed_mods->objs[i];
+ if (!submod->is_submod) {
+ continue;
+ }
+
+ if (strcmp(submod->name, inc->name)) {
+ continue;
+ }
+
+ if (inc->rev[0] && submod->revs && strncmp(inc->rev, submod->revs[0].date, LY_REV_SIZE)) {
+ LOGVAL(PARSER_CTX(pctx), LYVE_REFERENCE,
+ "Submodule %s includes different revision (%s) of the submodule %s:%s included by the main module %s.",
+ ((struct lysp_submodule *)PARSER_CUR_PMOD(pctx))->name, inc->rev,
+ submod->name, submod->revs[0].date, PARSER_CUR_PMOD(pctx)->mod->name);
+ return LY_EVALID;
+ }
+
+ inc->submodule = submod;
+ return LY_SUCCESS;
+ }
+
+ return LY_ENOT;
+}
+
+/**
+ * @brief Make the copy of the given include record into the main module.
+ *
+ * YANG 1.0 does not require the main module to include all the submodules. Therefore, parsing submodules can cause
+ * reallocating and extending the includes array in the main module by the submodules included only in submodules.
+ *
+ * @param[in] pctx main parser context
+ * @param[in] inc Include record to copy into main module taken from @p pctx.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_inject_submodule(struct lysp_ctx *pctx, struct lysp_include *inc)
+{
+ LY_ARRAY_COUNT_TYPE i;
+ struct lysp_include *inc_new, *inc_tofill = NULL;
+ struct lysp_module *main_pmod = PARSER_CUR_PMOD(pctx)->mod->parsed;
+
+ /* first, try to find the corresponding record with missing parsed submodule */
+ LY_ARRAY_FOR(main_pmod->includes, i) {
+ if (strcmp(main_pmod->includes[i].name, inc->name)) {
+ continue;
+ }
+ inc_tofill = &main_pmod->includes[i];
+ break;
+ }
+
+ if (inc_tofill) {
+ inc_tofill->submodule = inc->submodule;
+ } else {
+ LY_ARRAY_NEW_RET(PARSER_CTX(pctx), main_pmod->includes, inc_new, LY_EMEM);
+
+ inc_new->submodule = inc->submodule;
+ DUP_STRING_RET(PARSER_CTX(pctx), inc->name, inc_new->name);
+ DUP_STRING_RET(PARSER_CTX(pctx), inc->dsc, inc_new->dsc);
+ DUP_STRING_RET(PARSER_CTX(pctx), inc->ref, inc_new->ref);
+ /* TODO duplicate extensions */
+ memcpy(inc_new->rev, inc->rev, LY_REV_SIZE);
+ inc_new->injected = 1;
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_load_submodules(struct lysp_ctx *pctx, struct lysp_module *pmod, struct ly_set *new_mods)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_ctx *ctx = PARSER_CTX(pctx);
+
+ LY_ARRAY_FOR(pmod->includes, u) {
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lysp_submodule *submod = NULL;
+ struct lysp_include *inc = &pmod->includes[u];
+
+ if (inc->submodule) {
+ continue;
+ }
+
+ 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);
+ }
+
+ /* try to use currently parsed submodule */
+ r = lysp_parsed_mods_get_submodule(pctx, inc);
+ LY_CHECK_RET(r != LY_ENOT, r);
+
+ /* submodule not present in the main module, get the input data and parse it */
+ if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+search_clb:
+ if (ctx->imp_clb) {
+ const char *submodule_data = NULL;
+ 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 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) {
+ 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, 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];
+
+ ly_in_free(in, 0);
+ if (submodule_data_free) {
+ submodule_data_free((void *)submodule_data, ctx->imp_clb_data);
+ }
+ }
+ }
+ if (!submod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_file;
+ }
+ } else {
+search_file:
+ if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
+ /* submodule was not received from the callback or there is no callback set */
+ lys_parse_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, pctx->main_ctx,
+ PARSER_CUR_PMOD(pctx->main_ctx)->mod->name, 1, new_mods, (void **)&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];
+ }
+ if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_clb;
+ }
+ }
+ if (submod) {
+ if (!inc->rev[0] && (submod->latest_revision == 1)) {
+ /* update the latest_revision flag - here we have selected the latest available schema,
+ * consider that even the callback provides correct latest revision */
+ submod->latest_revision = 2;
+ }
+
+ inc->submodule = submod;
+ if (ret == LY_ENOT) {
+ /* 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]));
+ }
+ }
+ if (!inc->submodule) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name,
+ PARSER_CUR_PMOD(pctx)->is_submod ? ((struct lysp_submodule *)PARSER_CUR_PMOD(pctx))->name :
+ PARSER_CUR_PMOD(pctx)->mod->name);
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const struct lysc_when *
+lysc_has_when(const struct lysc_node *node)
+{
+ struct lysc_when **when;
+
+ if (!node) {
+ return NULL;
+ }
+
+ do {
+ when = lysc_node_when(node);
+ if (when) {
+ return when[0];
+ }
+ node = node->parent;
+ } while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
+
+ return NULL;
+}
+
+LIBYANG_API_DEF const struct lys_module *
+lysc_owner_module(const struct lysc_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ for ( ; node->parent; node = node->parent) {}
+ return node->module;
+}
+
+LIBYANG_API_DEF const char *
+lys_nodetype2str(uint16_t nodetype)
+{
+ switch (nodetype) {
+ case LYS_CONTAINER:
+ return "container";
+ case LYS_CHOICE:
+ return "choice";
+ case LYS_LEAF:
+ return "leaf";
+ case LYS_LEAFLIST:
+ return "leaf-list";
+ case LYS_LIST:
+ return "list";
+ case LYS_ANYXML:
+ return "anyxml";
+ case LYS_ANYDATA:
+ return "anydata";
+ case LYS_CASE:
+ return "case";
+ case LYS_RPC:
+ return "RPC";
+ case LYS_ACTION:
+ return "action";
+ case LYS_NOTIF:
+ return "notification";
+ case LYS_USES:
+ return "uses";
+ default:
+ return "unknown";
+ }
+}
+
+const char *
+lys_datatype2str(LY_DATA_TYPE basetype)
+{
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ return "binary";
+ case LY_TYPE_UINT8:
+ return "uint8";
+ case LY_TYPE_UINT16:
+ return "uint16";
+ case LY_TYPE_UINT32:
+ return "uint32";
+ case LY_TYPE_UINT64:
+ return "uint64";
+ case LY_TYPE_STRING:
+ return "string";
+ case LY_TYPE_BITS:
+ return "bits";
+ case LY_TYPE_BOOL:
+ return "boolean";
+ case LY_TYPE_DEC64:
+ return "decimal64";
+ case LY_TYPE_EMPTY:
+ return "empty";
+ case LY_TYPE_ENUM:
+ return "enumeration";
+ case LY_TYPE_IDENT:
+ return "identityref";
+ case LY_TYPE_INST:
+ return "instance-identifier";
+ case LY_TYPE_LEAFREF:
+ return "leafref";
+ case LY_TYPE_UNION:
+ return "union";
+ case LY_TYPE_INT8:
+ return "int8";
+ case LY_TYPE_INT16:
+ return "int16";
+ case LY_TYPE_INT32:
+ return "int32";
+ case LY_TYPE_INT64:
+ return "int64";
+ default:
+ return "unknown";
+ }
+}
+
+LIBYANG_API_DEF const struct lysp_tpdf *
+lysp_node_typedefs(const struct lysp_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return ((struct lysp_node_container *)node)->typedefs;
+ case LYS_LIST:
+ return ((struct lysp_node_list *)node)->typedefs;
+ case LYS_GROUPING:
+ return ((struct lysp_node_grp *)node)->typedefs;
+ case LYS_RPC:
+ case LYS_ACTION:
+ return ((struct lysp_node_action *)node)->typedefs;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ return ((struct lysp_node_action_inout *)node)->typedefs;
+ case LYS_NOTIF:
+ return ((struct lysp_node_notif *)node)->typedefs;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysp_node_grp *
+lysp_node_groupings(const struct lysp_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return ((struct lysp_node_container *)node)->groupings;
+ case LYS_LIST:
+ return ((struct lysp_node_list *)node)->groupings;
+ case LYS_GROUPING:
+ return ((struct lysp_node_grp *)node)->groupings;
+ case LYS_RPC:
+ case LYS_ACTION:
+ return ((struct lysp_node_action *)node)->groupings;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ return ((struct lysp_node_action_inout *)node)->groupings;
+ case LYS_NOTIF:
+ return ((struct lysp_node_notif *)node)->groupings;
+ default:
+ return NULL;
+ }
+}
+
+struct lysp_node_action **
+lysp_node_actions_p(struct lysp_node *node)
+{
+ assert(node);
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container *)node)->actions;
+ case LYS_LIST:
+ return &((struct lysp_node_list *)node)->actions;
+ case LYS_GROUPING:
+ return &((struct lysp_node_grp *)node)->actions;
+ case LYS_AUGMENT:
+ return &((struct lysp_node_augment *)node)->actions;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysp_node_action *
+lysp_node_actions(const struct lysp_node *node)
+{
+ struct lysp_node_action **actions;
+
+ actions = lysp_node_actions_p((struct lysp_node *)node);
+ if (actions) {
+ return *actions;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysp_node_notif **
+lysp_node_notifs_p(struct lysp_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container *)node)->notifs;
+ case LYS_LIST:
+ return &((struct lysp_node_list *)node)->notifs;
+ case LYS_GROUPING:
+ return &((struct lysp_node_grp *)node)->notifs;
+ case LYS_AUGMENT:
+ return &((struct lysp_node_augment *)node)->notifs;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysp_node_notif *
+lysp_node_notifs(const struct lysp_node *node)
+{
+ struct lysp_node_notif **notifs;
+
+ notifs = lysp_node_notifs_p((struct lysp_node *)node);
+ if (notifs) {
+ return *notifs;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysp_node **
+lysp_node_child_p(struct lysp_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container *)node)->child;
+ case LYS_CHOICE:
+ return &((struct lysp_node_choice *)node)->child;
+ case LYS_LIST:
+ return &((struct lysp_node_list *)node)->child;
+ case LYS_CASE:
+ return &((struct lysp_node_case *)node)->child;
+ case LYS_GROUPING:
+ return &((struct lysp_node_grp *)node)->child;
+ case LYS_AUGMENT:
+ return &((struct lysp_node_augment *)node)->child;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ return &((struct lysp_node_action_inout *)node)->child;
+ case LYS_NOTIF:
+ return &((struct lysp_node_notif *)node)->child;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysp_node *
+lysp_node_child(const struct lysp_node *node)
+{
+ struct lysp_node **child;
+
+ if (!node) {
+ return NULL;
+ }
+
+ child = lysp_node_child_p((struct lysp_node *)node);
+ if (child) {
+ return *child;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysp_restr **
+lysp_node_musts_p(const struct lysp_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container *)node)->musts;
+ case LYS_LEAF:
+ return &((struct lysp_node_leaf *)node)->musts;
+ case LYS_LEAFLIST:
+ return &((struct lysp_node_leaflist *)node)->musts;
+ case LYS_LIST:
+ return &((struct lysp_node_list *)node)->musts;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ return &((struct lysp_node_anydata *)node)->musts;
+ case LYS_NOTIF:
+ return &((struct lysp_node_notif *)node)->musts;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ return &((struct lysp_node_action_inout *)node)->musts;
+ default:
+ return NULL;
+ }
+}
+
+struct lysp_restr *
+lysp_node_musts(const struct lysp_node *node)
+{
+ struct lysp_restr **musts;
+
+ musts = lysp_node_musts_p(node);
+ if (musts) {
+ return *musts;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysp_when **
+lysp_node_when_p(const struct lysp_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container *)node)->when;
+ case LYS_CHOICE:
+ return &((struct lysp_node_choice *)node)->when;
+ case LYS_LEAF:
+ return &((struct lysp_node_leaf *)node)->when;
+ case LYS_LEAFLIST:
+ return &((struct lysp_node_leaflist *)node)->when;
+ case LYS_LIST:
+ return &((struct lysp_node_list *)node)->when;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ return &((struct lysp_node_anydata *)node)->when;
+ case LYS_CASE:
+ return &((struct lysp_node_case *)node)->when;
+ case LYS_USES:
+ return &((struct lysp_node_uses *)node)->when;
+ case LYS_AUGMENT:
+ return &((struct lysp_node_augment *)node)->when;
+ default:
+ return NULL;
+ }
+}
+
+struct lysp_when *
+lysp_node_when(const struct lysp_node *node)
+{
+ struct lysp_when **when;
+
+ when = lysp_node_when_p(node);
+ if (when) {
+ return *when;
+ } else {
+ return NULL;
+ }
+}
+
+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;
+ case LYS_LIST:
+ return &((struct lysc_node_list *)node)->actions;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysc_node_action *
+lysc_node_actions(const struct lysc_node *node)
+{
+ struct lysc_node_action **actions;
+
+ actions = lysc_node_actions_p((struct lysc_node *)node);
+ if (actions) {
+ return *actions;
+ } else {
+ return NULL;
+ }
+}
+
+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;
+ case LYS_LIST:
+ return &((struct lysc_node_list *)node)->notifs;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysc_node_notif *
+lysc_node_notifs(const struct lysc_node *node)
+{
+ struct lysc_node_notif **notifs;
+
+ notifs = lysc_node_notifs_p((struct lysc_node *)node);
+ if (notifs) {
+ return *notifs;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysc_node **
+lysc_node_child_p(const struct lysc_node *node)
+{
+ assert(node && !(node->nodetype & (LYS_RPC | LYS_ACTION)));
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysc_node_container *)node)->child;
+ case LYS_CHOICE:
+ return (struct lysc_node **)&((struct lysc_node_choice *)node)->cases;
+ case LYS_CASE:
+ return &((struct lysc_node_case *)node)->child;
+ case LYS_LIST:
+ return &((struct lysc_node_list *)node)->child;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ return &((struct lysc_node_action_inout *)node)->child;
+ case LYS_NOTIF:
+ return &((struct lysc_node_notif *)node)->child;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const struct lysc_node *
+lysc_node_child(const struct lysc_node *node)
+{
+ struct lysc_node **child;
+
+ if (!node) {
+ return NULL;
+ }
+
+ if (node->nodetype & (LYS_RPC | LYS_ACTION)) {
+ return &((struct lysc_node_action *)node)->input.node;
+ } else {
+ child = lysc_node_child_p(node);
+ if (child) {
+ return *child;
+ }
+ }
+
+ return NULL;
+}
+
+struct lysc_must **
+lysc_node_musts_p(const struct lysc_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysc_node_container *)node)->musts;
+ case LYS_LEAF:
+ return &((struct lysc_node_leaf *)node)->musts;
+ case LYS_LEAFLIST:
+ return &((struct lysc_node_leaflist *)node)->musts;
+ case LYS_LIST:
+ return &((struct lysc_node_list *)node)->musts;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ return &((struct lysc_node_anydata *)node)->musts;
+ case LYS_NOTIF:
+ return &((struct lysc_node_notif *)node)->musts;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ return &((struct lysc_node_action_inout *)node)->musts;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF struct lysc_must *
+lysc_node_musts(const struct lysc_node *node)
+{
+ struct lysc_must **must_p;
+
+ must_p = lysc_node_musts_p(node);
+ if (must_p) {
+ return *must_p;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysc_when ***
+lysc_node_when_p(const struct lysc_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysc_node_container *)node)->when;
+ case LYS_CHOICE:
+ return &((struct lysc_node_choice *)node)->when;
+ case LYS_LEAF:
+ return &((struct lysc_node_leaf *)node)->when;
+ case LYS_LEAFLIST:
+ return &((struct lysc_node_leaflist *)node)->when;
+ case LYS_LIST:
+ return &((struct lysc_node_list *)node)->when;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ return &((struct lysc_node_anydata *)node)->when;
+ case LYS_CASE:
+ return &((struct lysc_node_case *)node)->when;
+ case LYS_NOTIF:
+ return &((struct lysc_node_notif *)node)->when;
+ case LYS_RPC:
+ case LYS_ACTION:
+ return &((struct lysc_node_action *)node)->when;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF struct lysc_when **
+lysc_node_when(const struct lysc_node *node)
+{
+ struct lysc_when ***when_p;
+
+ when_p = lysc_node_when_p(node);
+ if (when_p) {
+ return *when_p;
+ } else {
+ return NULL;
+ }
+}
+
+enum ly_stmt
+lysp_match_kw(struct ly_in *in, uint64_t *indent)
+{
+/**
+ * @brief Move the input by COUNT items. Also updates the indent value in yang parser context
+ * @param[in] COUNT number of items for which the DATA pointer is supposed to move on.
+ *
+ * *INDENT-OFF*
+ */
+#define MOVE_IN(COUNT) \
+ ly_in_skip(in, COUNT); \
+ if (indent) { \
+ (*indent)+=COUNT; \
+ }
+#define IF_KW(STR, LEN, STMT) \
+ if (!strncmp(in->current, STR, LEN)) { \
+ MOVE_IN(LEN); \
+ (*kw)=STMT; \
+ }
+#define IF_KW_PREFIX(STR, LEN) \
+ if (!strncmp(in->current, STR, LEN)) { \
+ MOVE_IN(LEN);
+#define IF_KW_PREFIX_END \
+ }
+
+ const char *start = in->current;
+ enum ly_stmt result = LY_STMT_NONE;
+ enum ly_stmt *kw = &result;
+ /* read the keyword itself */
+ switch (in->current[0]) {
+ case 'a':
+ MOVE_IN(1);
+ IF_KW("rgument", 7, LY_STMT_ARGUMENT)
+ else IF_KW("ugment", 6, LY_STMT_AUGMENT)
+ else IF_KW("ction", 5, LY_STMT_ACTION)
+ else IF_KW_PREFIX("ny", 2)
+ IF_KW("data", 4, LY_STMT_ANYDATA)
+ else IF_KW("xml", 3, LY_STMT_ANYXML)
+ IF_KW_PREFIX_END
+ break;
+ case 'b':
+ MOVE_IN(1);
+ IF_KW("ase", 3, LY_STMT_BASE)
+ else IF_KW("elongs-to", 9, LY_STMT_BELONGS_TO)
+ else IF_KW("it", 2, LY_STMT_BIT)
+ break;
+ case 'c':
+ MOVE_IN(1);
+ IF_KW("ase", 3, LY_STMT_CASE)
+ else IF_KW("hoice", 5, LY_STMT_CHOICE)
+ else IF_KW_PREFIX("on", 2)
+ IF_KW("fig", 3, LY_STMT_CONFIG)
+ else IF_KW_PREFIX("ta", 2)
+ IF_KW("ct", 2, LY_STMT_CONTACT)
+ else IF_KW("iner", 4, LY_STMT_CONTAINER)
+ IF_KW_PREFIX_END
+ IF_KW_PREFIX_END
+ break;
+ case 'd':
+ MOVE_IN(1);
+ IF_KW_PREFIX("e", 1)
+ IF_KW("fault", 5, LY_STMT_DEFAULT)
+ else IF_KW("scription", 9, LY_STMT_DESCRIPTION)
+ else IF_KW_PREFIX("viat", 4)
+ IF_KW("e", 1, LY_STMT_DEVIATE)
+ else IF_KW("ion", 3, LY_STMT_DEVIATION)
+ IF_KW_PREFIX_END
+ IF_KW_PREFIX_END
+ break;
+ case 'e':
+ MOVE_IN(1);
+ IF_KW("num", 3, LY_STMT_ENUM)
+ else IF_KW_PREFIX("rror-", 5)
+ IF_KW("app-tag", 7, LY_STMT_ERROR_APP_TAG)
+ else IF_KW("message", 7, LY_STMT_ERROR_MESSAGE)
+ IF_KW_PREFIX_END
+ else IF_KW("xtension", 8, LY_STMT_EXTENSION)
+ break;
+ case 'f':
+ MOVE_IN(1);
+ IF_KW("eature", 6, LY_STMT_FEATURE)
+ else IF_KW("raction-digits", 14, LY_STMT_FRACTION_DIGITS)
+ break;
+ case 'g':
+ MOVE_IN(1);
+ IF_KW("rouping", 7, LY_STMT_GROUPING)
+ break;
+ case 'i':
+ MOVE_IN(1);
+ IF_KW("dentity", 7, LY_STMT_IDENTITY)
+ else IF_KW("f-feature", 9, LY_STMT_IF_FEATURE)
+ else IF_KW("mport", 5, LY_STMT_IMPORT)
+ else IF_KW_PREFIX("n", 1)
+ IF_KW("clude", 5, LY_STMT_INCLUDE)
+ else IF_KW("put", 3, LY_STMT_INPUT)
+ IF_KW_PREFIX_END
+ break;
+ case 'k':
+ MOVE_IN(1);
+ IF_KW("ey", 2, LY_STMT_KEY)
+ break;
+ case 'l':
+ MOVE_IN(1);
+ IF_KW_PREFIX("e", 1)
+ IF_KW("af-list", 7, LY_STMT_LEAF_LIST)
+ else IF_KW("af", 2, LY_STMT_LEAF)
+ else IF_KW("ngth", 4, LY_STMT_LENGTH)
+ IF_KW_PREFIX_END
+ else IF_KW("ist", 3, LY_STMT_LIST)
+ break;
+ case 'm':
+ MOVE_IN(1);
+ IF_KW_PREFIX("a", 1)
+ IF_KW("ndatory", 7, LY_STMT_MANDATORY)
+ else IF_KW("x-elements", 10, LY_STMT_MAX_ELEMENTS)
+ IF_KW_PREFIX_END
+ else IF_KW("in-elements", 11, LY_STMT_MIN_ELEMENTS)
+ else IF_KW("ust", 3, LY_STMT_MUST)
+ else IF_KW_PREFIX("od", 2)
+ IF_KW("ule", 3, LY_STMT_MODULE)
+ else IF_KW("ifier", 5, LY_STMT_MODIFIER)
+ IF_KW_PREFIX_END
+ break;
+ case 'n':
+ MOVE_IN(1);
+ IF_KW("amespace", 8, LY_STMT_NAMESPACE)
+ else IF_KW("otification", 11, LY_STMT_NOTIFICATION)
+ break;
+ case 'o':
+ MOVE_IN(1);
+ IF_KW_PREFIX("r", 1)
+ IF_KW("dered-by", 8, LY_STMT_ORDERED_BY)
+ else IF_KW("ganization", 10, LY_STMT_ORGANIZATION)
+ IF_KW_PREFIX_END
+ else IF_KW("utput", 5, LY_STMT_OUTPUT)
+ break;
+ case 'p':
+ MOVE_IN(1);
+ IF_KW("ath", 3, LY_STMT_PATH)
+ else IF_KW("attern", 6, LY_STMT_PATTERN)
+ else IF_KW("osition", 7, LY_STMT_POSITION)
+ else IF_KW_PREFIX("re", 2)
+ IF_KW("fix", 3, LY_STMT_PREFIX)
+ else IF_KW("sence", 5, LY_STMT_PRESENCE)
+ IF_KW_PREFIX_END
+ break;
+ case 'r':
+ MOVE_IN(1);
+ IF_KW("ange", 4, LY_STMT_RANGE)
+ else IF_KW_PREFIX("e", 1)
+ IF_KW_PREFIX("f", 1)
+ IF_KW("erence", 6, LY_STMT_REFERENCE)
+ else IF_KW("ine", 3, LY_STMT_REFINE)
+ IF_KW_PREFIX_END
+ else IF_KW("quire-instance", 14, LY_STMT_REQUIRE_INSTANCE)
+ else IF_KW("vision-date", 11, LY_STMT_REVISION_DATE)
+ else IF_KW("vision", 6, LY_STMT_REVISION)
+ IF_KW_PREFIX_END
+ else IF_KW("pc", 2, LY_STMT_RPC)
+ break;
+ case 's':
+ MOVE_IN(1);
+ IF_KW("tatus", 5, LY_STMT_STATUS)
+ else IF_KW("ubmodule", 8, LY_STMT_SUBMODULE)
+ break;
+ case 't':
+ MOVE_IN(1);
+ IF_KW("ypedef", 6, LY_STMT_TYPEDEF)
+ else IF_KW("ype", 3, LY_STMT_TYPE)
+ break;
+ case 'u':
+ MOVE_IN(1);
+ IF_KW_PREFIX("ni", 2)
+ IF_KW("que", 3, LY_STMT_UNIQUE)
+ else IF_KW("ts", 2, LY_STMT_UNITS)
+ IF_KW_PREFIX_END
+ else IF_KW("ses", 3, LY_STMT_USES)
+ break;
+ case 'v':
+ MOVE_IN(1);
+ IF_KW("alue", 4, LY_STMT_VALUE)
+ break;
+ case 'w':
+ MOVE_IN(1);
+ IF_KW("hen", 3, LY_STMT_WHEN)
+ break;
+ case 'y':
+ MOVE_IN(1);
+ IF_KW("ang-version", 11, LY_STMT_YANG_VERSION)
+ else IF_KW("in-element", 10, LY_STMT_YIN_ELEMENT)
+ break;
+ default:
+ /* if indent is not NULL we are matching keyword from YANG data */
+ if (indent) {
+ if (in->current[0] == ';') {
+ MOVE_IN(1);
+ *kw = LY_STMT_SYNTAX_SEMICOLON;
+ } else if (in->current[0] == '{') {
+ MOVE_IN(1);
+ *kw = LY_STMT_SYNTAX_LEFT_BRACE;
+ } else if (in->current[0] == '}') {
+ MOVE_IN(1);
+ *kw = LY_STMT_SYNTAX_RIGHT_BRACE;
+ }
+ }
+ break;
+ }
+
+ if ((*kw < LY_STMT_SYNTAX_SEMICOLON) && isalnum(in->current[0])) {
+ /* the keyword is not terminated */
+ *kw = LY_STMT_NONE;
+ in->current = start;
+ }
+
+#undef IF_KW
+#undef IF_KW_PREFIX
+#undef IF_KW_PREFIX_END
+#undef MOVE_IN
+ /* *INDENT-ON* */
+
+ return result;
+}
+
+LY_ERR
+lysp_ext_find_definition(const struct ly_ctx *ctx, const struct lysp_ext_instance *ext, const struct lys_module **ext_mod,
+ struct lysp_ext **ext_def)
+{
+ const char *tmp, *name, *prefix;
+ size_t pref_len, name_len;
+ LY_ARRAY_COUNT_TYPE u, v;
+ const struct lys_module *mod = NULL;
+ const struct lysp_submodule *submod;
+
+ if (ext_def) {
+ *ext_def = NULL;
+ }
+
+ /* parse the prefix, the nodeid was previously already parsed and checked */
+ tmp = ext->name;
+ ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len);
+
+ /* get module where the extension definition should be placed */
+ *ext_mod = mod = ly_resolve_prefix(ctx, prefix, pref_len, ext->format, ext->prefix_data);
+ if (!mod) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Invalid prefix \"%.*s\" used for extension instance identifier.", (int)pref_len, prefix);
+ return LY_EVALID;
+ } else if (!mod->parsed->extensions) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Extension instance \"%s\" refers \"%s\" module that does not contain extension definitions.",
+ ext->name, mod->name);
+ return LY_EVALID;
+ }
+
+ if (!ext_def) {
+ /* we are done */
+ return LY_SUCCESS;
+ }
+
+ /* find the parsed extension definition there */
+ LY_ARRAY_FOR(mod->parsed->extensions, v) {
+ if (!strcmp(name, mod->parsed->extensions[v].name)) {
+ *ext_def = &mod->parsed->extensions[v];
+ break;
+ }
+ }
+ if (!*ext_def) {
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ submod = mod->parsed->includes[u].submodule;
+ LY_ARRAY_FOR(submod->extensions, v) {
+ if (!strcmp(name, submod->extensions[v].name)) {
+ *ext_def = &submod->extensions[v];
+ break;
+ }
+ }
+ }
+ }
+
+ if (!*ext_def) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Extension definition of extension instance \"%s\" not found.", ext->name);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_ext_instance_resolve_argument(struct ly_ctx *ctx, struct lysp_ext_instance *ext_p)
+{
+ assert(ext_p->def);
+
+ if (!ext_p->def->argname || ext_p->argument) {
+ /* nothing to do */
+ return LY_SUCCESS;
+ }
+
+ if (ext_p->format == LY_VALUE_XML) {
+ /* schema was parsed from YIN and an argument is expected, ... */
+ struct lysp_stmt *stmt = NULL;
+
+ if (ext_p->def->flags & LYS_YINELEM_TRUE) {
+ /* ... argument was the first XML child element */
+ for (stmt = ext_p->child; stmt && (stmt->flags & LYS_YIN_ATTR); stmt = stmt->next) {}
+ if (stmt) {
+ const char *arg, *ext, *name_arg, *name_ext, *prefix_arg, *prefix_ext;
+ size_t name_arg_len, name_ext_len, prefix_arg_len, prefix_ext_len;
+
+ stmt = ext_p->child;
+
+ arg = stmt->stmt;
+ ly_parse_nodeid(&arg, &prefix_arg, &prefix_arg_len, &name_arg, &name_arg_len);
+ if (ly_strncmp(ext_p->def->argname, name_arg, name_arg_len)) {
+ LOGVAL(ctx, LYVE_SEMANTICS, "Extension instance \"%s\" expects argument element \"%s\" as its first XML child, "
+ "but \"%.*s\" element found.", ext_p->name, ext_p->def->argname, (int)name_arg_len, name_arg);
+ return LY_EVALID;
+ }
+
+ /* check namespace - all the extension instances must be qualified and argument element is expected in the same
+ * namespace. Do not check just prefixes, there can be different prefixes pointing to the same namespace */
+ ext = ext_p->name; /* include prefix */
+ ly_parse_nodeid(&ext, &prefix_ext, &prefix_ext_len, &name_ext, &name_ext_len);
+
+ if (ly_resolve_prefix(ctx, prefix_ext, prefix_ext_len, ext_p->format, ext_p->prefix_data) !=
+ ly_resolve_prefix(ctx, prefix_arg, prefix_arg_len, stmt->format, stmt->prefix_data)) {
+ LOGVAL(ctx, LYVE_SEMANTICS, "Extension instance \"%s\" element and its argument element \"%s\" are "
+ "expected in the same namespace, but they differ.", ext_p->name, ext_p->def->argname);
+ return LY_EVALID;
+ }
+ }
+ } else {
+ /* ... argument was one of the XML attributes which are represented as child stmt with LYS_YIN_ATTR flag */
+ for (stmt = ext_p->child; stmt && (stmt->flags & LYS_YIN_ATTR); stmt = stmt->next) {
+ if (!strcmp(stmt->stmt, ext_p->def->argname)) {
+ /* this is the extension's argument */
+ break;
+ }
+ }
+ }
+
+ if (stmt) {
+ LY_CHECK_RET(lydict_insert(ctx, stmt->arg, 0, &ext_p->argument));
+ stmt->flags |= LYS_YIN_ARGUMENT;
+ }
+ }
+
+ if (!ext_p->argument) {
+ /* missing extension's argument */
+ LOGVAL(ctx, LYVE_SEMANTICS, "Extension instance \"%s\" missing argument %s\"%s\".",
+ ext_p->name, (ext_p->def->flags & LYS_YINELEM_TRUE) ? "element " : "", ext_p->def->argname);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ARRAY_COUNT_TYPE
+lysp_ext_instance_iter(struct lysp_ext_instance *ext, LY_ARRAY_COUNT_TYPE index, enum ly_stmt substmt)
+{
+ LY_CHECK_ARG_RET(NULL, ext, LY_EINVAL);
+
+ for ( ; index < LY_ARRAY_COUNT(ext); index++) {
+ if (ext[index].parent_stmt == substmt) {
+ return index;
+ }
+ }
+
+ return LY_ARRAY_COUNT(ext);
+}
+
+LIBYANG_API_DEF const struct lysc_node *
+lysc_data_node(const struct lysc_node *schema)
+{
+ const struct lysc_node *parent;
+
+ parent = schema;
+ while (parent && !(parent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_RPC |
+ LYS_ACTION | LYS_NOTIF))) {
+ parent = parent->parent;
+ }
+
+ return parent;
+}
+
+ly_bool
+lys_has_recompiled(const struct lys_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (LYSP_HAS_RECOMPILED(mod->parsed)) {
+ return 1;
+ }
+
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (LYSP_HAS_RECOMPILED(mod->parsed->includes[u].submodule)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+ly_bool
+lys_has_compiled(const struct lys_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (LYSP_HAS_COMPILED(mod->parsed)) {
+ return 1;
+ }
+
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (LYSP_HAS_COMPILED(mod->parsed->includes[u].submodule)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+ly_bool
+lys_has_dep_mods(const struct lys_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* features */
+ if (mod->parsed->features) {
+ return 1;
+ }
+
+ /* groupings */
+ if (mod->parsed->groupings) {
+ return 1;
+ }
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (mod->parsed->includes[u].submodule->groupings) {
+ return 1;
+ }
+ }
+
+ /* augments (adding nodes with leafrefs) */
+ if (mod->parsed->augments) {
+ return 1;
+ }
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (mod->parsed->includes[u].submodule->augments) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+const char *
+lys_stmt_str(enum ly_stmt stmt)
+{
+ switch (stmt) {
+ case LY_STMT_NONE:
+ return NULL;
+ case LY_STMT_ACTION:
+ return "action";
+ case LY_STMT_ANYDATA:
+ return "anydata";
+ case LY_STMT_ANYXML:
+ return "anyxml";
+ case LY_STMT_ARGUMENT:
+ return "argument";
+ case LY_STMT_ARG_TEXT:
+ return "text";
+ case LY_STMT_ARG_VALUE:
+ return "value";
+ case LY_STMT_AUGMENT:
+ return "augment";
+ case LY_STMT_BASE:
+ return "base";
+ case LY_STMT_BELONGS_TO:
+ return "belongs-to";
+ case LY_STMT_BIT:
+ return "bit";
+ case LY_STMT_CASE:
+ return "case";
+ case LY_STMT_CHOICE:
+ return "choice";
+ case LY_STMT_CONFIG:
+ return "config";
+ case LY_STMT_CONTACT:
+ return "contact";
+ case LY_STMT_CONTAINER:
+ return "container";
+ case LY_STMT_DEFAULT:
+ return "default";
+ case LY_STMT_DESCRIPTION:
+ return "description";
+ case LY_STMT_DEVIATE:
+ return "deviate";
+ case LY_STMT_DEVIATION:
+ return "deviation";
+ case LY_STMT_ENUM:
+ return "enum";
+ case LY_STMT_ERROR_APP_TAG:
+ return "error-app-tag";
+ case LY_STMT_ERROR_MESSAGE:
+ return "error-message";
+ case LY_STMT_EXTENSION:
+ return "extension";
+ case LY_STMT_EXTENSION_INSTANCE:
+ return NULL;
+ case LY_STMT_FEATURE:
+ return "feature";
+ case LY_STMT_FRACTION_DIGITS:
+ return "fraction-digits";
+ case LY_STMT_GROUPING:
+ return "grouping";
+ case LY_STMT_IDENTITY:
+ return "identity";
+ case LY_STMT_IF_FEATURE:
+ return "if-feature";
+ case LY_STMT_IMPORT:
+ return "import";
+ case LY_STMT_INCLUDE:
+ return "include";
+ case LY_STMT_INPUT:
+ return "input";
+ case LY_STMT_KEY:
+ return "key";
+ case LY_STMT_LEAF:
+ return "leaf";
+ case LY_STMT_LEAF_LIST:
+ return "leaf-list";
+ case LY_STMT_LENGTH:
+ return "length";
+ case LY_STMT_LIST:
+ return "list";
+ case LY_STMT_MANDATORY:
+ return "mandatory";
+ case LY_STMT_MAX_ELEMENTS:
+ return "max-elements";
+ case LY_STMT_MIN_ELEMENTS:
+ return "min-elements";
+ case LY_STMT_MODIFIER:
+ return "modifier";
+ case LY_STMT_MODULE:
+ return "module";
+ case LY_STMT_MUST:
+ return "must";
+ case LY_STMT_NAMESPACE:
+ return "namespace";
+ case LY_STMT_NOTIFICATION:
+ return "notification";
+ case LY_STMT_ORDERED_BY:
+ return "ordered-by";
+ case LY_STMT_ORGANIZATION:
+ return "organization";
+ case LY_STMT_OUTPUT:
+ return "output";
+ case LY_STMT_PATH:
+ return "path";
+ case LY_STMT_PATTERN:
+ return "pattern";
+ case LY_STMT_POSITION:
+ return "position";
+ case LY_STMT_PREFIX:
+ return "prefix";
+ case LY_STMT_PRESENCE:
+ return "presence";
+ case LY_STMT_RANGE:
+ return "range";
+ case LY_STMT_REFERENCE:
+ return "reference";
+ case LY_STMT_REFINE:
+ return "refine";
+ case LY_STMT_REQUIRE_INSTANCE:
+ return "require-instance";
+ case LY_STMT_REVISION:
+ return "revision";
+ case LY_STMT_REVISION_DATE:
+ return "revision-date";
+ case LY_STMT_RPC:
+ return "rpc";
+ case LY_STMT_STATUS:
+ return "status";
+ case LY_STMT_SUBMODULE:
+ return "submodule";
+ case LY_STMT_SYNTAX_LEFT_BRACE:
+ return "{";
+ case LY_STMT_SYNTAX_RIGHT_BRACE:
+ return "}";
+ case LY_STMT_SYNTAX_SEMICOLON:
+ return ";";
+ case LY_STMT_TYPE:
+ return "type";
+ case LY_STMT_TYPEDEF:
+ return "typedef";
+ case LY_STMT_UNIQUE:
+ return "unique";
+ case LY_STMT_UNITS:
+ return "units";
+ case LY_STMT_USES:
+ return "uses";
+ case LY_STMT_VALUE:
+ return "value";
+ case LY_STMT_WHEN:
+ return "when";
+ case LY_STMT_YANG_VERSION:
+ return "yang-version";
+ case LY_STMT_YIN_ELEMENT:
+ return "yin-element";
+ }
+
+ return NULL;
+}
+
+const char *
+lys_stmt_arg(enum ly_stmt stmt)
+{
+ switch (stmt) {
+ case LY_STMT_NONE:
+ case LY_STMT_ARG_TEXT:
+ case LY_STMT_ARG_VALUE:
+ case LY_STMT_EXTENSION_INSTANCE:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_SYNTAX_LEFT_BRACE:
+ case LY_STMT_SYNTAX_RIGHT_BRACE:
+ case LY_STMT_SYNTAX_SEMICOLON:
+ return NULL;
+ case LY_STMT_ACTION:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_BASE:
+ case LY_STMT_BIT:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_ENUM:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_MODULE:
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_RPC:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPE:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_UNITS:
+ case LY_STMT_USES:
+ return "name";
+ case LY_STMT_AUGMENT:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_REFINE:
+ return "target-node";
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ return "module";
+ case LY_STMT_CONFIG:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_KEY:
+ case LY_STMT_LENGTH:
+ case LY_STMT_MANDATORY:
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_ORDERED_BY:
+ case LY_STMT_PATH:
+ case LY_STMT_PATTERN:
+ case LY_STMT_POSITION:
+ case LY_STMT_PREFIX:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_RANGE:
+ case LY_STMT_REQUIRE_INSTANCE:
+ case LY_STMT_STATUS:
+ case LY_STMT_VALUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ return "value";
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_REFERENCE:
+ return "text";
+ case LY_STMT_MUST:
+ case LY_STMT_WHEN:
+ return "condition";
+ case LY_STMT_NAMESPACE:
+ return "uri";
+ case LY_STMT_REVISION:
+ case LY_STMT_REVISION_DATE:
+ return "date";
+ case LY_STMT_UNIQUE:
+ return "tag";
+ }
+
+ return NULL;
+}
+
+uint8_t
+lys_stmt_flags(enum ly_stmt stmt)
+{
+ switch (stmt) {
+ case LY_STMT_NONE:
+ case LY_STMT_ARG_TEXT:
+ case LY_STMT_ARG_VALUE:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_EXTENSION_INSTANCE:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_INPUT:
+ case LY_STMT_KEY:
+ case LY_STMT_LENGTH:
+ case LY_STMT_MUST:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_PATH:
+ case LY_STMT_PATTERN:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_RANGE:
+ case LY_STMT_SYNTAX_LEFT_BRACE:
+ case LY_STMT_SYNTAX_RIGHT_BRACE:
+ case LY_STMT_SYNTAX_SEMICOLON:
+ case LY_STMT_UNIQUE:
+ case LY_STMT_UNITS:
+ case LY_STMT_WHEN:
+ return 0;
+ case LY_STMT_ACTION:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_AUGMENT:
+ case LY_STMT_BASE:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_BIT:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONFIG:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_ENUM:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IDENTITY:
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_MANDATORY:
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_MODULE:
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_ORDERED_BY:
+ case LY_STMT_POSITION:
+ case LY_STMT_PREFIX:
+ case LY_STMT_REFINE:
+ case LY_STMT_REQUIRE_INSTANCE:
+ case LY_STMT_REVISION:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_RPC:
+ case LY_STMT_STATUS:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPE:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_USES:
+ case LY_STMT_VALUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ return LY_STMT_FLAG_ID;
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_REFERENCE:
+ return LY_STMT_FLAG_YIN;
+ }
+
+ return 0;
+}
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
new file mode 100644
index 0000000..9eedecf
--- /dev/null
+++ b/src/tree_schema_free.c
@@ -0,0 +1,1737 @@
+/**
+ * @file tree_schema_free.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Freeing functions for schema tree structures.
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "tree_schema_free.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "compat.h"
+#include "dict.h"
+#include "log.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+#include "xpath.h"
+
+static void lysc_node_free_(struct lysf_ctx *ctx, struct lysc_node *node);
+
+void
+lysp_qname_free(const struct ly_ctx *ctx, struct lysp_qname *qname)
+{
+ if (qname) {
+ lydict_remove(ctx, qname->str);
+ }
+}
+
+/**
+ * @brief Free the parsed generic statement structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] grp Parsed schema statement structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt)
+{
+ struct lysp_stmt *child, *next;
+
+ lydict_remove(ctx, stmt->stmt);
+ lydict_remove(ctx, stmt->arg);
+ ly_free_prefix_data(stmt->format, stmt->prefix_data);
+
+ LY_LIST_FOR_SAFE(stmt->child, next, child) {
+ lysp_stmt_free(ctx, child);
+ }
+
+ free(stmt);
+}
+
+void
+lysp_ext_instance_free(struct lysf_ctx *ctx, struct lysp_ext_instance *ext)
+{
+ struct lysp_stmt *stmt, *next;
+
+ lydict_remove(ctx->ctx, ext->name);
+ lydict_remove(ctx->ctx, ext->argument);
+ ly_free_prefix_data(ext->format, ext->prefix_data);
+ if (ext->record && ext->record->plugin.pfree) {
+ ext->record->plugin.pfree(ctx->ctx, ext);
+ }
+
+ LY_LIST_FOR_SAFE(ext->child, next, stmt) {
+ lysp_stmt_free(ctx->ctx, stmt);
+ }
+}
+
+/**
+ * @brief Free the parsed import structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] import Parsed schema import structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_import_free(struct lysf_ctx *ctx, struct lysp_import *import)
+{
+ /* imported module is freed directly from the context's list */
+ lydict_remove(ctx->ctx, import->name);
+ lydict_remove(ctx->ctx, import->prefix);
+ lydict_remove(ctx->ctx, import->dsc);
+ lydict_remove(ctx->ctx, import->ref);
+ FREE_ARRAY(ctx, import->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Common function to erase include record in main module and submodule.
+ *
+ * There is a difference since the main module is expected to have the complete list if the included submodules and
+ * the parsed submodule is shared with any include in a submodule. Therefore, the referenced submodules in the include
+ * record are freed only from main module's records.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] include The include record to be erased, the record itself is not freed.
+ * @param[in] main_module Flag to get know if the include record is placed in main module so also the referenced submodule
+ * is supposed to be freed.
+ */
+static void
+lysp_include_free_(struct lysf_ctx *ctx, struct lysp_include *include, ly_bool main_module)
+{
+ if (main_module && include->submodule) {
+ lysp_module_free(ctx, (struct lysp_module *)include->submodule);
+ }
+ lydict_remove(ctx->ctx, include->name);
+ lydict_remove(ctx->ctx, include->dsc);
+ lydict_remove(ctx->ctx, include->ref);
+ FREE_ARRAY(ctx, include->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Free the parsed include structure of a submodule.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] include Parsed schema include structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_include_free_submodule(struct lysf_ctx *ctx, struct lysp_include *include)
+{
+ lysp_include_free_(ctx, include, 0);
+}
+
+/**
+ * @brief Free the parsed include structure of a module.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] include Parsed schema include structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_include_free(struct lysf_ctx *ctx, struct lysp_include *include)
+{
+ lysp_include_free_(ctx, include, 1);
+}
+
+/**
+ * @brief Free the parsed revision structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] rev Parsed schema revision structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_revision_free(struct lysf_ctx *ctx, struct lysp_revision *rev)
+{
+ lydict_remove(ctx->ctx, rev->dsc);
+ lydict_remove(ctx->ctx, rev->ref);
+ FREE_ARRAY(ctx, rev->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Free the compiled extension definition and NULL the provided pointer.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] ext Compiled extension definition to be freed.
+ */
+static void
+lysc_extension_free(struct lysf_ctx *ctx, struct lysc_ext **ext)
+{
+ if (ly_set_contains(&ctx->ext_set, *ext, NULL)) {
+ /* already freed and only referenced again in this module */
+ return;
+ }
+
+ /* remember this extension to be freed, nothing to do on error */
+ (void)ly_set_add(&ctx->ext_set, *ext, 0, NULL);
+
+ /* recursive exts free */
+ FREE_ARRAY(ctx, (*ext)->exts, lysc_ext_instance_free);
+
+ *ext = NULL;
+}
+
+/**
+ * @brief Free the parsed ext structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] ext Parsed schema ext structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_ext_free(struct lysf_ctx *ctx, struct lysp_ext *ext)
+{
+ lydict_remove(ctx->ctx, ext->name);
+ lydict_remove(ctx->ctx, ext->argname);
+ lydict_remove(ctx->ctx, ext->dsc);
+ lydict_remove(ctx->ctx, ext->ref);
+ FREE_ARRAY(ctx, ext->exts, lysp_ext_instance_free);
+ if (ext->compiled) {
+ lysc_extension_free(ctx, &ext->compiled);
+ }
+}
+
+/**
+ * @brief Free the parsed feature structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] feat Parsed schema feature structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_feature_free(struct lysf_ctx *ctx, struct lysp_feature *feat)
+{
+ lydict_remove(ctx->ctx, feat->name);
+ FREE_ARRAY(ctx->ctx, feat->iffeatures, lysp_qname_free);
+ FREE_ARRAY(ctx, feat->iffeatures_c, lysc_iffeature_free);
+ LY_ARRAY_FREE(feat->depfeatures);
+ lydict_remove(ctx->ctx, feat->dsc);
+ lydict_remove(ctx->ctx, feat->ref);
+ FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Free the parsed identity structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] ident Parsed schema identity structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_ident_free(struct lysf_ctx *ctx, struct lysp_ident *ident)
+{
+ lydict_remove(ctx->ctx, ident->name);
+ FREE_ARRAY(ctx->ctx, ident->iffeatures, lysp_qname_free);
+ FREE_STRINGS(ctx->ctx, ident->bases);
+ lydict_remove(ctx->ctx, ident->dsc);
+ lydict_remove(ctx->ctx, ident->ref);
+ FREE_ARRAY(ctx, ident->exts, lysp_ext_instance_free);
+}
+
+void
+lysp_restr_free(struct lysf_ctx *ctx, struct lysp_restr *restr)
+{
+ lydict_remove(ctx->ctx, restr->arg.str);
+ lydict_remove(ctx->ctx, restr->emsg);
+ lydict_remove(ctx->ctx, restr->eapptag);
+ lydict_remove(ctx->ctx, restr->dsc);
+ lydict_remove(ctx->ctx, restr->ref);
+ FREE_ARRAY(ctx, restr->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Free the parsed type enum item.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] item Parsed schema type enum item to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_type_enum_free(struct lysf_ctx *ctx, struct lysp_type_enum *item)
+{
+ lydict_remove(ctx->ctx, item->name);
+ lydict_remove(ctx->ctx, item->dsc);
+ lydict_remove(ctx->ctx, item->ref);
+ FREE_ARRAY(ctx->ctx, item->iffeatures, lysp_qname_free);
+ FREE_ARRAY(ctx, item->exts, lysp_ext_instance_free);
+}
+
+void
+lysp_type_free(struct lysf_ctx *ctx, struct lysp_type *type)
+{
+ if (!type) {
+ return;
+ }
+
+ lydict_remove(ctx->ctx, type->name);
+ FREE_MEMBER(ctx, type->range, lysp_restr_free);
+ FREE_MEMBER(ctx, type->length, lysp_restr_free);
+ FREE_ARRAY(ctx, type->patterns, lysp_restr_free);
+ FREE_ARRAY(ctx, type->enums, lysp_type_enum_free);
+ FREE_ARRAY(ctx, type->bits, lysp_type_enum_free);
+ lyxp_expr_free(ctx->ctx, type->path);
+ FREE_STRINGS(ctx->ctx, type->bases);
+ FREE_ARRAY(ctx, type->types, lysp_type_free);
+ FREE_ARRAY(ctx, type->exts, lysp_ext_instance_free);
+ if (type->compiled) {
+ lysc_type_free(ctx, type->compiled);
+ }
+}
+
+/**
+ * @brief Free the parsed typedef structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] tpdf Parsed schema typedef structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_tpdf_free(struct lysf_ctx *ctx, struct lysp_tpdf *tpdf)
+{
+ lydict_remove(ctx->ctx, tpdf->name);
+ lydict_remove(ctx->ctx, tpdf->units);
+ lydict_remove(ctx->ctx, tpdf->dflt.str);
+ lydict_remove(ctx->ctx, tpdf->dsc);
+ lydict_remove(ctx->ctx, tpdf->ref);
+ FREE_ARRAY(ctx, tpdf->exts, lysp_ext_instance_free);
+
+ lysp_type_free(ctx, &tpdf->type);
+
+}
+
+/**
+ * @brief Free the parsed grouping structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] grp Parsed schema grouping structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_grp_free(struct lysf_ctx *ctx, struct lysp_node_grp *grp)
+{
+ struct lysp_node *node, *next;
+
+ FREE_ARRAY(ctx, grp->typedefs, lysp_tpdf_free);
+ LY_LIST_FOR_SAFE((struct lysp_node *)grp->groupings, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE(grp->child, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)grp->actions, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)grp->notifs, next, node) {
+ lysp_node_free(ctx, node);
+ }
+}
+
+void
+lysp_when_free(struct lysf_ctx *ctx, struct lysp_when *when)
+{
+ lydict_remove(ctx->ctx, when->cond);
+ lydict_remove(ctx->ctx, when->dsc);
+ lydict_remove(ctx->ctx, when->ref);
+ FREE_ARRAY(ctx, when->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Free the parsed augment structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] aug Parsed schema augment structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_augment_free(struct lysf_ctx *ctx, struct lysp_node_augment *aug)
+{
+ struct lysp_node *node, *next;
+
+ LY_LIST_FOR_SAFE(aug->child, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)aug->actions, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)aug->notifs, next, node) {
+ lysp_node_free(ctx, node);
+ }
+}
+
+void
+lysp_deviate_free(struct lysf_ctx *ctx, struct lysp_deviate *d)
+{
+ struct lysp_deviate_add *add = (struct lysp_deviate_add *)d;
+ struct lysp_deviate_rpl *rpl = (struct lysp_deviate_rpl *)d;
+
+ if (!d) {
+ return;
+ }
+
+ FREE_ARRAY(ctx, d->exts, lysp_ext_instance_free);
+ switch (d->mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ /* nothing to do */
+ break;
+ case LYS_DEV_ADD:
+ case LYS_DEV_DELETE: /* compatible for dynamically allocated data */
+ lydict_remove(ctx->ctx, add->units);
+ FREE_ARRAY(ctx, add->musts, lysp_restr_free);
+ FREE_ARRAY(ctx->ctx, add->uniques, lysp_qname_free);
+ FREE_ARRAY(ctx->ctx, add->dflts, lysp_qname_free);
+ break;
+ case LYS_DEV_REPLACE:
+ FREE_MEMBER(ctx, rpl->type, lysp_type_free);
+ lydict_remove(ctx->ctx, rpl->units);
+ lysp_qname_free(ctx->ctx, &rpl->dflt);
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ break;
+ }
+}
+
+void
+lysp_deviation_free(struct lysf_ctx *ctx, struct lysp_deviation *dev)
+{
+ struct lysp_deviate *next, *iter;
+
+ lydict_remove(ctx->ctx, dev->nodeid);
+ lydict_remove(ctx->ctx, dev->dsc);
+ lydict_remove(ctx->ctx, dev->ref);
+ LY_LIST_FOR_SAFE(dev->deviates, next, iter) {
+ lysp_deviate_free(ctx, iter);
+ free(iter);
+ }
+ FREE_ARRAY(ctx, dev->exts, lysp_ext_instance_free);
+}
+
+/**
+ * @brief Free the parsed refine structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] ref Parsed schema refine structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysp_refine_free(struct lysf_ctx *ctx, struct lysp_refine *ref)
+{
+ lydict_remove(ctx->ctx, ref->nodeid);
+ lydict_remove(ctx->ctx, ref->dsc);
+ lydict_remove(ctx->ctx, ref->ref);
+ FREE_ARRAY(ctx->ctx, ref->iffeatures, lysp_qname_free);
+ FREE_ARRAY(ctx, ref->musts, lysp_restr_free);
+ lydict_remove(ctx->ctx, ref->presence);
+ FREE_ARRAY(ctx->ctx, ref->dflts, lysp_qname_free);
+ FREE_ARRAY(ctx, ref->exts, lysp_ext_instance_free);
+}
+
+void
+lysp_node_free(struct lysf_ctx *ctx, struct lysp_node *node)
+{
+ struct lysp_node *child, *next;
+ struct lysp_node_container *cont;
+ struct lysp_node_leaf *leaf;
+ struct lysp_node_leaflist *llist;
+ struct lysp_node_list *list;
+ struct lysp_node_choice *choice;
+ struct lysp_node_case *cas;
+ struct lysp_node_uses *uses;
+ struct lysp_node_action *act;
+ struct lysp_node_action_inout *inout;
+ struct lysp_node_notif *notif;
+ struct lysp_restr *musts = lysp_node_musts(node);
+ struct lysp_when *when = lysp_node_when(node);
+
+ lydict_remove(ctx->ctx, node->name);
+ lydict_remove(ctx->ctx, node->dsc);
+ lydict_remove(ctx->ctx, node->ref);
+ FREE_ARRAY(ctx->ctx, node->iffeatures, lysp_qname_free);
+ FREE_ARRAY(ctx, node->exts, lysp_ext_instance_free);
+
+ FREE_MEMBER(ctx, when, lysp_when_free);
+ FREE_ARRAY(ctx, musts, lysp_restr_free);
+
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ cont = (struct lysp_node_container *)node;
+
+ lydict_remove(ctx->ctx, cont->presence);
+ FREE_ARRAY(ctx, cont->typedefs, lysp_tpdf_free);
+ if (cont->groupings) {
+ LY_LIST_FOR_SAFE(&cont->groupings->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ LY_LIST_FOR_SAFE(cont->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ if (cont->actions) {
+ LY_LIST_FOR_SAFE(&cont->actions->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ if (cont->notifs) {
+ LY_LIST_FOR_SAFE(&cont->notifs->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ break;
+ case LYS_LEAF:
+ leaf = (struct lysp_node_leaf *)node;
+
+ lysp_type_free(ctx, &leaf->type);
+ lydict_remove(ctx->ctx, leaf->units);
+ lydict_remove(ctx->ctx, leaf->dflt.str);
+ break;
+ case LYS_LEAFLIST:
+ llist = (struct lysp_node_leaflist *)node;
+
+ lysp_type_free(ctx, &llist->type);
+ lydict_remove(ctx->ctx, llist->units);
+ FREE_ARRAY(ctx->ctx, llist->dflts, lysp_qname_free);
+ break;
+ case LYS_LIST:
+ list = (struct lysp_node_list *)node;
+
+ lydict_remove(ctx->ctx, list->key);
+ FREE_ARRAY(ctx, list->typedefs, lysp_tpdf_free);
+ if (list->groupings) {
+ LY_LIST_FOR_SAFE(&list->groupings->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ LY_LIST_FOR_SAFE(list->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ if (list->actions) {
+ LY_LIST_FOR_SAFE(&list->actions->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ if (list->notifs) {
+ LY_LIST_FOR_SAFE(&list->notifs->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ FREE_ARRAY(ctx->ctx, list->uniques, lysp_qname_free);
+ break;
+ case LYS_CHOICE:
+ choice = (struct lysp_node_choice *)node;
+
+ LY_LIST_FOR_SAFE(choice->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ lydict_remove(ctx->ctx, choice->dflt.str);
+ break;
+ case LYS_CASE:
+ cas = (struct lysp_node_case *)node;
+
+ LY_LIST_FOR_SAFE(cas->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ /* nothing special to do */
+ break;
+ case LYS_USES:
+ uses = (struct lysp_node_uses *)node;
+
+ FREE_ARRAY(ctx, uses->refines, lysp_refine_free);
+ if (uses->augments) {
+ LY_LIST_FOR_SAFE(&uses->augments->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ break;
+ case LYS_RPC:
+ case LYS_ACTION:
+ act = (struct lysp_node_action *)node;
+
+ FREE_ARRAY(ctx, act->typedefs, lysp_tpdf_free);
+ if (act->groupings) {
+ LY_LIST_FOR_SAFE(&act->groupings->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ if (act->input.nodetype) {
+ lysp_node_free(ctx, &act->input.node);
+ }
+ if (act->output.nodetype) {
+ lysp_node_free(ctx, &act->output.node);
+ }
+ break;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ inout = (struct lysp_node_action_inout *)node;
+
+ FREE_ARRAY(ctx, inout->typedefs, lysp_tpdf_free);
+ if (inout->groupings) {
+ LY_LIST_FOR_SAFE(&inout->groupings->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ LY_LIST_FOR_SAFE(inout->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ /* do not free the node, it is never standalone but part of the action node */
+ return;
+ case LYS_NOTIF:
+ notif = (struct lysp_node_notif *)node;
+
+ FREE_ARRAY(ctx, notif->typedefs, lysp_tpdf_free);
+ if (notif->groupings) {
+ LY_LIST_FOR_SAFE(&notif->groupings->node, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ }
+ LY_LIST_FOR_SAFE(notif->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ break;
+ case LYS_GROUPING:
+ lysp_grp_free(ctx, (struct lysp_node_grp *)node);
+ break;
+ case LYS_AUGMENT:
+ lysp_augment_free(ctx, ((struct lysp_node_augment *)node));
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ }
+
+ free(node);
+}
+
+void
+lysp_module_free(struct lysf_ctx *ctx, struct lysp_module *module)
+{
+ struct lysp_node *node, *next;
+
+ if (!module) {
+ return;
+ }
+
+ FREE_ARRAY(ctx, module->imports, lysp_import_free);
+ FREE_ARRAY(ctx, module->includes, module->is_submod ? lysp_include_free_submodule : lysp_include_free);
+
+ FREE_ARRAY(ctx, module->revs, lysp_revision_free);
+ FREE_ARRAY(ctx, module->extensions, lysp_ext_free);
+ FREE_ARRAY(ctx, module->features, lysp_feature_free);
+ FREE_ARRAY(ctx, module->identities, lysp_ident_free);
+ FREE_ARRAY(ctx, module->typedefs, lysp_tpdf_free);
+ LY_LIST_FOR_SAFE((struct lysp_node *)module->groupings, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE(module->data, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)module->augments, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)module->rpcs, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysp_node *)module->notifs, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ FREE_ARRAY(ctx, module->deviations, lysp_deviation_free);
+ FREE_ARRAY(ctx, module->exts, lysp_ext_instance_free);
+
+ if (module->is_submod) {
+ struct lysp_submodule *submod = (struct lysp_submodule *)module;
+
+ lydict_remove(ctx->ctx, submod->name);
+ lydict_remove(ctx->ctx, submod->filepath);
+ lydict_remove(ctx->ctx, submod->prefix);
+ lydict_remove(ctx->ctx, submod->org);
+ lydict_remove(ctx->ctx, submod->contact);
+ lydict_remove(ctx->ctx, submod->dsc);
+ lydict_remove(ctx->ctx, submod->ref);
+ }
+
+ free(module);
+}
+
+void
+lysc_ext_instance_free(struct lysf_ctx *ctx, struct lysc_ext_instance *ext)
+{
+ if (ext->def && ext->def->plugin && ext->def->plugin->cfree) {
+ ext->def->plugin->cfree(ctx->ctx, ext);
+ }
+ lydict_remove(ctx->ctx, ext->argument);
+ FREE_ARRAY(ctx, ext->exts, lysc_ext_instance_free);
+}
+
+void
+lysc_iffeature_free(struct lysf_ctx *UNUSED(ctx), struct lysc_iffeature *iff)
+{
+ LY_ARRAY_FREE(iff->features);
+ free(iff->expr);
+}
+
+/**
+ * @brief Free the compiled when structure (decrease refcount) and NULL the provided pointer.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] grp Parsed schema grouping structure to free. Note that the structure itself is not freed.
+ */
+static void
+lysc_when_free(struct lysf_ctx *ctx, struct lysc_when **w)
+{
+ if (--(*w)->refcount) {
+ return;
+ }
+ lyxp_expr_free(ctx->ctx, (*w)->cond);
+ ly_free_prefix_data(LY_VALUE_SCHEMA_RESOLVED, (*w)->prefixes);
+ lydict_remove(ctx->ctx, (*w)->dsc);
+ lydict_remove(ctx->ctx, (*w)->ref);
+ FREE_ARRAY(ctx, (*w)->exts, lysc_ext_instance_free);
+ free(*w);
+}
+
+/**
+ * @brief Free the compiled must structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] must Compiled must structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_must_free(struct lysf_ctx *ctx, struct lysc_must *must)
+{
+ if (!must) {
+ return;
+ }
+
+ lyxp_expr_free(ctx->ctx, must->cond);
+ ly_free_prefix_data(LY_VALUE_SCHEMA_RESOLVED, must->prefixes);
+ lydict_remove(ctx->ctx, must->emsg);
+ lydict_remove(ctx->ctx, must->eapptag);
+ lydict_remove(ctx->ctx, must->dsc);
+ lydict_remove(ctx->ctx, must->ref);
+ FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
+}
+
+/**
+ * @brief Unlink the identity from all derived identity arrays.
+ *
+ * @param[in] ident Identity to unlink.
+ */
+static void
+lysc_ident_derived_unlink(const struct lysc_ident *ident)
+{
+ LY_ARRAY_COUNT_TYPE u, v, w;
+ const struct lysp_submodule *submod;
+ const struct lysp_module *base_pmod = NULL;
+ const struct lysp_ident *identp = NULL;
+ const struct lys_module *mod, *iter;
+ const char *base_name;
+ uint32_t i;
+
+ /* find the parsed identity */
+ LY_ARRAY_FOR(ident->module->parsed->identities, u) {
+ if (ident->module->parsed->identities[u].name == ident->name) {
+ identp = &ident->module->parsed->identities[u];
+ base_pmod = ident->module->parsed;
+ break;
+ }
+ }
+ if (!identp) {
+ LY_ARRAY_FOR(ident->module->parsed->includes, v) {
+ submod = ident->module->parsed->includes[v].submodule;
+ LY_ARRAY_FOR(submod->identities, u) {
+ if (submod->identities[u].name == ident->name) {
+ identp = &submod->identities[u];
+ base_pmod = (struct lysp_module *)submod;
+ break;
+ }
+ }
+ }
+ }
+ assert(identp);
+
+ /* remove link from all the foreign bases, it may not be there if identity compilation failed */
+ LY_ARRAY_FOR(identp->bases, u) {
+ base_name = strchr(identp->bases[u], ':');
+ if (!base_name) {
+ continue;
+ }
+
+ /* prefixed identity */
+ mod = ly_resolve_prefix(ident->module->ctx, identp->bases[u], base_name - identp->bases[u], LY_VALUE_SCHEMA,
+ (void *)base_pmod);
+ if (!mod) {
+ continue;
+ }
+ ++base_name;
+
+ i = 0;
+ while ((iter = ly_ctx_get_module_iter(ident->module->ctx, &i))) {
+ if (iter == mod) {
+ break;
+ }
+ }
+ if (!iter) {
+ /* target module was freed already */
+ continue;
+ }
+
+ /* find the compiled base */
+ LY_ARRAY_FOR(mod->identities, v) {
+ if (!strcmp(mod->identities[v].name, base_name)) {
+ /* find the derived link */
+ LY_ARRAY_FOR(mod->identities[v].derived, w) {
+ if (mod->identities[v].derived[w] == ident) {
+ /* remove the link */
+ LY_ARRAY_DECREMENT(mod->identities[v].derived);
+ if (!LY_ARRAY_COUNT(mod->identities[v].derived)) {
+ LY_ARRAY_FREE(mod->identities[v].derived);
+ mod->identities[v].derived = NULL;
+ } else if (w < LY_ARRAY_COUNT(mod->identities[v].derived)) {
+ memmove(mod->identities[v].derived + w, mod->identities[v].derived + w + 1,
+ (LY_ARRAY_COUNT(mod->identities[v].derived) - w) * sizeof ident);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * @brief Free the compiled identity structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] ident Compiled identity structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_ident_free(struct lysf_ctx *ctx, struct lysc_ident *ident)
+{
+ lydict_remove(ctx->ctx, ident->name);
+ lydict_remove(ctx->ctx, ident->dsc);
+ lydict_remove(ctx->ctx, ident->ref);
+ LY_ARRAY_FREE(ident->derived);
+ 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
+lysc_range_free(struct lysf_ctx *ctx, struct lysc_range *range)
+{
+ LY_ARRAY_FREE(range->parts);
+ lydict_remove(ctx->ctx, range->eapptag);
+ lydict_remove(ctx->ctx, range->emsg);
+ lydict_remove(ctx->ctx, range->dsc);
+ lydict_remove(ctx->ctx, range->ref);
+ FREE_ARRAY(ctx, range->exts, lysc_ext_instance_free);
+}
+
+void
+lysc_pattern_free(struct lysf_ctx *ctx, struct lysc_pattern **pattern)
+{
+ if (--(*pattern)->refcount) {
+ return;
+ }
+ pcre2_code_free((*pattern)->code);
+ lydict_remove(ctx->ctx, (*pattern)->expr);
+ lydict_remove(ctx->ctx, (*pattern)->eapptag);
+ lydict_remove(ctx->ctx, (*pattern)->emsg);
+ lydict_remove(ctx->ctx, (*pattern)->dsc);
+ lydict_remove(ctx->ctx, (*pattern)->ref);
+ FREE_ARRAY(ctx, (*pattern)->exts, lysc_ext_instance_free);
+ free(*pattern);
+}
+
+void
+lysc_enum_item_free(struct lysf_ctx *ctx, struct lysc_type_bitenum_item *item)
+{
+ lydict_remove(ctx->ctx, item->name);
+ lydict_remove(ctx->ctx, item->dsc);
+ lydict_remove(ctx->ctx, item->ref);
+ FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
+}
+
+/**
+ * @brief Free the compiled type structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] type Pointer to compiled type structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_type2_free(struct lysf_ctx *ctx, struct lysc_type **type)
+{
+ lysc_type_free(ctx, *type);
+}
+
+void
+lysc_type_free(struct lysf_ctx *ctx, struct lysc_type *type)
+{
+ if (!type || (LY_ATOMIC_DEC_BARRIER(type->refcount) > 1)) {
+ return;
+ }
+
+ switch (type->basetype) {
+ case LY_TYPE_BINARY:
+ FREE_MEMBER(ctx, ((struct lysc_type_bin *)type)->length, lysc_range_free);
+ break;
+ case LY_TYPE_BITS:
+ FREE_ARRAY(ctx, (struct lysc_type_bitenum_item *)((struct lysc_type_bits *)type)->bits, lysc_enum_item_free);
+ break;
+ case LY_TYPE_DEC64:
+ FREE_MEMBER(ctx, ((struct lysc_type_dec *)type)->range, lysc_range_free);
+ break;
+ case LY_TYPE_STRING:
+ FREE_MEMBER(ctx, ((struct lysc_type_str *)type)->length, lysc_range_free);
+ FREE_ARRAY(ctx, ((struct lysc_type_str *)type)->patterns, lysc_pattern_free);
+ break;
+ case LY_TYPE_ENUM:
+ FREE_ARRAY(ctx, ((struct lysc_type_enum *)type)->enums, lysc_enum_item_free);
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ FREE_MEMBER(ctx, ((struct lysc_type_num *)type)->range, lysc_range_free);
+ break;
+ case LY_TYPE_IDENT:
+ LY_ARRAY_FREE(((struct lysc_type_identityref *)type)->bases);
+ break;
+ case LY_TYPE_UNION:
+ FREE_ARRAY(ctx, ((struct lysc_type_union *)type)->types, lysc_type2_free);
+ break;
+ case LY_TYPE_LEAFREF:
+ lyxp_expr_free(ctx->ctx, ((struct lysc_type_leafref *)type)->path);
+ ly_free_prefix_data(LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes);
+ lysc_type_free(ctx, ((struct lysc_type_leafref *)type)->realtype);
+ break;
+ case LY_TYPE_INST:
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ case LY_TYPE_UNKNOWN:
+ /* nothing to do */
+ break;
+ }
+
+ FREE_ARRAY(ctx, type->exts, lysc_ext_instance_free);
+ free(type);
+}
+
+/**
+ * @brief Free the compiled input/output structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] inout Compiled inout structure to be freed.
+ * Since the structure is part of the RPC/action structure, it is not freed itself.
+ */
+static void
+lysc_node_action_inout_free(struct lysf_ctx *ctx, struct lysc_node_action_inout *inout)
+{
+ struct lysc_node *child, *child_next;
+
+ FREE_ARRAY(ctx, inout->musts, lysc_must_free);
+ LY_LIST_FOR_SAFE(inout->child, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+}
+
+/**
+ * @brief Free the compiled RPC/action structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] action Compiled RPC/action structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_action_free(struct lysf_ctx *ctx, struct lysc_node_action *action)
+{
+ FREE_ARRAY(ctx, action->when, lysc_when_free);
+ if (action->input.nodetype) {
+ lysc_node_free_(ctx, &action->input.node);
+ }
+ if (action->output.nodetype) {
+ lysc_node_free_(ctx, &action->output.node);
+ }
+}
+
+/**
+ * @brief Free the compiled notification structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] notif Compiled notification structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_notif_free(struct lysf_ctx *ctx, struct lysc_node_notif *notif)
+{
+ struct lysc_node *child, *child_next;
+
+ FREE_ARRAY(ctx, notif->when, lysc_when_free);
+ FREE_ARRAY(ctx, notif->musts, lysc_must_free);
+ LY_LIST_FOR_SAFE(notif->child, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+}
+
+void
+lysc_node_container_free(struct lysf_ctx *ctx, struct lysc_node_container *node)
+{
+ struct lysc_node *child, *child_next;
+
+ LY_LIST_FOR_SAFE(node->child, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+ LY_LIST_FOR_SAFE((struct lysc_node *)node->actions, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+ LY_LIST_FOR_SAFE((struct lysc_node *)node->notifs, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+}
+
+/**
+ * @brief Free the compiled leaf structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled leaf structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_leaf_free(struct lysf_ctx *ctx, struct lysc_node_leaf *node)
+{
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+ if (node->type) {
+ lysc_type_free(ctx, node->type);
+ }
+ lydict_remove(ctx->ctx, node->units);
+ if (node->dflt) {
+ node->dflt->realtype->plugin->free(ctx->ctx, node->dflt);
+ lysc_type_free(ctx, (struct lysc_type *)node->dflt->realtype);
+ free(node->dflt);
+ }
+}
+
+/**
+ * @brief Free the compiled leaflist structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled leaflist structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_leaflist_free(struct lysf_ctx *ctx, struct lysc_node_leaflist *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+ if (node->type) {
+ lysc_type_free(ctx, node->type);
+ }
+ lydict_remove(ctx->ctx, node->units);
+ LY_ARRAY_FOR(node->dflts, u) {
+ node->dflts[u]->realtype->plugin->free(ctx->ctx, node->dflts[u]);
+ lysc_type_free(ctx, (struct lysc_type *)node->dflts[u]->realtype);
+ free(node->dflts[u]);
+ }
+ LY_ARRAY_FREE(node->dflts);
+}
+
+/**
+ * @brief Free the compiled list structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled list structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_list_free(struct lysf_ctx *ctx, struct lysc_node_list *node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_node *child, *child_next;
+
+ LY_LIST_FOR_SAFE(node->child, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+ LY_ARRAY_FOR(node->uniques, u) {
+ LY_ARRAY_FREE(node->uniques[u]);
+ }
+ LY_ARRAY_FREE(node->uniques);
+
+ LY_LIST_FOR_SAFE((struct lysc_node *)node->actions, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+ LY_LIST_FOR_SAFE((struct lysc_node *)node->notifs, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+}
+
+/**
+ * @brief Free the compiled choice structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled choice structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_choice_free(struct lysf_ctx *ctx, struct lysc_node_choice *node)
+{
+ struct lysc_node *child, *child_next;
+
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ LY_LIST_FOR_SAFE((struct lysc_node *)node->cases, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+}
+
+/**
+ * @brief Free the compiled case structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled case structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_case_free(struct lysf_ctx *ctx, struct lysc_node_case *node)
+{
+ struct lysc_node *child, *child_next;
+
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ LY_LIST_FOR_SAFE(node->child, child_next, child) {
+ lysc_node_free_(ctx, child);
+ }
+}
+
+/**
+ * @brief Free the compiled anyxml/anydata structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled anyxml/anydata structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_anydata_free(struct lysf_ctx *ctx, struct lysc_node_anydata *node)
+{
+ FREE_ARRAY(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+}
+
+/**
+ * @brief Free the compiled node structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled node structure to be freed.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+static void
+lysc_node_free_(struct lysf_ctx *ctx, struct lysc_node *node)
+{
+ ly_bool inout = 0;
+
+ /* common part */
+ lydict_remove(ctx->ctx, node->name);
+ lydict_remove(ctx->ctx, node->dsc);
+ lydict_remove(ctx->ctx, node->ref);
+
+ /* nodetype-specific part */
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ lysc_node_container_free(ctx, (struct lysc_node_container *)node);
+ break;
+ case LYS_LEAF:
+ lysc_node_leaf_free(ctx, (struct lysc_node_leaf *)node);
+ break;
+ case LYS_LEAFLIST:
+ lysc_node_leaflist_free(ctx, (struct lysc_node_leaflist *)node);
+ break;
+ case LYS_LIST:
+ lysc_node_list_free(ctx, (struct lysc_node_list *)node);
+ break;
+ case LYS_CHOICE:
+ lysc_node_choice_free(ctx, (struct lysc_node_choice *)node);
+ break;
+ case LYS_CASE:
+ lysc_node_case_free(ctx, (struct lysc_node_case *)node);
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ lysc_node_anydata_free(ctx, (struct lysc_node_anydata *)node);
+ break;
+ case LYS_RPC:
+ case LYS_ACTION:
+ lysc_node_action_free(ctx, (struct lysc_node_action *)node);
+ break;
+ case LYS_INPUT:
+ case LYS_OUTPUT:
+ lysc_node_action_inout_free(ctx, (struct lysc_node_action_inout *)node);
+ inout = 1;
+ break;
+ case LYS_NOTIF:
+ lysc_node_notif_free(ctx, (struct lysc_node_notif *)node);
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ }
+
+ FREE_ARRAY(ctx, node->exts, lysc_ext_instance_free);
+
+ if (!inout) {
+ free(node);
+ }
+}
+
+void
+lysc_node_free(struct lysf_ctx *ctx, struct lysc_node *node, ly_bool unlink)
+{
+ struct lysc_node *next, *iter, **child_p;
+
+ if (node->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ /* inouts are part of actions and cannot be unlinked/freed separately, we can only free all the children */
+ struct lysc_node_action_inout *inout = (struct lysc_node_action_inout *)node;
+
+ LY_LIST_FOR_SAFE(inout->child, next, iter) {
+ lysc_node_free_(ctx, iter);
+ }
+ inout->child = NULL;
+ return;
+ }
+
+ if (unlink) {
+ /* unlink from siblings */
+ if (node->prev->next) {
+ node->prev->next = node->next;
+ }
+ if (node->next) {
+ node->next->prev = node->prev;
+ } else {
+ /* unlinking the last node */
+ if (node->parent) {
+ if (node->nodetype == LYS_ACTION) {
+ iter = (struct lysc_node *)lysc_node_actions(node->parent);
+ } else if (node->nodetype == LYS_NOTIF) {
+ iter = (struct lysc_node *)lysc_node_notifs(node->parent);
+ } else {
+ iter = (struct lysc_node *)lysc_node_child(node->parent);
+ }
+ LY_CHECK_ERR_RET(!iter, LOGINT(ctx->ctx), );
+ } else if (node->nodetype == LYS_RPC) {
+ iter = (struct lysc_node *)node->module->compiled->rpcs;
+ } else if (node->nodetype == LYS_NOTIF) {
+ iter = (struct lysc_node *)node->module->compiled->notifs;
+ } else {
+ iter = node->module->compiled->data;
+ }
+ /* update the "last" pointer from the first node */
+ iter->prev = node->prev;
+ }
+
+ /* unlink from parent */
+ if (node->parent) {
+ if (node->nodetype == LYS_ACTION) {
+ child_p = (struct lysc_node **)lysc_node_actions_p(node->parent);
+ } else if (node->nodetype == LYS_NOTIF) {
+ child_p = (struct lysc_node **)lysc_node_notifs_p(node->parent);
+ } else {
+ child_p = lysc_node_child_p(node->parent);
+ }
+ } else if (node->nodetype == LYS_RPC) {
+ child_p = (struct lysc_node **)&node->module->compiled->rpcs;
+ } else if (node->nodetype == LYS_NOTIF) {
+ child_p = (struct lysc_node **)&node->module->compiled->notifs;
+ } else {
+ child_p = &node->module->compiled->data;
+ }
+ if (child_p && (*child_p == node)) {
+ /* the node is the first child */
+ *child_p = node->next;
+ }
+ }
+
+ lysc_node_free_(ctx, node);
+}
+
+void
+lysc_module_free(struct lysf_ctx *ctx, struct lysc_module *module)
+{
+ struct lysc_node *node, *node_next;
+
+ if (!module) {
+ return;
+ }
+
+ LY_LIST_FOR_SAFE(module->data, node_next, node) {
+ lysc_node_free_(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysc_node *)module->rpcs, node_next, node) {
+ lysc_node_free_(ctx, node);
+ }
+ LY_LIST_FOR_SAFE((struct lysc_node *)module->notifs, node_next, node) {
+ lysc_node_free_(ctx, node);
+ }
+ FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
+
+ free(module);
+}
+
+void
+lys_module_free(struct lysf_ctx *ctx, struct lys_module *module, ly_bool remove_links)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!module) {
+ return;
+ }
+
+ assert(!module->implemented);
+ assert(!module->compiled);
+
+ if (remove_links) {
+ /* remove derived identity links */
+ LY_ARRAY_FOR(module->identities, u) {
+ lysc_ident_derived_unlink(&module->identities[u]);
+ }
+ }
+ FREE_ARRAY(ctx, module->identities, lysc_ident_free);
+ lysp_module_free(ctx, module->parsed);
+
+ LY_ARRAY_FREE(module->augmented_by);
+ LY_ARRAY_FREE(module->deviated_by);
+
+ lydict_remove(ctx->ctx, module->name);
+ lydict_remove(ctx->ctx, module->revision);
+ lydict_remove(ctx->ctx, module->ns);
+ lydict_remove(ctx->ctx, module->prefix);
+ lydict_remove(ctx->ctx, module->filepath);
+ lydict_remove(ctx->ctx, module->org);
+ lydict_remove(ctx->ctx, module->contact);
+ lydict_remove(ctx->ctx, module->dsc);
+ lydict_remove(ctx->ctx, module->ref);
+
+ free(module);
+}
+
+void
+lysf_ctx_erase(struct lysf_ctx *ctx)
+{
+ struct lysc_ext *ext;
+ uint32_t i;
+
+ for (i = 0; i < ctx->ext_set.count; ++i) {
+ ext = ctx->ext_set.objs[i];
+
+ lydict_remove(ctx->ctx, ext->name);
+ lydict_remove(ctx->ctx, ext->argname);
+ free(ext);
+ }
+ ly_set_erase(&ctx->ext_set, NULL);
+}
+
+LIBYANG_API_DEF void
+lyplg_ext_pfree_instance_substatements(const struct ly_ctx *ctx, struct lysp_ext_substmt *substmts)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysf_ctx fctx = {.ctx = (struct ly_ctx *)ctx};
+ ly_bool node_free;
+
+ LY_ARRAY_FOR(substmts, u) {
+ if (!substmts[u].storage) {
+ continue;
+ }
+
+ switch (substmts[u].stmt) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_AUGMENT:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_GROUPING:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST:
+ case LY_STMT_USES: {
+ struct lysp_node *child, *child_next;
+
+ LY_LIST_FOR_SAFE(*((struct lysp_node **)substmts[u].storage), child_next, child) {
+ node_free = (child->nodetype & (LYS_INPUT | LYS_OUTPUT)) ? 1 : 0;
+ lysp_node_free(&fctx, child);
+ if (node_free) {
+ free(child);
+ }
+ }
+ *((struct lysc_node **)substmts[u].storage) = NULL;
+ break;
+ }
+ case LY_STMT_BASE:
+ /* multiple strings */
+ FREE_ARRAY(ctx, **(const char ***)substmts[u].storage, lydict_remove);
+ break;
+
+ case LY_STMT_BIT:
+ case LY_STMT_ENUM:
+ /* single enum */
+ lysp_type_enum_free(&fctx, *(struct lysp_type_enum **)substmts[u].storage);
+ break;
+
+ case LY_STMT_DEVIATE:
+ /* single deviate */
+ lysp_deviate_free(&fctx, *(struct lysp_deviate **)substmts[u].storage);
+ break;
+
+ case LY_STMT_DEVIATION:
+ /* single deviation */
+ lysp_deviation_free(&fctx, *(struct lysp_deviation **)substmts[u].storage);
+ break;
+
+ case LY_STMT_EXTENSION:
+ /* single extension */
+ lysp_ext_free(&fctx, *(struct lysp_ext **)substmts[u].storage);
+ break;
+
+ case LY_STMT_EXTENSION_INSTANCE:
+ /* multiple extension instances */
+ FREE_ARRAY(&fctx, *(struct lysp_ext_instance **)substmts[u].storage, lysp_ext_instance_free);
+ break;
+
+ case LY_STMT_FEATURE:
+ /* multiple features */
+ FREE_ARRAY(&fctx, *(struct lysp_feature **)substmts[u].storage, lysp_feature_free);
+ break;
+
+ case LY_STMT_IDENTITY:
+ /* multiple identities */
+ FREE_ARRAY(&fctx, *(struct lysp_ident **)substmts[u].storage, lysp_ident_free);
+ break;
+
+ case LY_STMT_IMPORT:
+ /* multiple imports */
+ FREE_ARRAY(&fctx, *(struct lysp_import **)substmts[u].storage, lysp_import_free);
+ break;
+
+ case LY_STMT_INCLUDE:
+ /* multiple includes */
+ FREE_ARRAY(&fctx, *(struct lysp_include **)substmts[u].storage, lysp_include_free);
+ break;
+
+ case LY_STMT_REFINE:
+ /* multiple refines */
+ FREE_ARRAY(&fctx, *(struct lysp_refine **)substmts[u].storage, lysp_refine_free);
+ break;
+
+ case LY_STMT_REVISION:
+ /* multiple revisions */
+ FREE_ARRAY(&fctx, *(struct lysp_revision **)substmts[u].storage, lysp_revision_free);
+ break;
+
+ case LY_STMT_CONFIG:
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_MANDATORY:
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ case LY_STMT_ORDERED_BY:
+ case LY_STMT_POSITION:
+ case LY_STMT_REQUIRE_INSTANCE:
+ case LY_STMT_STATUS:
+ case LY_STMT_VALUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ /* nothing to do */
+ break;
+
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_KEY:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PREFIX:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_UNITS:
+ /* single string */
+ lydict_remove(ctx, *(const char **)substmts[u].storage);
+ break;
+
+ case LY_STMT_LENGTH:
+ case LY_STMT_MUST:
+ case LY_STMT_PATTERN:
+ case LY_STMT_RANGE:
+ /* multiple restrictions */
+ FREE_ARRAY(&fctx, *(struct lysp_restr **)substmts[u].storage, lysp_restr_free);
+ break;
+
+ case LY_STMT_WHEN:
+ /* multiple whens */
+ FREE_ARRAY(&fctx, *(struct lysp_when **)substmts[u].storage, lysp_when_free);
+ break;
+
+ case LY_STMT_PATH:
+ /* single expression */
+ lyxp_expr_free(ctx, *(struct lyxp_expr **)substmts[u].storage);
+ break;
+
+ case LY_STMT_DEFAULT:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_UNIQUE:
+ /* multiple qnames */
+ FREE_ARRAY(ctx, *(struct lysp_qname **)substmts[u].storage, lysp_qname_free);
+ break;
+
+ case LY_STMT_TYPEDEF:
+ /* multiple typedefs */
+ FREE_ARRAY(&fctx, *(struct lysp_tpdf **)substmts[u].storage, lysp_tpdf_free);
+ break;
+
+ case LY_STMT_TYPE: {
+ /* single type */
+ struct lysp_type **type_p = substmts[u].storage;
+
+ lysp_type_free(&fctx, *type_p);
+ free(*type_p);
+ break;
+ }
+ case LY_STMT_MODULE:
+ case LY_STMT_SUBMODULE:
+ /* single (sub)module */
+ lysp_module_free(&fctx, *(struct lysp_module **)substmts[u].storage);
+ break;
+
+ default:
+ LOGINT(ctx);
+ }
+ }
+
+ LY_ARRAY_FREE(substmts);
+}
+
+LIBYANG_API_DEF void
+lyplg_ext_cfree_instance_substatements(const struct ly_ctx *ctx, struct lysc_ext_substmt *substmts)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysf_ctx fctx = {.ctx = (struct ly_ctx *)ctx};
+ ly_bool node_free;
+
+ LY_ARRAY_FOR(substmts, u) {
+ if (!substmts[u].storage) {
+ continue;
+ }
+
+ switch (substmts[u].stmt) {
+ case LY_STMT_NOTIFICATION:
+ case LY_STMT_INPUT:
+ case LY_STMT_OUTPUT:
+ case LY_STMT_ACTION:
+ case LY_STMT_RPC:
+ case LY_STMT_ANYDATA:
+ case LY_STMT_ANYXML:
+ case LY_STMT_CASE:
+ case LY_STMT_CHOICE:
+ case LY_STMT_CONTAINER:
+ case LY_STMT_LEAF:
+ case LY_STMT_LEAF_LIST:
+ case LY_STMT_LIST: {
+ struct lysc_node *child, *child_next;
+
+ LY_LIST_FOR_SAFE(*((struct lysc_node **)substmts[u].storage), child_next, child) {
+ node_free = (child->nodetype & (LYS_INPUT | LYS_OUTPUT)) ? 1 : 0;
+ lysc_node_free_(&fctx, child);
+ if (node_free) {
+ free(child);
+ }
+ }
+ *((struct lysc_node **)substmts[u].storage) = NULL;
+ break;
+ }
+ case LY_STMT_USES:
+ case LY_STMT_CONFIG:
+ case LY_STMT_FRACTION_DIGITS:
+ case LY_STMT_MANDATORY:
+ case LY_STMT_MAX_ELEMENTS:
+ case LY_STMT_MIN_ELEMENTS:
+ case LY_STMT_ORDERED_BY:
+ case LY_STMT_POSITION:
+ case LY_STMT_REQUIRE_INSTANCE:
+ case LY_STMT_STATUS:
+ case LY_STMT_VALUE:
+ /* nothing to do */
+ break;
+
+ case LY_STMT_ARGUMENT:
+ case LY_STMT_CONTACT:
+ case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_KEY:
+ case LY_STMT_MODIFIER:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PRESENCE:
+ case LY_STMT_REFERENCE:
+ case LY_STMT_UNITS: {
+ /* single item */
+ const char *str = *((const char **)substmts[u].storage);
+
+ lydict_remove(ctx, str);
+ break;
+ }
+ case LY_STMT_BIT:
+ case LY_STMT_ENUM: {
+ /* sized array */
+ struct lysc_type_bitenum_item *items = *((struct lysc_type_bitenum_item **)substmts[u].storage);
+
+ FREE_ARRAY(&fctx, items, lysc_enum_item_free);
+ break;
+ }
+ case LY_STMT_LENGTH:
+ case LY_STMT_RANGE: {
+ /* single item */
+ struct lysc_range *range = *((struct lysc_range **)substmts[u].storage);
+
+ lysc_range_free(&fctx, range);
+ break;
+ }
+ case LY_STMT_MUST: {
+ /* sized array */
+ struct lysc_must *musts = *((struct lysc_must **)substmts[u].storage);
+
+ FREE_ARRAY(&fctx, musts, lysc_must_free);
+ break;
+ }
+ case LY_STMT_WHEN:
+ /* single item, expects a pointer */
+ lysc_when_free(&fctx, substmts[u].storage);
+ break;
+
+ case LY_STMT_PATTERN: {
+ /* sized array of pointers */
+ struct lysc_pattern **patterns = *((struct lysc_pattern ***)substmts[u].storage);
+
+ FREE_ARRAY(&fctx, patterns, lysc_pattern_free);
+ break;
+ }
+ case LY_STMT_TYPE: {
+ /* single item */
+ struct lysc_type *type = *((struct lysc_type **)substmts[u].storage);
+
+ lysc_type_free(&fctx, type);
+ break;
+ }
+ case LY_STMT_IDENTITY: {
+ /* sized array */
+ struct lysc_ident *idents = *((struct lysc_ident **)substmts[u].storage);
+
+ FREE_ARRAY(&fctx, idents, lysc_ident_free);
+ break;
+ }
+ case LY_STMT_EXTENSION_INSTANCE: {
+ /* sized array */
+ struct lysc_ext_instance *exts = *((struct lysc_ext_instance **)substmts[u].storage);
+
+ FREE_ARRAY(&fctx, exts, lysc_ext_instance_free);
+ break;
+ }
+ case LY_STMT_AUGMENT:
+ case LY_STMT_BASE:
+ case LY_STMT_BELONGS_TO:
+ case LY_STMT_DEFAULT:
+ case LY_STMT_DEVIATE:
+ case LY_STMT_DEVIATION:
+ case LY_STMT_EXTENSION:
+ case LY_STMT_FEATURE:
+ case LY_STMT_GROUPING:
+ case LY_STMT_IF_FEATURE:
+ case LY_STMT_IMPORT:
+ case LY_STMT_INCLUDE:
+ case LY_STMT_MODULE:
+ case LY_STMT_PATH:
+ case LY_STMT_PREFIX:
+ case LY_STMT_REFINE:
+ case LY_STMT_REVISION:
+ case LY_STMT_REVISION_DATE:
+ case LY_STMT_SUBMODULE:
+ case LY_STMT_TYPEDEF:
+ case LY_STMT_UNIQUE:
+ case LY_STMT_YANG_VERSION:
+ case LY_STMT_YIN_ELEMENT:
+ /* it is not possible to compile these statements */
+ break;
+
+ default:
+ LOGINT(ctx);
+ }
+ }
+
+ LY_ARRAY_FREE(substmts);
+}
+
+void
+lysp_yang_ctx_free(struct lysp_yang_ctx *ctx)
+{
+ if (ctx) {
+ if (ctx->main_ctx == (struct lysp_ctx *)ctx) {
+ ly_set_erase(&ctx->tpdfs_nodes, NULL);
+ ly_set_erase(&ctx->grps_nodes, NULL);
+ }
+ assert(!ctx->tpdfs_nodes.count && !ctx->grps_nodes.count);
+ ly_set_erase(&ctx->ext_inst, NULL);
+ ly_set_rm_index(ctx->parsed_mods, ctx->parsed_mods->count - 1, NULL);
+ if (!ctx->parsed_mods->count) {
+ ly_set_free(ctx->parsed_mods, NULL);
+ }
+ free(ctx);
+ }
+}
+
+void
+lysp_yin_ctx_free(struct lysp_yin_ctx *ctx)
+{
+ if (ctx) {
+ if (ctx->main_ctx == (struct lysp_ctx *)ctx) {
+ ly_set_erase(&ctx->tpdfs_nodes, NULL);
+ ly_set_erase(&ctx->grps_nodes, NULL);
+ }
+ assert(!ctx->tpdfs_nodes.count && !ctx->grps_nodes.count);
+ ly_set_erase(&ctx->ext_inst, NULL);
+ ly_set_rm_index(ctx->parsed_mods, ctx->parsed_mods->count - 1, NULL);
+ if (!ctx->parsed_mods->count) {
+ ly_set_free(ctx->parsed_mods, NULL);
+ }
+ lyxml_ctx_free(ctx->xmlctx);
+ free(ctx);
+ }
+}
diff --git a/src/tree_schema_free.h b/src/tree_schema_free.h
new file mode 100644
index 0000000..d79164b
--- /dev/null
+++ b/src/tree_schema_free.h
@@ -0,0 +1,221 @@
+/**
+ * @file tree_schema_free.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief internal freeing functions for YANG schema trees.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_SCHEMA_FREE_H_
+#define LY_TREE_SCHEMA_FREE_H_
+
+#include "set.h"
+#include "tree_schema.h"
+
+struct lysp_yang_ctx;
+struct lysp_yin_ctx;
+
+struct lysf_ctx {
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct ly_set ext_set;
+};
+
+/**
+ * @brief Macro to free [sized array](@ref sizedarrays) of items using the provided free function. The ARRAY itself is also freed,
+ * but the memory is not sanitized.
+ */
+#define FREE_ARRAY(CTX, ARRAY, FUNC) {LY_ARRAY_COUNT_TYPE c__; LY_ARRAY_FOR(ARRAY, c__){(FUNC)(CTX, &(ARRAY)[c__]);}LY_ARRAY_FREE(ARRAY);}
+
+/**
+ * @brief Macro to free the specified MEMBER of a structure using the provided free function. The memory is not sanitized.
+ */
+#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {(FUNC)(CTX, MEMBER);free(MEMBER);}
+
+/**
+ * @brief Macro to free [sized array](@ref sizedarrays) of strings stored in the context's dictionary. The ARRAY itself is also freed,
+ * but the memory is not sanitized.
+ */
+#define FREE_STRINGS(CTX, ARRAY) {LY_ARRAY_COUNT_TYPE c__; LY_ARRAY_FOR(ARRAY, c__){lydict_remove(CTX, ARRAY[c__]);}LY_ARRAY_FREE(ARRAY);}
+
+/**
+ * @brief Free a parsed qualified name.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] qname Qualified name to free.
+ */
+void lysp_qname_free(const struct ly_ctx *ctx, struct lysp_qname *qname);
+
+/**
+ * @brief Free the parsed extension instance structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] ext Parsed extension instance structure to free. Note that the instance itself is not freed.
+ */
+void lysp_ext_instance_free(struct lysf_ctx *ctx, struct lysp_ext_instance *ext);
+
+/**
+ * @brief Free a parsed restriction.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] restr Restriction to free.
+ */
+void lysp_restr_free(struct lysf_ctx *ctx, struct lysp_restr *restr);
+
+/**
+ * @brief Free the parsed type structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] type Parsed schema type structure to free. Note that the type itself is not freed.
+ */
+void lysp_type_free(struct lysf_ctx *ctx, struct lysp_type *type);
+
+/**
+ * @brief Free the parsed when structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] when Parsed schema when structure to free. Note that the structure itself is not freed.
+ */
+void lysp_when_free(struct lysf_ctx *ctx, struct lysp_when *when);
+
+/**
+ * @brief Free the parsed deviate structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] d Parsed schema deviate structure to free. Note that the structure itself is not freed.
+ */
+void lysp_deviate_free(struct lysf_ctx *ctx, struct lysp_deviate *d);
+
+/**
+ * @brief Free the parsed deviation structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] dev Parsed schema deviation structure to free. Note that the structure itself is not freed.
+ */
+void lysp_deviation_free(struct lysf_ctx *ctx, struct lysp_deviation *dev);
+
+/**
+ * @brief Free a parsed node.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] node Node to free.
+ */
+void lysp_node_free(struct lysf_ctx *ctx, struct lysp_node *node);
+
+/**
+ * @brief Free the parsed YANG schema tree structure. Works for both modules and submodules.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] module Parsed YANG schema tree structure to free.
+ */
+void lysp_module_free(struct lysf_ctx *ctx, struct lysp_module *module);
+
+/**
+ * @brief Free the compiled extension instance structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] ext Compiled extension instance structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_ext_instance_free(struct lysf_ctx *ctx, struct lysc_ext_instance *ext);
+
+/**
+ * @brief Free the compiled if-feature structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] iff Compiled if-feature structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_iffeature_free(struct lysf_ctx *ctx, struct lysc_iffeature *iff);
+
+/**
+ * @brief Free a compiled pattern.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] pattern Pointer to the pattern to free.
+ */
+void lysc_pattern_free(struct lysf_ctx *ctx, struct lysc_pattern **pattern);
+
+/**
+ * @brief Free a bit/enum item.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] item Bit/enum item to free.
+ */
+void lysc_enum_item_free(struct lysf_ctx *ctx, struct lysc_type_bitenum_item *item);
+
+/**
+ * @brief Free the compiled type structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] type Compiled type structure to be freed. The structure has refcount, so it is freed only in case
+ * the value is decreased to 0.
+ */
+void lysc_type_free(struct lysf_ctx *ctx, struct lysc_type *type);
+
+/**
+ * @brief Free the compiled container node structure.
+ *
+ * Only the container-specific members are freed, for generic node free function,
+ * use ::lysc_node_free().
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] node Compiled container node structure to be freed.
+ */
+void lysc_node_container_free(struct lysf_ctx *ctx, struct lysc_node_container *node);
+
+/**
+ * @brief Free the compiled node structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in] node Compiled node structure to be freed.
+ * @param[in] unlink Whether to first unlink the node before freeing.
+ */
+void lysc_node_free(struct lysf_ctx *ctx, struct lysc_node *node, ly_bool unlink);
+
+/**
+ * @brief Free the compiled schema structure.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] module Compiled schema module structure to free.
+ */
+void lysc_module_free(struct lysf_ctx *ctx, struct lysc_module *module);
+
+/**
+ * @brief Free the schema structure. It just frees, it does not remove the schema from its context.
+ *
+ * @param[in] ctx Free context.
+ * @param[in,out] module Schema module structure to free.
+ * @param[in] remove_links Whether to remove links in other modules to structures in this module. Not needed if
+ * the whole context is being freed.
+ */
+void lys_module_free(struct lysf_ctx *ctx, struct lys_module *module, ly_bool remove_links);
+
+/**
+ * @brief Erase free context.
+ *
+ * @param[in] ctx Free context to erase.
+ */
+void lysf_ctx_erase(struct lysf_ctx *ctx);
+
+/**
+ * @brief Free lys parser context.
+ *
+ * @param[in] ctx Context to free.
+ */
+void lysp_yang_ctx_free(struct lysp_yang_ctx *ctx);
+
+/**
+ * @brief Free yin parser context
+ *
+ * @param[in] ctx Context to free.
+ */
+void lysp_yin_ctx_free(struct lysp_yin_ctx *ctx);
+
+#endif /* LY_TREE_SCHEMA_FREE_H_ */
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
new file mode 100644
index 0000000..95dee0b
--- /dev/null
+++ b/src/tree_schema_internal.h
@@ -0,0 +1,735 @@
+/**
+ * @file tree_schema_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief internal functions for YANG schema trees.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_TREE_SCHEMA_INTERNAL_H_
+#define LY_TREE_SCHEMA_INTERNAL_H_
+
+#include <stdint.h>
+
+#include "common.h"
+#include "set.h"
+#include "tree_schema.h"
+
+struct lysc_ctx;
+struct lys_glob_unres;
+
+#define LY_YANG_SUFFIX ".yang"
+#define LY_YANG_SUFFIX_LEN 5
+#define LY_YIN_SUFFIX ".yin"
+#define LY_YIN_SUFFIX_LEN 4
+
+#define YIN_NS_URI "urn:ietf:params:xml:ns:yang:yin:1"
+
+#define LY_PCRE2_MSG_LIMIT 256
+
+/**
+ * @brief The maximum depth at which the last nested block is located.
+ * Designed to protect against corrupted input that causes a stack-overflow error.
+ * For yang language and json format, the block is bounded by "{ }".
+ * For the xml format, the opening and closing element tag is considered as the block.
+ */
+#define LY_MAX_BLOCK_DEPTH 500
+
+/* list of the deviate modifications strings */
+extern const char * const ly_devmod_list[];
+#define ly_devmod2str(TYPE) ly_devmod_list[TYPE]
+
+/**
+ * @brief Check module version is at least 2 (YANG 1.1) because of the keyword presence.
+ * Logs error message and returns LY_EVALID in case of module in YANG version 1.0.
+ * @param[in] CTX yang parser context to get current module and for logging.
+ * @param[in] KW keyword allowed only in YANG version 1.1 (or later) - for logging.
+ * @param[in] PARENT parent statement where the KW is present - for logging.
+ */
+#define PARSER_CHECK_STMTVER2_RET(CTX, KW, PARENT) \
+ if (PARSER_CUR_PMOD(CTX)->version < LYS_VERSION_1_1) {LOGVAL_PARSER((CTX), LY_VCODE_INCHILDSTMT2, KW, PARENT); return LY_EVALID;}
+
+/* These 2 macros checks YANG's identifier grammar rule */
+#define is_yangidentstartchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
+#define is_yangidentchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || \
+ c == '_' || c == '-' || c == '.')
+
+/* Macro to check YANG's yang-char grammar rule */
+#define is_yangutf8char(c) ((c >= 0x20 && c <= 0xd7ff) || c == 0x09 || c == 0x0a || c == 0x0d || \
+ (c >= 0xe000 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+ (c >= 0x10000 && c <= 0x1fffd) || (c >= 0x20000 && c <= 0x2fffd) || \
+ (c >= 0x30000 && c <= 0x3fffd) || (c >= 0x40000 && c <= 0x2fffd) || \
+ (c >= 0x50000 && c <= 0x5fffd) || (c >= 0x60000 && c <= 0x6fffd) || \
+ (c >= 0x70000 && c <= 0x7fffd) || (c >= 0x80000 && c <= 0x8fffd) || \
+ (c >= 0x90000 && c <= 0x9fffd) || (c >= 0xa0000 && c <= 0xafffd) || \
+ (c >= 0xb0000 && c <= 0xbfffd) || (c >= 0xc0000 && c <= 0xcfffd) || \
+ (c >= 0xd0000 && c <= 0xdfffd) || (c >= 0xe0000 && c <= 0xefffd) || \
+ (c >= 0xf0000 && c <= 0xffffd) || (c >= 0x100000 && c <= 0x10fffd))
+
+/**
+ * @brief Try to find object with MEMBER string matching the IDENT in the given ARRAY.
+ * Macro logs an error message and returns LY_EVALID in case of existence of a matching object.
+ *
+ * @param[in] CTX yang parser context for logging.
+ * @param[in] ARRAY [sized array](@ref sizedarrays) of a generic objects with member named MEMBER to search.
+ * @param[in] MEMBER Name of the member of the objects in the ARRAY to compare.
+ * @param[in] STMT Name of the compared YANG statements for logging.
+ * @param[in] IDENT String trying to find in the ARRAY's objects inside the MEMBER member.
+ */
+#define CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, STMT, IDENT) \
+ if (ARRAY) { \
+ for (LY_ARRAY_COUNT_TYPE u_ = 0; u_ < LY_ARRAY_COUNT(ARRAY) - 1; ++u_) { \
+ if (!strcmp((ARRAY)[u_].MEMBER, IDENT)) { \
+ LOGVAL_PARSER(CTX, LY_VCODE_DUPIDENT, IDENT, STMT); \
+ return LY_EVALID; \
+ } \
+ } \
+ }
+
+#define CHECK_NONEMPTY(CTX, VALUE_LEN, STMT) \
+ if (!VALUE_LEN) { \
+ LOGWRN(PARSER_CTX(CTX), "Empty argument of %s statement does not make sense.", STMT); \
+ }
+
+/*
+ * Additional YANG constants
+ */
+#define Y_TAB_SPACES 8 /**< number of spaces instead of tab character */
+#define LY_TYPE_DEC64_FD_MAX 18 /**< Maximal value of decimal64's fraction-digits */
+
+/**
+ * @brief List of YANG statement groups - the (sub)module's substatements
+ */
+enum yang_module_stmt {
+ Y_MOD_MODULE_HEADER,
+ Y_MOD_LINKAGE,
+ Y_MOD_META,
+ Y_MOD_REVISION,
+ Y_MOD_BODY
+};
+
+/**
+ * @brief Types of arguments of YANG statements
+ */
+enum yang_arg {
+ Y_IDENTIF_ARG, /**< YANG "identifier-arg-str" rule */
+ Y_PREF_IDENTIF_ARG, /**< YANG "identifier-ref-arg-str" or node-identifier rule */
+ Y_STR_ARG, /**< YANG "string" rule */
+ Y_MAYBE_STR_ARG /**< optional YANG "string" rule */
+};
+
+#define PARSER_CUR_PMOD(CTX) ((struct lysp_module *)(CTX)->parsed_mods->objs[(CTX)->parsed_mods->count - 1])
+#define PARSER_CTX(CTX) ((CTX) ? PARSER_CUR_PMOD(CTX)->mod->ctx : NULL)
+#define LOGVAL_PARSER(CTX, ...) LOGVAL(PARSER_CTX(CTX), __VA_ARGS__)
+
+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 *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,
+ then should be set to the context of the module to which it belongs,
+ otherwise it points to the beginning of this structure. */
+};
+
+/**
+ * @brief Internal context for yang schema parser.
+ */
+struct lysp_yang_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 *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,
+ then should be set to the context of the module to which it belongs,
+ otherwise it points to the beginning of this structure. */
+ struct ly_in *in; /**< input handler for the parser */
+ uint64_t indent; /**< current position on the line for YANG indentation */
+ uint32_t depth; /**< current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */
+};
+
+/**
+ * @brief Internal context for yin schema parser.
+ */
+struct lysp_yin_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 *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,
+ then should be set to the context of the module to which it belongs,
+ otherwise it points to the beginning of this structure. */
+ struct lyxml_ctx *xmlctx; /**< context for xml parser */
+};
+
+/**
+ * @brief Check that @p c is valid UTF8 code point for YANG string.
+ *
+ * @param[in] ctx parser context for logging.
+ * @param[in] c UTF8 code point of a character to check.
+ * @return LY_ERR values.
+ */
+LY_ERR lysp_check_stringchar(struct lysp_ctx *ctx, uint32_t c);
+
+/**
+ * @brief Check that @p c is valid UTF8 code point for YANG identifier.
+ *
+ * @param[in] ctx parser context for logging. If NULL, does not log.
+ * @param[in] c UTF8 code point of a character to check.
+ * @param[in] first Flag to check the first character of an identifier, which is more restricted.
+ * @param[in,out] prefix Storage for internally used flag in case of possible prefixed identifiers:
+ * 0 - colon not yet found (no prefix)
+ * 1 - @p c is the colon character
+ * 2 - prefix already processed, now processing the identifier
+ *
+ * If the identifier cannot be prefixed, NULL is expected.
+ * @return LY_ERR values.
+ */
+LY_ERR lysp_check_identifierchar(struct lysp_ctx *ctx, uint32_t c, ly_bool first, uint8_t *prefix);
+
+/**
+ * @brief Check the currently present prefixes in the module for collision with the new one.
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] imports List of current imports of the module to check prefix collision.
+ * @param[in] module_prefix Prefix of the module to check collision.
+ * @param[in] value Newly added prefix value (including its location to distinguish collision with itself).
+ * @return LY_EEXIST when prefix is already used in the module, LY_SUCCESS otherwise
+ */
+LY_ERR lysp_check_prefix(struct lysp_ctx *ctx, struct lysp_import *imports, const char *module_prefix, const char **value);
+
+/**
+ * @brief Check date string (4DIGIT "-" 2DIGIT "-" 2DIGIT)
+ *
+ * @param[in] ctx Optional context for logging.
+ * @param[in] date Date string to check (non-necessarily terminated by \0)
+ * @param[in] date_len Length of the date string, 10 expected.
+ * @param[in] stmt Statement name for error message.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_date(struct lysp_ctx *ctx, const char *date, size_t date_len, const char *stmt);
+
+/**
+ * @brief Find type specified type definition.
+ *
+ * @param[in] id Name of the type including possible prefix. Module where the prefix is being searched is start_module.
+ * @param[in] start_node Context node where the type is being instantiated to be able to search typedefs in parents.
+ * @param[in] start_module Module where the type is being instantiated for search for typedefs.
+ * @param[in] ext Extension where the type is being instantiated, if any.
+ * @param[out] type Built-in type identifier of the id. If #LY_TYPE_UNKNOWN, tpdf is expected to contain found YANG schema typedef statement.
+ * @param[out] tpdf Found type definition.
+ * @param[out] node Node where the found typedef is defined, NULL in case of a top-level typedef.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_type_find(const char *id, struct lysp_node *start_node, const struct lysp_module *start_module,
+ const struct lysc_ext_instance *ext, LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node);
+
+/**
+ * @brief Check names of typedefs in the parsed module to detect collisions.
+ *
+ * @param[in] ctx Parser context for logging and to maintain tpdfs_nodes
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_typedefs(struct lysp_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of groupings in the parsed module to detect collisions.
+ *
+ * @param[in] ctx Parser context for logging and to maintain grps_nodes.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_groupings(struct lysp_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of features in the parsed module and submodules to detect collisions.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_features(struct lysp_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of identities in the parsed module and submodules to detect collisions.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_identities(struct lysp_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Just move the newest revision into the first position, does not sort the rest
+ * @param[in] revs Sized-array of the revisions in a printable schema tree.
+ */
+void lysp_sort_revisions(struct lysp_revision *revs);
+
+/**
+ * @brief Validate enum name.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] name String to check.
+ * @param[in] name_len Length of name.
+ *
+ * @return LY_ERR values
+ */
+LY_ERR lysp_check_enum_name(struct lysp_ctx *ctx, const char *name, size_t name_len);
+
+/**
+ * @brief Find source data for a specific module, parse it, and add into the context.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] name Name of the module to load.
+ * @param[in] revision Optional revision of the module to load. If NULL, the newest revision is loaded.
+ * @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] mod Created module structure.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_parse_load(struct ly_ctx *ctx, const char *name, const char *revision, struct ly_set *new_mods,
+ struct lys_module **mod);
+
+/**
+ * @brief Parse included submodules into the simply parsed YANG module.
+ *
+ * YANG 1.0 does not require the main module to include all the submodules. Therefore, parsing submodules can cause
+ * reallocating and extending the includes array in the main module by the submodules included only in submodules.
+ *
+ * @param[in] pctx main parser context
+ * @param[in] pmod Parsed module with the includes array to be processed.
+ * @param[in,out] new_mods Set of all the new mods added to the context. Includes this module and all of its imports.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_load_submodules(struct lysp_ctx *pctx, struct lysp_module *pmod, struct ly_set *new_mods);
+
+/**
+ * @brief Get address of a node's actions list if any.
+ * Decides the node's type and in case it has an actions list, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's actions member if any, NULL otherwise.
+ */
+struct lysp_node_action **lysp_node_actions_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's notifications list if any.
+ * Decides the node's type and in case it has a notifications list, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's notifs member if any, NULL otherwise.
+ */
+struct lysp_node_notif **lysp_node_notifs_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ * Decides the node's type and in case it has a children list, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysp_node **lysp_node_child_p(struct lysp_node *node);
+
+/**
+ * @brief Get the address of the node's musts member, if any.
+ * Decides the node's type and in case it has a musts member, returns its address.
+ *
+ * @param[in] node Node to examine.
+ * @return The address of the node's musts member if any, NULL otherwise.
+ */
+struct lysp_restr **lysp_node_musts_p(const struct lysp_node *node);
+
+/**
+ * @brief Get the node's musts member, if any.
+ * Decides the node's type and in case it has a musts member, returns its address.
+ *
+ * @param[in] node Node to examine.
+ * @return The node's musts member if any, NULL otherwise.
+ */
+struct lysp_restr *lysp_node_musts(const struct lysp_node *node);
+
+/**
+ * @brief Get the address of the node's when member, if any.
+ * Decides the node's type and in case it has a when, returns it.
+ *
+ * @param[in] node Node to examine.
+ * @return The address of the node's when member if any, NULL otherwise.
+ */
+struct lysp_when **lysp_node_when_p(const struct lysp_node *node);
+
+/**
+ * @brief Get the node's when member, if any.
+ * Decides the node's type and in case it has a when, returns it.
+ *
+ * @param[in] node Node to examine.
+ * @return The node's when member if any, NULL otherwise.
+ */
+struct lysp_when *lysp_node_when(const struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ * Decides the node's type and in case it has a children list, returns its address.
+ *
+ * Do not use for RPC and action nodes.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysc_node **lysc_node_child_p(const struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's notifs pointer if any.
+ * Decides the node's type and in case it has a notifs array, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's notifs member if any, NULL otherwise.
+ */
+struct lysc_node_notif **lysc_node_notifs_p(struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's actions pointer if any.
+ * Decides the node's type and in case it has a actions array, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's actions member if any, NULL otherwise.
+ */
+struct lysc_node_action **lysc_node_actions_p(struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's when member if any.
+ * Decides the node's type and in case it has a when member, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's when member if any, NULL otherwise.
+ */
+struct lysc_when ***lysc_node_when_p(const struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's musts member if any.
+ * Decides the node's type and in case it has a musts member, returns its address.
+ *
+ * @param[in] node Node to check.
+ * @return Address of the node's musts member if any, NULL otherwise.
+ */
+struct lysc_must **lysc_node_musts_p(const struct lysc_node *node);
+
+/**
+ * @brief Find parsed extension definition for the given extension instance.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Extension instance for which the definition will be searched.
+ * @param[out] ext_mod Module of the extension definition of @p ext.
+ * @param[out] ext_def Optional found extension definition.
+ * @return LY_SUCCESS when the definition was found.
+ * @return LY_EVALID when the extension instance is invalid and/or the definition not found.
+ */
+LY_ERR lysp_ext_find_definition(const struct ly_ctx *ctx, const struct lysp_ext_instance *ext, const struct lys_module **ext_mod,
+ struct lysp_ext **ext_def);
+
+/**
+ * @brief Get schema node in extension instance according to the given parameters.
+ *
+ * Wraps ::lys_getnext_ext() and match according to the given arguments.
+ *
+ * @param[in] ext Extension instance which top-level schema node is being searched.
+ * @param[in] module Optional parameter to match the extension instance's (and its data) module.
+ * @param[in] name Name of the schema node to find, if the string is not NULL-terminated, the @p name_len must be set.
+ * @param[in] name_len Length of the @p name string, use in case the @p name is not NULL-terminated string.
+ * @param[in] nodetype Allowed [type of the node](@ref schemanodetypes).
+ * @param[in] options ORed [lys_getnext options](@ref sgetnextflags).
+ * @return Found schema node if there is some satisfy the provided requirements.
+ */
+const struct lysc_node *lysc_ext_find_node(const struct lysc_ext_instance *ext, const struct lys_module *module,
+ const char *name, size_t name_len, uint16_t nodetype, uint32_t options);
+
+/**
+ * @brief When the module comes from YIN format, the argument name is unknown because of missing extension definition
+ * (it might come from import modules which is not yet parsed at that time). Therefore, all the attributes are stored
+ * as substatements and resolving argument is postponed.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] ext_p Parsed extension to be updated.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_ext_instance_resolve_argument(struct ly_ctx *ctx, struct lysp_ext_instance *ext_p);
+
+/**
+ * @brief Iterate over the specified type of the extension instances
+ *
+ * @param[in] ext ([Sized array](@ref sizedarrays)) of extensions to explore
+ * @param[in] index Index in the @p ext array where to start searching (first call with 0, the consequent calls with
+ * the returned index increased by 1 (until the iteration is not terminated by returning LY_ARRAY_COUNT(ext).
+ * @param[in] substmt The statement the extension is supposed to belong to.
+ * @result index in the ext array, LY_ARRAY_COUNT(ext) value if not present.
+ */
+LY_ARRAY_COUNT_TYPE lysp_ext_instance_iter(struct lysp_ext_instance *ext, LY_ARRAY_COUNT_TYPE index, enum ly_stmt substmt);
+
+/**
+ * @brief Stringify YANG built-in type.
+ *
+ * @param[in] basetype Built-in type ID to stringify.
+ * @return Constant string with the name of the built-in type.
+ */
+const char *lys_datatype2str(LY_DATA_TYPE basetype);
+
+/**
+ * @brief Implement a module and resolve all global unres.
+ *
+ * @param[in] mod Module to implement.
+ * @param[in] features Features to set, see ::lys_set_features().
+ * @param[in] unres Global unres with all the created modules.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR _lys_set_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres);
+
+/**
+ * @brief Create dependency sets for all modules in a context.
+ * Also sets to_compile flags for all the modules that should be (re)compiled.
+ *
+ * @param[in] ctx Context to use.
+ * @param[in,out] main_set Set of dependency module sets.
+ * @param[in] mod Optional only module whose dependency set is needed, otherwise all sets are created.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_unres_dep_sets_create(struct ly_ctx *ctx, struct ly_set *main_set, struct lys_module *mod);
+
+/**
+ * @brief Revert changes stored in global compile context after a failed compilation.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres to use.
+ */
+void lys_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres);
+
+/**
+ * @brief Erase the global compile context.
+ *
+ * @param[in] unres Global unres to erase.
+ */
+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);
+
+/**
+ * @brief Parse a module and add it into the context.
+ *
+ * @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,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);
+
+/**
+ * @brief Parse submodule.
+ *
+ * The latest_revision flag of submodule is updated.
+ *
+ * @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] 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] 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);
+
+/**
+ * @brief Fill filepath value if available in input handler @p in
+ *
+ * @param[in] ctx Context with dictionary where the filepath value will be stored.
+ * @param[in] in Input handler to examine (filepath is not available for all the input types).
+ * @param[out] filepath Address of the variable where the filepath is stored.
+ */
+void lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath);
+
+/**
+ * @brief Get the @ref ifftokens from the given position in the 2bits array
+ * (libyang format of the if-feature expression).
+ * @param[in] list The 2bits array with the compiled if-feature expression.
+ * @param[in] pos Position (0-based) to specify from which position get the operator.
+ */
+uint8_t lysc_iff_getop(uint8_t *list, size_t pos);
+
+/**
+ * @brief match yang keyword
+ *
+ * @param[in,out] in Input structure, is updated.
+ * @param[in,out] indent Pointer to the counter of current position on the line for YANG indentation (optional).
+ * @return yang_keyword values.
+ */
+enum ly_stmt lysp_match_kw(struct ly_in *in, uint64_t *indent);
+
+/**
+ * @brief Generate path of the given node in the requested format.
+ *
+ * @param[in] node Schema path of this node will be generated.
+ * @param[in] parent Build relative path only until this parent is found. If NULL, the full absolute path is printed.
+ * @param[in] pathtype Format of the path to generate.
+ * @param[in,out] buffer Prepared buffer of the @p buflen length to store the generated path.
+ * If NULL, memory for the complete path is allocated.
+ * @param[in] buflen Size of the provided @p buffer.
+ * @return NULL in case of memory allocation error, path of the node otherwise.
+ * In case the @p buffer is NULL, the returned string is dynamically allocated and caller is responsible to free it.
+ */
+char *lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LYSC_PATH_TYPE pathtype, char *buffer,
+ size_t buflen);
+
+/**
+ * @brief Get format-specific prefix for a module.
+ *
+ * This function is available for type plugins via ::lyplg_type_get_prefix() API function.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in] format Format of the prefix.
+ * @param[in] prefix_data Format-specific data based on @p format:
+ * LY_VALUE_CANON - NULL
+ * LY_VALUE_SCHEMA - const struct ::lysp_module* (module used for resolving imports to prefixes)
+ * LY_VALUE_SCHEMA_RESOLVED - struct ::lysc_prefix* (sized array of pairs: prefix - module)
+ * LY_VALUE_XML - struct ::ly_set* (set of all returned modules as struct ::lys_module)
+ * LY_VALUE_JSON - NULL
+ * LY_VALUE_LYB - NULL
+ * @return Module prefix to print.
+ * @return NULL on error.
+ */
+const char *ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @brief Resolve format-specific prefixes to modules.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] prefix Prefix to resolve.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of the prefix.
+ * @param[in] prefix_data Format-specific data based on @p format:
+ * LY_VALUE_CANON - NULL
+ * LY_VALUE_SCHEMA - const struct lysp_module * (module used for resolving prefixes from imports)
+ * LY_VALUE_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module)
+ * LY_VALUE_XML - const struct ly_set * (set with defined namespaces stored as ::lyxml_ns)
+ * LY_VALUE_JSON - NULL
+ * LY_VALUE_LYB - NULL
+ * @return Resolved prefix module,
+ * @return NULL otherwise.
+ */
+const struct lys_module *ly_resolve_prefix(const struct ly_ctx *ctx, const void *prefix, size_t prefix_len,
+ LY_VALUE_FORMAT format, const void *prefix_data);
+
+/**
+ * @brief Learn whether @p PMOD needs to be recompiled if it is implemented.
+ *
+ * @param[in] PMOD Parsed module or submodule.
+ * @return Whether it has statements that are recompiled or not.
+ */
+#define LYSP_HAS_RECOMPILED(PMOD) \
+ (PMOD->data || PMOD->rpcs || PMOD->notifs || PMOD->exts)
+
+/**
+ * @brief Learn whether the module has statements that need to be recompiled or not.
+ *
+ * @param[in] mod Module to examine.
+ * @return Whether it has statements that are recompiled or not.
+ */
+ly_bool lys_has_recompiled(const struct lys_module *mod);
+
+/**
+ * @brief Learn whether @p PMOD needs to be compiled if it is implemented.
+ *
+ * @param[in] PMOD Parsed module or submodule.
+ * @return Whether it needs (has) a compiled module or not.
+ */
+#define LYSP_HAS_COMPILED(PMOD) \
+ (LYSP_HAS_RECOMPILED(PMOD) || PMOD->augments || PMOD->deviations)
+
+/**
+ * @brief Learn whether the module has statements that need to be compiled or not.
+ *
+ * @param[in] mod Module to examine.
+ * @return Whether it needs compiled module or not.
+ */
+ly_bool lys_has_compiled(const struct lys_module *mod);
+
+/**
+ * @brief Learn whether the module has any grouping statements or not.
+ *
+ * @param[in] mod Module to examine.
+ * @return Whether it has groupings or not.
+ */
+ly_bool lys_has_dep_mods(const struct lys_module *mod);
+
+/**
+ * @brief Get YANG string keyword of a statement.
+ *
+ * @return YANG string keyword of the statement.
+ */
+const char *lys_stmt_str(enum ly_stmt stmt);
+
+/**
+ * @brief Get YIN argument (attribute) name of a statement.
+ *
+ * @return YIN argument name, if any.
+ */
+const char *lys_stmt_arg(enum ly_stmt stmt);
+
+#define LY_STMT_FLAG_YIN 0x1 /**< has YIN element */
+#define LY_STMT_FLAG_ID 0x2 /**< the value is identifier -> no quotes */
+
+/**
+ * @brief Get statement printer flags.
+ *
+ * @return Additional statement information as LY_STMT_FLAG_* flags.
+ */
+uint8_t lys_stmt_flags(enum ly_stmt stmt);
+
+/**
+ * @brief Learn whether the module qualifies for a single dep set with only this module or not.
+ *
+ * @param[in] mod Module to examine.
+ * @return Whether it qualifies as a single dep set or not.
+ */
+#define LYS_IS_SINGLE_DEP_SET(mod) \
+ (!(mod)->parsed->features && (!lys_has_compiled(mod) || ((mod)->compiled && !lys_has_recompiled(mod))))
+
+/**
+ * @brief Get pointer to a compiled ext instance storage for a specific statement.
+ *
+ * @param[in] ext Compiled ext instance.
+ * @param[in] stmt Compiled statement. Can be a mask when the first match is returned, it is expected the storage is
+ * the same for all the masked statements.
+ * @param[out] storage_p Pointer to a compiled ext instance substatement storage, NULL if was not compiled.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the substatement is not supported.
+ */
+LY_ERR lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, const void ***storage_p);
+
+#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/src/validation.c b/src/validation.c
new file mode 100644
index 0000000..6db020a
--- /dev/null
+++ b/src/validation.c
@@ -0,0 +1,2029 @@
+/**
+ * @file validation.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Validation
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "validation.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "diff.h"
+#include "hash_table.h"
+#include "log.h"
+#include "parser_data.h"
+#include "parser_internal.h"
+#include "plugins_exts.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_types.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+LY_ERR
+lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *new_diff = NULL;
+ const struct lyd_node *prev_inst;
+ char *key = NULL, *value = NULL, *position = NULL;
+ size_t buflen = 0, bufused = 0;
+ uint32_t pos;
+
+ assert((op == LYD_DIFF_OP_DELETE) || (op == LYD_DIFF_OP_CREATE));
+
+ if ((op == LYD_DIFF_OP_CREATE) && lysc_is_userordered(node->schema)) {
+ if (lysc_is_dup_inst_list(node->schema)) {
+ pos = lyd_list_pos(node);
+
+ /* generate position meta */
+ if (pos > 1) {
+ if (asprintf(&position, "%" PRIu32, pos - 1) == -1) {
+ LOGMEM(LYD_CTX(node));
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ } else {
+ position = strdup("");
+ LY_CHECK_ERR_GOTO(!position, LOGMEM(LYD_CTX(node)); ret = LY_EMEM, cleanup);
+ }
+ } else {
+ if (node->prev->next && (node->prev->schema == node->schema)) {
+ prev_inst = node->prev;
+ } else {
+ /* first instance */
+ prev_inst = NULL;
+ }
+
+ if (node->schema->nodetype == LYS_LIST) {
+ /* generate key meta */
+ if (prev_inst) {
+ LY_CHECK_GOTO(ret = lyd_path_list_predicate(prev_inst, &key, &buflen, &bufused, 0), cleanup);
+ } else {
+ key = strdup("");
+ LY_CHECK_ERR_GOTO(!key, LOGMEM(LYD_CTX(node)); ret = LY_EMEM, cleanup);
+ }
+ } else {
+ /* generate value meta */
+ if (prev_inst) {
+ value = strdup(lyd_get_value(prev_inst));
+ LY_CHECK_ERR_GOTO(!value, LOGMEM(LYD_CTX(node)); ret = LY_EMEM, cleanup);
+ } else {
+ value = strdup("");
+ LY_CHECK_ERR_GOTO(!value, LOGMEM(LYD_CTX(node)); ret = LY_EMEM, cleanup);
+ }
+ }
+ }
+ }
+
+ /* create new diff tree */
+ LY_CHECK_GOTO(ret = lyd_diff_add(node, op, NULL, NULL, key, value, position, NULL, NULL, &new_diff), cleanup);
+
+ /* merge into existing diff */
+ ret = lyd_diff_merge_all(diff, new_diff, 0);
+
+cleanup:
+ lyd_free_tree(new_diff);
+ free(key);
+ free(value);
+ free(position);
+ return ret;
+}
+
+/**
+ * @brief Evaluate all relevant "when" conditions of a node.
+ *
+ * @param[in] tree Data tree.
+ * @param[in] node Node whose relevant when conditions will be evaluated.
+ * @param[in] schema Schema node of @p node. It may not be possible to use directly if @p node is opaque.
+ * @param[in] xpath_options Additional XPath options to use.
+ * @param[out] disabled First when that evaluated false, if any.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINCOMPLETE if a referenced node does not have its when evaluated.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node, const struct lysc_node *schema,
+ uint32_t xpath_options, const struct lysc_when **disabled)
+{
+ LY_ERR ret;
+ const struct lyd_node *ctx_node;
+ struct lyxp_set xp_set;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(!node->schema || (node->schema == schema));
+
+ *disabled = NULL;
+
+ do {
+ const struct lysc_when *when;
+ struct lysc_when **when_list = lysc_node_when(schema);
+
+ LY_ARRAY_FOR(when_list, u) {
+ when = when_list[u];
+
+ /* get context node */
+ if (when->context == schema) {
+ ctx_node = node;
+ } else {
+ assert((!when->context && !node->parent) || (when->context == node->parent->schema));
+ ctx_node = lyd_parent(node);
+ }
+
+ /* evaluate when */
+ memset(&xp_set, 0, sizeof xp_set);
+ ret = lyxp_eval(LYD_CTX(node), when->cond, schema->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
+ ctx_node, ctx_node, tree, NULL, &xp_set, LYXP_SCHEMA | xpath_options);
+ lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
+
+ /* return error or LY_EINCOMPLETE for dependant unresolved when */
+ LY_CHECK_RET(ret);
+
+ if (!xp_set.val.bln) {
+ /* false when */
+ *disabled = when;
+ return LY_SUCCESS;
+ }
+ }
+
+ schema = schema->parent;
+ } while (schema && (schema->nodetype & (LYS_CASE | LYS_CHOICE)));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate when conditions of collected unres nodes.
+ *
+ * @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
+ * @param[in] mod Module of the @p tree to take into consideration when deleting @p tree and moving it.
+ * If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
+ * the first top-level sibling.
+ * @param[in] node_when Set with nodes with "when" conditions.
+ * @param[in] xpath_options Additional XPath options to use.
+ * @param[in,out] node_types Set with nodes with unresolved types, remove any with false "when" parents.
+ * @param[in,out] diff Validation diff.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+ uint32_t xpath_options, struct ly_set *node_types, struct lyd_node **diff)
+{
+ LY_ERR rc, r;
+ uint32_t i, idx;
+ const struct lysc_when *disabled;
+ struct lyd_node *node = NULL, *elem;
+
+ if (!node_when->count) {
+ return LY_SUCCESS;
+ }
+
+ i = node_when->count;
+ do {
+ --i;
+ node = node_when->dnodes[i];
+ LOG_LOCSET(node->schema, node, NULL, NULL);
+
+ /* evaluate all when expressions that affect this node's existence */
+ r = lyd_validate_node_when(*tree, node, node->schema, xpath_options, &disabled);
+ if (!r) {
+ if (disabled) {
+ /* when false */
+ if (node->flags & LYD_WHEN_TRUE) {
+ /* autodelete */
+ lyd_del_move_root(tree, node, mod);
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_GOTO(rc = lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff), error);
+ }
+
+ /* remove from node types set, if present */
+ if (node_types && node_types->count) {
+ LYD_TREE_DFS_BEGIN(node, elem) {
+ /* only term nodes with a validation callback can be in node_types */
+ if ((elem->schema->nodetype & LYD_NODE_TERM) &&
+ ((struct lysc_node_leaf *)elem->schema)->type->plugin->validate &&
+ ly_set_contains(node_types, elem, &idx)) {
+ LY_CHECK_GOTO(rc = ly_set_rm_index(node_types, idx, NULL), error);
+ }
+ LYD_TREE_DFS_END(node, elem);
+ }
+ }
+
+ /* free */
+ lyd_free_tree(node);
+ } else {
+ /* invalid data */
+ LOGVAL(LYD_CTX(node), LY_VCODE_NOWHEN, disabled->cond->expr);
+ rc = LY_EVALID;
+ goto error;
+ }
+ } else {
+ /* when true */
+ node->flags |= LYD_WHEN_TRUE;
+ }
+
+ /* remove this node from the set keeping the order, its when was resolved */
+ ly_set_rm_index_ordered(node_when, i, NULL);
+ } else if (r != LY_EINCOMPLETE) {
+ /* error */
+ rc = r;
+ goto error;
+ }
+
+ LOG_LOCBACK(1, 1, 0, 0);
+ } while (i);
+
+ return LY_SUCCESS;
+
+error:
+ LOG_LOCBACK(1, 1, 0, 0);
+ return rc;
+}
+
+LY_ERR
+lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum lyd_type data_type, struct ly_set *node_when,
+ uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_node,
+ struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t i;
+
+ if (ext_val && ext_val->count) {
+ /* first validate parsed extension data */
+ i = ext_val->count;
+ do {
+ --i;
+
+ struct lyd_ctx_ext_val *ext_v = ext_val->objs[i];
+
+ /* validate extension data */
+ ret = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
+ LY_CHECK_RET(ret);
+
+ /* remove this item from the set */
+ ly_set_rm_index(ext_val, i, free);
+ } while (i);
+ }
+
+ if (ext_node && ext_node->count) {
+ /* validate data nodes with extension instances */
+ i = ext_node->count;
+ do {
+ --i;
+
+ struct lyd_ctx_ext_node *ext_n = ext_node->objs[i];
+
+ /* validate the node */
+ ret = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node, val_opts);
+ LY_CHECK_RET(ret);
+
+ /* remove this item from the set */
+ ly_set_rm_index(ext_node, i, free);
+ } while (i);
+ }
+
+ if (node_when) {
+ /* evaluate all when conditions */
+ uint32_t prev_count;
+
+ do {
+ prev_count = node_when->count;
+ LY_CHECK_RET(lyd_validate_unres_when(tree, mod, node_when, when_xp_opts, node_types, diff));
+ /* 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);
+ }
+
+ if (node_types && node_types->count) {
+ /* finish incompletely validated terminal values (traverse from the end for efficient set removal) */
+ i = node_types->count;
+ do {
+ --i;
+
+ struct lyd_node_term *node = node_types->objs[i];
+ struct lysc_type *type = ((struct lysc_node_leaf *)node->schema)->type;
+
+ /* resolve the value of the node */
+ LOG_LOCSET(NULL, &node->node, NULL, NULL);
+ ret = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, &node->node, *tree);
+ LOG_LOCBACK(0, 1, 0, 0);
+ LY_CHECK_RET(ret);
+
+ /* remove this node from the set */
+ ly_set_rm_index(node_types, i, NULL);
+ } while (i);
+ }
+
+ if (meta_types && meta_types->count) {
+ /* ... and metadata values */
+ i = meta_types->count;
+ do {
+ --i;
+
+ struct lyd_meta *meta = meta_types->objs[i];
+ struct lysc_type *type;
+
+ /* validate and store the value of the metadata */
+ lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
+ ret = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
+ LY_CHECK_RET(ret);
+
+ /* remove this attr from the set */
+ ly_set_rm_index(meta_types, i, NULL);
+ } while (i);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Validate instance duplication.
+ *
+ * @param[in] first First sibling to search in.
+ * @param[in] node Data node instance to check.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *node)
+{
+ struct lyd_node **match_p;
+ ly_bool fail = 0;
+
+ assert(node->flags & LYD_NEW);
+
+ /* key-less list or non-configuration leaf-list */
+ if (lysc_is_dup_inst_list(node->schema)) {
+ /* duplicate instances allowed */
+ return LY_SUCCESS;
+ }
+
+ /* find exactly the same next instance using hashes if possible */
+ if (node->parent && node->parent->children_ht) {
+ if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
+ fail = 1;
+ }
+ } else {
+ for ( ; first; first = first->next) {
+ if (first == node) {
+ continue;
+ }
+
+ if (node->schema->nodetype & (LYD_NODE_ANY | LYS_LEAF)) {
+ if (first->schema == node->schema) {
+ fail = 1;
+ break;
+ }
+ } else if (!lyd_compare_single(first, node, 0)) {
+ fail = 1;
+ break;
+ }
+ }
+ }
+
+ if (fail) {
+ LOGVAL(node->schema->module->ctx, LY_VCODE_DUP, node->schema->name);
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate multiple case data existence with possible autodelete.
+ *
+ * @param[in,out] first First sibling to search in, is updated if needed.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] choic Choice node whose cases to check.
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_cases(struct lyd_node **first, const struct lys_module *mod, const struct lysc_node_choice *choic,
+ struct lyd_node **diff)
+{
+ const struct lysc_node *scase, *iter, *old_case = NULL, *new_case = NULL;
+ struct lyd_node *match, *to_del;
+ ly_bool found;
+
+ LOG_LOCSET(&choic->node, NULL, NULL, NULL);
+
+ LY_LIST_FOR((struct lysc_node *)choic->cases, scase) {
+ found = 0;
+ iter = NULL;
+ match = NULL;
+ while ((match = lys_getnext_data(match, *first, &iter, scase, NULL))) {
+ if (match->flags & LYD_NEW) {
+ /* a new case data found, nothing more to look for */
+ found = 2;
+ break;
+ } else {
+ /* and old case data found */
+ if (found == 0) {
+ found = 1;
+ }
+ }
+ }
+
+ if (found == 1) {
+ /* there should not be 2 old cases */
+ if (old_case) {
+ /* old data from 2 cases */
+ LOGVAL(choic->module->ctx, LY_VCODE_DUPCASE, old_case->name, scase->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EVALID;
+ }
+
+ /* remember an old existing case */
+ old_case = scase;
+ } else if (found == 2) {
+ if (new_case) {
+ /* new data from 2 cases */
+ LOGVAL(choic->module->ctx, LY_VCODE_DUPCASE, new_case->name, scase->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EVALID;
+ }
+
+ /* remember a new existing case */
+ new_case = scase;
+ }
+ }
+
+ LOG_LOCBACK(1, 0, 0, 0);
+
+ if (old_case && new_case) {
+ /* auto-delete old case */
+ iter = NULL;
+ match = NULL;
+ to_del = NULL;
+ while ((match = lys_getnext_data(match, *first, &iter, old_case, NULL))) {
+ lyd_del_move_root(first, to_del, mod);
+
+ /* free previous node */
+ lyd_free_tree(to_del);
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(match, LYD_DIFF_OP_DELETE, diff));
+ }
+ to_del = match;
+ }
+ lyd_del_move_root(first, to_del, mod);
+ lyd_free_tree(to_del);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check whether a schema node can have some default values (true for NP containers as well).
+ *
+ * @param[in] schema Schema node to check.
+ * @return non-zero if yes,
+ * @return 0 otherwise.
+ */
+static int
+lyd_val_has_default(const struct lysc_node *schema)
+{
+ switch (schema->nodetype) {
+ case LYS_LEAF:
+ if (((struct lysc_node_leaf *)schema)->dflt) {
+ return 1;
+ }
+ break;
+ case LYS_LEAFLIST:
+ if (((struct lysc_node_leaflist *)schema)->dflts) {
+ return 1;
+ }
+ break;
+ case LYS_CONTAINER:
+ if (!(schema->flags & LYS_PRESENCE)) {
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Properly delete a node as part of auto-delete validation tasks.
+ *
+ * @param[in,out] first First sibling, is updated if needed.
+ * @param[in] del Node instance to delete.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] node Current iteration node, update it if it is deleted.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, const struct lys_module *mod,
+ struct lyd_node **node, struct lyd_node **diff)
+{
+ struct lyd_node *iter;
+ ly_bool node_autodel = 0;
+
+ lyd_del_move_root(first, del, mod);
+ if (del == *node) {
+ *node = (*node)->next;
+ node_autodel = 1;
+ }
+ if (diff) {
+ /* add into diff */
+ if ((del->schema->nodetype == LYS_CONTAINER) && !(del->schema->flags & LYS_PRESENCE)) {
+ /* we do not want to track NP container changes, but remember any removed children */
+ LY_LIST_FOR(lyd_child(del), iter) {
+ lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
+ }
+ } else {
+ lyd_val_diff_add(del, LYD_DIFF_OP_DELETE, diff);
+ }
+ }
+ lyd_free_tree(del);
+
+ return node_autodel;
+}
+
+/**
+ * @brief Auto-delete leaf-list default instances to prevent validation errors.
+ *
+ * @param[in,out] first First sibling to search in, is updated if needed.
+ * @param[in,out] node New data node instance to check, is updated if auto-deleted.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_leaflist_dflt(struct lyd_node **first, struct lyd_node **node, const struct lys_module *mod,
+ struct lyd_node **diff)
+{
+ const struct lysc_node *schema;
+ struct lyd_node *iter, *next;
+ ly_bool found = 0, node_autodel = 0;
+
+ assert((*node)->flags & LYD_NEW);
+
+ schema = (*node)->schema;
+ assert(schema->nodetype == LYS_LEAFLIST);
+
+ /* check whether there is any explicit instance */
+ LYD_LIST_FOR_INST(*first, schema, iter) {
+ if (!(iter->flags & LYD_DEFAULT)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /* no explicit instance, keep defaults as they are */
+ return 0;
+ }
+
+ LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
+ if (iter->flags & LYD_DEFAULT) {
+ /* default instance found, remove it */
+ if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ node_autodel = 1;
+ }
+ }
+ }
+
+ return node_autodel;
+}
+
+/**
+ * @brief Auto-delete container or leaf default instances to prevent validation errors.
+ *
+ * @param[in,out] first First sibling to search in, is updated if needed.
+ * @param[in,out] node New data node instance to check, is updated if auto-deleted.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_cont_leaf_dflt(struct lyd_node **first, struct lyd_node **node, const struct lys_module *mod,
+ struct lyd_node **diff)
+{
+ const struct lysc_node *schema;
+ struct lyd_node *iter, *next;
+ ly_bool found = 0, node_autodel = 0;
+
+ assert((*node)->flags & LYD_NEW);
+
+ schema = (*node)->schema;
+ assert(schema->nodetype & (LYS_LEAF | LYS_CONTAINER));
+
+ /* check whether there is any explicit instance */
+ LYD_LIST_FOR_INST(*first, schema, iter) {
+ if (!(iter->flags & LYD_DEFAULT)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ /* remove all default instances */
+ LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
+ if (iter->flags & LYD_DEFAULT) {
+ /* default instance, remove it */
+ if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ node_autodel = 1;
+ }
+ }
+ }
+ } else {
+ /* remove a single old default instance, if any */
+ LYD_LIST_FOR_INST(*first, schema, iter) {
+ if ((iter->flags & LYD_DEFAULT) && !(iter->flags & LYD_NEW)) {
+ /* old default instance, remove it */
+ if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ node_autodel = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ return node_autodel;
+}
+
+/**
+ * @brief Auto-delete leftover default nodes of deleted cases (that have no existing explicit data).
+ *
+ * @param[in,out] first First sibling to search in, is updated if needed.
+ * @param[in,out] node Default data node instance to check.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node, const struct lys_module *mod,
+ struct lyd_node **diff)
+{
+ const struct lysc_node *schema;
+ struct lysc_node_choice *choic;
+ struct lyd_node *iter = NULL;
+ const struct lysc_node *slast = NULL;
+ ly_bool node_autodel = 0;
+
+ assert((*node)->flags & LYD_DEFAULT);
+
+ schema = (*node)->schema;
+
+ if (!schema->parent || (schema->parent->nodetype != LYS_CASE)) {
+ /* the default node is not a descendant of a case */
+ return 0;
+ }
+
+ choic = (struct lysc_node_choice *)schema->parent->parent;
+ assert(choic->nodetype == LYS_CHOICE);
+
+ if (choic->dflt && (choic->dflt == (struct lysc_node_case *)schema->parent)) {
+ /* data of a default case, keep them */
+ return 0;
+ }
+
+ /* try to find an explicit node of the case */
+ while ((iter = lys_getnext_data(iter, *first, &slast, schema->parent, NULL))) {
+ if (!(iter->flags & LYD_DEFAULT)) {
+ break;
+ }
+ }
+
+ if (!iter) {
+ /* there are only default nodes of the case meaning it does not exist and neither should any default nodes
+ * of the case, remove this one default node */
+ if (lyd_validate_autodel_node_del(first, *node, mod, node, diff)) {
+ node_autodel = 1;
+ }
+ }
+
+ return node_autodel;
+}
+
+/**
+ * @brief Validate new siblings in choices, recursively for nested choices.
+ *
+ * @param[in,out] first First sibling.
+ * @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_choice_r(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
+ struct lyd_node **diff)
+{
+ const struct lysc_node *snode = NULL;
+
+ while (*first && (snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
+ /* check case duplicites */
+ if (snode->nodetype == LYS_CHOICE) {
+ LY_CHECK_RET(lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff));
+
+ /* check for nested choice */
+ LY_CHECK_RET(lyd_validate_choice_r(first, snode, mod, diff));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
+ struct lyd_node **diff)
+{
+ LY_ERR r;
+ struct lyd_node *node;
+ const struct lysc_node *last_dflt_schema = NULL;
+
+ assert(first && (sparent || mod));
+
+ /* validate choices */
+ LY_CHECK_RET(lyd_validate_choice_r(first, sparent, mod, diff));
+
+ node = *first;
+ while (node) {
+ if (!node->schema || (mod && (lyd_owner_module(node) != mod))) {
+ /* opaque node or all top-level data from this module checked */
+ break;
+ }
+
+ if (!(node->flags & (LYD_NEW | LYD_DEFAULT))) {
+ /* check only new and default nodes */
+ node = node->next;
+ continue;
+ }
+
+ if (lyd_val_has_default(node->schema) && (node->schema != last_dflt_schema) && (node->flags & LYD_NEW)) {
+ /* remove old default(s) of the new node if an explicit instance exists */
+ last_dflt_schema = node->schema;
+ if (node->schema->nodetype == LYS_LEAFLIST) {
+ if (lyd_validate_autodel_leaflist_dflt(first, &node, mod, diff)) {
+ continue;
+ }
+ } else {
+ if (lyd_validate_autodel_cont_leaf_dflt(first, &node, mod, diff)) {
+ continue;
+ }
+ }
+ }
+
+ if (node->flags & LYD_NEW) {
+ /* then check new node instance duplicities */
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ r = lyd_validate_duplicates(*first, node);
+ LOG_LOCBACK(0, 1, 0, 0);
+ LY_CHECK_RET(r);
+
+ /* this node is valid */
+ node->flags &= ~LYD_NEW;
+ }
+
+ if (node->flags & LYD_DEFAULT) {
+ /* remove leftover default nodes from a no-longer existing case */
+ if (lyd_validate_autodel_case_dflt(first, &node, mod, diff)) {
+ continue;
+ }
+ }
+
+ /* next iter */
+ node = node->next;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate any "when" conditions of a non-existent data node with existing parent.
+ *
+ * @param[in] first First data sibling of the non-existing node.
+ * @param[in] parent Data parent of the non-existing node.
+ * @param[in] snode Schema node of the non-existing node.
+ * @param[out] disabled First when that evaluated false, if any.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+ const struct lysc_when **disabled)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *tree, *dummy = NULL;
+ uint32_t xp_opts;
+
+ /* find root */
+ if (parent) {
+ tree = (struct lyd_node *)parent;
+ while (tree->parent) {
+ tree = lyd_parent(tree);
+ }
+ tree = lyd_first_sibling(tree);
+ } else {
+ /* is the first sibling from the same module, but may not be the actual first */
+ tree = lyd_first_sibling(first);
+ }
+
+ /* create dummy opaque node */
+ ret = lyd_new_opaq((struct lyd_node *)parent, snode->module->ctx, snode->name, NULL, NULL, snode->module->name, &dummy);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* connect it if needed */
+ if (!parent) {
+ if (first) {
+ lyd_insert_sibling((struct lyd_node *)first, dummy, &tree);
+ } else {
+ assert(!tree);
+ tree = dummy;
+ }
+ }
+
+ /* explicitly specified accesible tree */
+ if (snode->flags & LYS_CONFIG_W) {
+ xp_opts = LYXP_ACCESS_TREE_CONFIG;
+ } else {
+ xp_opts = LYXP_ACCESS_TREE_ALL;
+ }
+
+ /* evaluate all when */
+ ret = lyd_validate_node_when(tree, dummy, snode, xp_opts, disabled);
+ if (ret == LY_EINCOMPLETE) {
+ /* all other when must be resolved by now */
+ LOGINT(snode->module->ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ } else if (ret) {
+ /* error */
+ goto cleanup;
+ }
+
+cleanup:
+ lyd_free_tree(dummy);
+ return ret;
+}
+
+/**
+ * @brief Validate mandatory node existence.
+ *
+ * @param[in] first First sibling to search in.
+ * @param[in] parent Data parent.
+ * @param[in] snode Schema node to validate.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode)
+{
+ const struct lysc_when *disabled;
+
+ if (snode->nodetype == LYS_CHOICE) {
+ /* some data of a choice case exist */
+ if (lys_getnext_data(NULL, first, NULL, snode, NULL)) {
+ return LY_SUCCESS;
+ }
+ } else {
+ assert(snode->nodetype & (LYS_LEAF | LYS_CONTAINER | LYD_NODE_ANY));
+
+ if (!lyd_find_sibling_val(first, snode, NULL, 0, NULL)) {
+ /* data instance found */
+ return LY_SUCCESS;
+ }
+ }
+
+ disabled = NULL;
+ if (lysc_has_when(snode)) {
+ /* if there are any when conditions, they must be true for a validation error */
+ LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
+ }
+
+ if (!disabled) {
+ /* node instance not found */
+ if (snode->nodetype == LYS_CHOICE) {
+ LOGVAL_APPTAG(snode->module->ctx, "missing-choice", LY_VCODE_NOMAND_CHOIC, snode->name);
+ } else {
+ LOGVAL(snode->module->ctx, LY_VCODE_NOMAND, snode->name);
+ }
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate min/max-elements constraints, if any.
+ *
+ * @param[in] first First sibling to search in.
+ * @param[in] parent Data parent.
+ * @param[in] snode Schema node to validate.
+ * @param[in] min Minimum number of elements, 0 for no restriction.
+ * @param[in] max Max number of elements, 0 for no restriction.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+ uint32_t min, uint32_t max)
+{
+ uint32_t count = 0;
+ struct lyd_node *iter;
+ const struct lysc_when *disabled;
+ ly_bool invalid_instance = 0;
+
+ assert(min || max);
+
+ LYD_LIST_FOR_INST(first, snode, iter) {
+ ++count;
+
+ if (min && (count == min)) {
+ /* satisfied */
+ min = 0;
+ if (!max) {
+ /* nothing more to check */
+ break;
+ }
+ }
+ if (max && (count > max)) {
+ /* not satisifed */
+ LOG_LOCSET(NULL, iter, NULL, NULL);
+ invalid_instance = 1;
+ break;
+ }
+ }
+
+ if (min) {
+ assert(count < min);
+
+ disabled = NULL;
+ if (lysc_has_when(snode)) {
+ /* if there are any when conditions, they must be true for a validation error */
+ LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
+ }
+
+ if (!disabled) {
+ LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
+ goto failure;
+ }
+ } else if (max && (count > max)) {
+ LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
+ goto failure;
+ }
+
+ return LY_SUCCESS;
+
+failure:
+ LOG_LOCBACK(0, invalid_instance, 0, 0);
+ return LY_EVALID;
+}
+
+/**
+ * @brief Find node referenced by a list unique statement.
+ *
+ * @param[in] uniq_leaf Unique leaf to find.
+ * @param[in] list List instance to use for the search.
+ * @return Found leaf,
+ * @return NULL if no leaf found.
+ */
+static struct lyd_node *
+lyd_val_uniq_find_leaf(const struct lysc_node_leaf *uniq_leaf, const struct lyd_node *list)
+{
+ struct lyd_node *node;
+ const struct lysc_node *iter;
+ size_t depth = 0, i;
+
+ /* get leaf depth */
+ for (iter = &uniq_leaf->node; iter && (iter != list->schema); iter = lysc_data_parent(iter)) {
+ ++depth;
+ }
+
+ node = (struct lyd_node *)list;
+ while (node && depth) {
+ /* find schema node with this depth */
+ for (i = depth - 1, iter = &uniq_leaf->node; i; iter = lysc_data_parent(iter)) {
+ --i;
+ }
+
+ /* find iter instance in children */
+ assert(iter->nodetype & (LYS_CONTAINER | LYS_LEAF));
+ lyd_find_sibling_val(lyd_child(node), iter, NULL, 0, &node);
+ --depth;
+ }
+
+ return node;
+}
+
+/**
+ * @brief Callback for comparing 2 list unique leaf values.
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ *
+ * @param[in] cb_data 0 to compare all uniques, n to compare only n-th unique.
+ */
+static ly_bool
+lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
+{
+ struct ly_ctx *ctx;
+ struct lysc_node_list *slist;
+ struct lyd_node *diter, *first, *second;
+ struct lyd_value *val1, *val2;
+ char *path1, *path2, *uniq_str, *ptr;
+ LY_ARRAY_COUNT_TYPE u, v, action;
+
+ assert(val1_p && val2_p);
+
+ first = *((struct lyd_node **)val1_p);
+ second = *((struct lyd_node **)val2_p);
+ action = (uintptr_t)cb_data;
+
+ assert(first && (first->schema->nodetype == LYS_LIST));
+ assert(second && (second->schema == first->schema));
+
+ ctx = first->schema->module->ctx;
+
+ slist = (struct lysc_node_list *)first->schema;
+
+ /* compare unique leaves */
+ if (action > 0) {
+ u = action - 1;
+ if (u < LY_ARRAY_COUNT(slist->uniques)) {
+ goto uniquecheck;
+ }
+ }
+ LY_ARRAY_FOR(slist->uniques, u) {
+uniquecheck:
+ LY_ARRAY_FOR(slist->uniques[u], v) {
+ /* first */
+ diter = lyd_val_uniq_find_leaf(slist->uniques[u][v], first);
+ if (diter) {
+ val1 = &((struct lyd_node_term *)diter)->value;
+ } else {
+ /* use default value */
+ val1 = slist->uniques[u][v]->dflt;
+ }
+
+ /* second */
+ diter = lyd_val_uniq_find_leaf(slist->uniques[u][v], second);
+ if (diter) {
+ val2 = &((struct lyd_node_term *)diter)->value;
+ } else {
+ /* use default value */
+ val2 = slist->uniques[u][v]->dflt;
+ }
+
+ if (!val1 || !val2 || val1->realtype->plugin->compare(val1, val2)) {
+ /* values differ or either one is not set */
+ break;
+ }
+ }
+ if (v && (v == LY_ARRAY_COUNT(slist->uniques[u]))) {
+ /* all unique leafs are the same in this set, create this nice error */
+ path1 = lyd_path(first, LYD_PATH_STD, NULL, 0);
+ path2 = lyd_path(second, LYD_PATH_STD, NULL, 0);
+
+ /* use buffer to rebuild the unique string */
+#define UNIQ_BUF_SIZE 1024
+ uniq_str = malloc(UNIQ_BUF_SIZE);
+ uniq_str[0] = '\0';
+ ptr = uniq_str;
+ LY_ARRAY_FOR(slist->uniques[u], v) {
+ if (v) {
+ strcpy(ptr, " ");
+ ++ptr;
+ }
+ ptr = lysc_path_until((struct lysc_node *)slist->uniques[u][v], &slist->node, LYSC_PATH_LOG,
+ ptr, UNIQ_BUF_SIZE - (ptr - uniq_str));
+ if (!ptr) {
+ /* path will be incomplete, whatever */
+ break;
+ }
+
+ ptr += strlen(ptr);
+ }
+ LOG_LOCSET(NULL, second, NULL, NULL);
+ LOGVAL_APPTAG(ctx, "data-not-unique", LY_VCODE_NOUNIQ, uniq_str, path1, path2);
+ LOG_LOCBACK(0, 1, 0, 0);
+
+ free(path1);
+ free(path2);
+ free(uniq_str);
+#undef UNIQ_BUF_SIZE
+
+ return 1;
+ }
+
+ if (action > 0) {
+ /* done */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Validate list unique leaves.
+ *
+ * @param[in] first First sibling to search in.
+ * @param[in] snode Schema node to validate.
+ * @param[in] uniques List unique arrays to validate.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode, const struct lysc_node_leaf ***uniques)
+{
+ const struct lyd_node *diter;
+ struct ly_set *set;
+ LY_ARRAY_COUNT_TYPE u, v, x = 0;
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t hash, i;
+ size_t key_len;
+ ly_bool dyn;
+ const void *hash_key;
+ void *cb_data;
+ struct hash_table **uniqtables = NULL;
+ struct lyd_value *val;
+ struct ly_ctx *ctx = snode->module->ctx;
+
+ assert(uniques);
+
+ /* get all list instances */
+ LY_CHECK_RET(ly_set_new(&set));
+ LY_LIST_FOR(first, diter) {
+ if (diter->schema == snode) {
+ ret = ly_set_add(set, (void *)diter, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+ if (set->count == 2) {
+ /* simple comparison */
+ if (lyd_val_uniq_list_equal(&set->objs[0], &set->objs[1], 0, (void *)0)) {
+ /* instance duplication */
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ } else if (set->count > 2) {
+ /* use hashes for comparison */
+ uniqtables = malloc(LY_ARRAY_COUNT(uniques) * sizeof *uniqtables);
+ LY_CHECK_ERR_GOTO(!uniqtables, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ x = LY_ARRAY_COUNT(uniques);
+ for (v = 0; v < x; v++) {
+ cb_data = (void *)(uintptr_t)(v + 1L);
+ uniqtables[v] = lyht_new(lyht_get_fixed_size(set->count), sizeof(struct lyd_node *),
+ lyd_val_uniq_list_equal, cb_data, 0);
+ LY_CHECK_ERR_GOTO(!uniqtables[v], LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+
+ for (i = 0; i < set->count; i++) {
+ /* loop for unique - get the hash for the instances */
+ for (u = 0; u < x; u++) {
+ val = NULL;
+ for (v = hash = 0; v < LY_ARRAY_COUNT(uniques[u]); v++) {
+ diter = lyd_val_uniq_find_leaf(uniques[u][v], set->objs[i]);
+ if (diter) {
+ val = &((struct lyd_node_term *)diter)->value;
+ } else {
+ /* use default value */
+ val = uniques[u][v]->dflt;
+ }
+ if (!val) {
+ /* unique item not present nor has default value */
+ break;
+ }
+
+ /* get hash key */
+ hash_key = val->realtype->plugin->print(NULL, val, LY_VALUE_LYB, NULL, &dyn, &key_len);
+ hash = dict_hash_multi(hash, hash_key, key_len);
+ if (dyn) {
+ free((void *)hash_key);
+ }
+ }
+ if (!val) {
+ /* skip this list instance since its unique set is incomplete */
+ continue;
+ }
+
+ /* finish the hash value */
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ /* insert into the hashtable */
+ ret = lyht_insert(uniqtables[u], &set->objs[i], hash, NULL);
+ if (ret == LY_EEXIST) {
+ /* instance duplication */
+ ret = LY_EVALID;
+ }
+ LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ }
+ }
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ for (v = 0; v < x; v++) {
+ if (!uniqtables[v]) {
+ /* failed when allocating uniquetables[j], following j are not allocated */
+ break;
+ }
+ lyht_free(uniqtables[v]);
+ }
+ free(uniqtables);
+
+ return ret;
+}
+
+/**
+ * @brief Validate data siblings based on generic schema node restrictions, recursively for schema-only nodes.
+ *
+ * @param[in] first First sibling to search in.
+ * @param[in] parent Data parent.
+ * @param[in] sparent Schema parent of the nodes to check.
+ * @param[in] mod Module of the nodes to check.
+ * @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[in] int_opts Internal parser options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_node *parent,
+ const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, uint32_t int_opts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lysc_node *snode = NULL, *scase;
+ struct lysc_node_list *slist;
+ struct lysc_node_leaflist *sllist;
+ uint32_t getnext_opts;
+
+ getnext_opts = LYS_GETNEXT_WITHCHOICE | (int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+
+ /* disabled nodes are skipped by lys_getnext */
+ while ((snode = lys_getnext(snode, sparent, mod, getnext_opts))) {
+ if ((val_opts & LYD_VALIDATE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+ continue;
+ }
+
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+
+ /* check min-elements and max-elements */
+ if (snode->nodetype == LYS_LIST) {
+ slist = (struct lysc_node_list *)snode;
+ if (slist->min || slist->max) {
+ ret = lyd_validate_minmax(first, parent, snode, slist->min, slist->max);
+ LY_CHECK_GOTO(ret, error);
+ }
+ } else if (snode->nodetype == LYS_LEAFLIST) {
+ sllist = (struct lysc_node_leaflist *)snode;
+ if (sllist->min || sllist->max) {
+ ret = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ } else if (snode->flags & LYS_MAND_TRUE) {
+ /* check generic mandatory existence */
+ ret = lyd_validate_mandatory(first, parent, snode);
+ LY_CHECK_GOTO(ret, error);
+ }
+
+ /* check unique */
+ if (snode->nodetype == LYS_LIST) {
+ slist = (struct lysc_node_list *)snode;
+ if (slist->uniques) {
+ ret = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques);
+ LY_CHECK_GOTO(ret, error);
+ }
+ }
+
+ if (snode->nodetype == LYS_CHOICE) {
+ /* find the existing case, if any */
+ LY_LIST_FOR(lysc_node_child(snode), scase) {
+ if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
+ /* validate only this case */
+ ret = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
+ LY_CHECK_GOTO(ret, error);
+ break;
+ }
+ }
+ }
+
+ LOG_LOCBACK(1, 0, 0, 0);
+ }
+
+ return LY_SUCCESS;
+
+error:
+ LOG_LOCBACK(1, 0, 0, 0);
+ return ret;
+}
+
+/**
+ * @brief Validate obsolete nodes, only warnings are printed.
+ *
+ * @param[in] node Node to check.
+ */
+static void
+lyd_validate_obsolete(const struct lyd_node *node)
+{
+ const struct lysc_node *snode;
+
+ snode = node->schema;
+ do {
+ if (snode->flags & LYS_STATUS_OBSLT) {
+ LOGWRN(snode->module->ctx, "Obsolete schema node \"%s\" instantiated in data.", snode->name);
+ break;
+ }
+
+ snode = snode->parent;
+ } while (snode && (snode->nodetype & (LYS_CHOICE | LYS_CASE)));
+}
+
+/**
+ * @brief Validate must conditions of a data node.
+ *
+ * @param[in] node Node to validate.
+ * @param[in] int_opts Internal parser options.
+ * @param[in] xpath_options Additional XPath options to use.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_must(const struct lyd_node *node, uint32_t int_opts, uint32_t xpath_options)
+{
+ LY_ERR ret;
+ struct lyxp_set xp_set;
+ struct lysc_must *musts;
+ const struct lyd_node *tree;
+ const struct lysc_node *schema;
+ const char *emsg, *eapptag;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) != (LYD_INTOPT_RPC | LYD_INTOPT_REPLY));
+ assert((int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) != (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY));
+
+ if (node->schema->nodetype & (LYS_ACTION | LYS_RPC)) {
+ if (int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION)) {
+ schema = &((struct lysc_node_action *)node->schema)->input.node;
+ } else if (int_opts & LYD_INTOPT_REPLY) {
+ schema = &((struct lysc_node_action *)node->schema)->output.node;
+ } else {
+ LOGINT_RET(LYD_CTX(node));
+ }
+ } else {
+ schema = node->schema;
+ }
+ musts = lysc_node_musts(schema);
+ if (!musts) {
+ /* no must to evaluate */
+ return LY_SUCCESS;
+ }
+
+ /* find first top-level node */
+ for (tree = node; tree->parent; tree = lyd_parent(tree)) {}
+ tree = lyd_first_sibling(tree);
+
+ LY_ARRAY_FOR(musts, u) {
+ memset(&xp_set, 0, sizeof xp_set);
+
+ /* evaluate must */
+ ret = lyxp_eval(LYD_CTX(node), musts[u].cond, node->schema->module, LY_VALUE_SCHEMA_RESOLVED,
+ musts[u].prefixes, node, node, tree, NULL, &xp_set, LYXP_SCHEMA | xpath_options);
+ if (ret == LY_EINCOMPLETE) {
+ LOGINT_RET(LYD_CTX(node));
+ } else if (ret) {
+ return ret;
+ }
+
+ /* check the result */
+ lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
+ if (!xp_set.val.bln) {
+ /* use specific error information */
+ emsg = musts[u].emsg;
+ eapptag = musts[u].eapptag ? musts[u].eapptag : "must-violation";
+ if (emsg) {
+ LOGVAL_APPTAG(LYD_CTX(node), eapptag, LYVE_DATA, "%s", emsg);
+ } else {
+ LOGVAL_APPTAG(LYD_CTX(node), eapptag, LY_VCODE_NOMUST, musts[u].cond->expr);
+ }
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Perform all remaining validation tasks, the data tree must be final when calling this function.
+ *
+ * @param[in] first First sibling.
+ * @param[in] parent Data parent.
+ * @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[in] int_opts Internal parser options.
+ * @param[in] must_xp_opts Additional XPath options to use for evaluating "must".
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
+ const struct lys_module *mod, uint32_t val_opts, uint32_t int_opts, uint32_t must_xp_opts)
+{
+ LY_ERR r;
+ const char *innode;
+ struct lyd_node *next = NULL, *node;
+
+ /* validate all restrictions of nodes themselves */
+ LY_LIST_FOR_SAFE(first, next, node) {
+ if (node->flags & LYD_EXT) {
+ /* ext instance data should have already been validated */
+ continue;
+ }
+
+ LOG_LOCSET(node->schema, node, NULL, NULL);
+
+ /* opaque data */
+ if (!node->schema) {
+ r = lyd_parse_opaq_error(node);
+ LOG_LOCBACK(0, 1, 0, 0);
+ return r;
+ }
+
+ if (!node->parent && mod && (lyd_owner_module(node) != mod)) {
+ /* all top-level data from this module checked */
+ LOG_LOCBACK(1, 1, 0, 0);
+ break;
+ }
+
+ /* no state/input/output/op data */
+ innode = NULL;
+ if ((val_opts & LYD_VALIDATE_NO_STATE) && (node->schema->flags & LYS_CONFIG_R)) {
+ innode = "state";
+ } else if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION)) && (node->schema->flags & LYS_IS_OUTPUT)) {
+ innode = "output";
+ } else if ((int_opts & LYD_INTOPT_REPLY) && (node->schema->flags & LYS_IS_INPUT)) {
+ innode = "input";
+ } else if (!(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) && (node->schema->nodetype == LYS_RPC)) {
+ innode = "rpc";
+ } else if (!(int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) && (node->schema->nodetype == LYS_ACTION)) {
+ innode = "action";
+ } else if (!(int_opts & LYD_INTOPT_NOTIF) && (node->schema->nodetype == LYS_NOTIF)) {
+ innode = "notification";
+ }
+ if (innode) {
+ LOGVAL(LYD_CTX(node), LY_VCODE_UNEXPNODE, innode, node->schema->name);
+ LOG_LOCBACK(1, 1, 0, 0);
+ return LY_EVALID;
+ }
+
+ /* obsolete data */
+ lyd_validate_obsolete(node);
+
+ /* node's musts */
+ if ((r = lyd_validate_must(node, int_opts, must_xp_opts))) {
+ LOG_LOCBACK(1, 1, 0, 0);
+ return r;
+ }
+
+ /* node value was checked by plugins */
+
+ /* next iter */
+ LOG_LOCBACK(1, 1, 0, 0);
+ }
+
+ /* validate schema-based restrictions */
+ LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts));
+
+ LY_LIST_FOR(first, node) {
+ if (!node->parent && mod && (lyd_owner_module(node) != mod)) {
+ /* all top-level data from this module checked */
+ break;
+ }
+
+ /* validate all children recursively */
+ LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts, must_xp_opts));
+
+ /* set default for containers */
+ lyd_cont_set_dflt(node);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate extension instance data by storing it in its unres set.
+ *
+ * @param[in] sibling First sibling with ::LYD_EXT flag, all the following ones are expected to have it, too.
+ * @param[in,out] ext_val Set with parsed extension instance data to validate.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_nested_ext(struct lyd_node *sibling, struct ly_set *ext_val)
+{
+ struct lyd_node *node;
+ struct lyd_ctx_ext_val *ext_v;
+ struct lysc_ext_instance *nested_exts, *ext = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* check of basic assumptions */
+ if (!sibling->parent || !sibling->parent->schema) {
+ LOGINT_RET(LYD_CTX(sibling));
+ }
+ LY_LIST_FOR(sibling, node) {
+ if (!(node->flags & LYD_EXT)) {
+ LOGINT_RET(LYD_CTX(sibling));
+ }
+ }
+
+ /* try to find the extension instance */
+ nested_exts = sibling->parent->schema->exts;
+ LY_ARRAY_FOR(nested_exts, u) {
+ if (nested_exts[u].def->plugin->validate) {
+ if (ext) {
+ /* more extension instances with validate callback */
+ LOGINT_RET(LYD_CTX(sibling));
+ }
+ ext = &nested_exts[u];
+ }
+ }
+ if (!ext) {
+ /* no extension instance with validate callback */
+ LOGINT_RET(LYD_CTX(sibling));
+ }
+
+ /* store for validation */
+ ext_v = malloc(sizeof *ext_v);
+ LY_CHECK_ERR_RET(!ext_v, LOGMEM(LYD_CTX(sibling)), LY_EMEM);
+ ext_v->ext = ext;
+ ext_v->sibling = sibling;
+ LY_CHECK_RET(ly_set_add(ext_val, ext_v, 1, NULL));
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node)
+{
+ struct lyd_ctx_ext_node *ext_n;
+ struct lysc_ext_instance *exts;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* try to find a relevant extension instance with node callback */
+ exts = node->schema->exts;
+ LY_ARRAY_FOR(exts, u) {
+ if (exts[u].def->plugin && exts[u].def->plugin->node) {
+ /* store for validation */
+ ext_n = malloc(sizeof *ext_n);
+ LY_CHECK_ERR_RET(!ext_n, LOGMEM(LYD_CTX(node)), LY_EMEM);
+ ext_n->ext = &exts[u];
+ ext_n->node = node;
+ LY_CHECK_RET(ly_set_add(ext_node, ext_n, 1, NULL));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate the whole data subtree.
+ *
+ * @param[in] root Subtree root.
+ * @param[in,out] node_when Set for nodes with when conditions.
+ * @param[in,out] node_types Set for unres node types.
+ * @param[in,out] meta_types Set for unres metadata types.
+ * @param[in,out] ext_node Set with nodes with extensions to validate.
+ * @param[in,out] ext_val Set for parsed extension data to validate.
+ * @param[in] impl_opts Implicit options, see @ref implicitoptions.
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_types,
+ struct ly_set *meta_types, struct ly_set *ext_node, struct ly_set *ext_val, uint32_t impl_opts,
+ struct lyd_node **diff)
+{
+ const struct lyd_meta *meta;
+ const struct lysc_type *type;
+ struct lyd_node *node;
+
+ LYD_TREE_DFS_BEGIN(root, node) {
+ if (node->flags & LYD_EXT) {
+ /* validate using the extension instance callback */
+ return lyd_validate_nested_ext(node, ext_val);
+ }
+
+ if (!node->schema) {
+ /* do not validate opaque nodes */
+ goto next_node;
+ }
+
+ LY_LIST_FOR(node->meta, meta) {
+ lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
+ if (type->plugin->validate) {
+ /* metadata type resolution */
+ LY_CHECK_RET(ly_set_add(meta_types, (void *)meta, 1, NULL));
+ }
+ }
+
+ if ((node->schema->nodetype & LYD_NODE_TERM) && ((struct lysc_node_leaf *)node->schema)->type->plugin->validate) {
+ /* node type resolution */
+ LY_CHECK_RET(ly_set_add(node_types, (void *)node, 1, NULL));
+ } else if (node->schema->nodetype & LYD_NODE_INNER) {
+ /* new node validation, autodelete */
+ LY_CHECK_RET(lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, diff));
+
+ /* add nested defaults */
+ LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff));
+ }
+
+ if (lysc_has_when(node->schema)) {
+ /* when evaluation */
+ LY_CHECK_RET(ly_set_add(node_when, (void *)node, 1, NULL));
+ }
+
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_RET(lyd_validate_node_ext(node, ext_node));
+
+next_node:
+ LYD_TREE_DFS_END(root, node);
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
+ ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
+ struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *first, *next, **first2, *iter;
+ const struct lys_module *mod;
+ struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
+ uint32_t i = 0;
+
+ assert(tree && ctx);
+ assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
+ (!node_when_p && !node_types_p && !meta_types_p && !ext_node_p && !ext_val_p));
+
+ if (!node_when_p) {
+ node_when_p = &node_when;
+ node_types_p = &node_types;
+ meta_types_p = &meta_types;
+ ext_node_p = &ext_node;
+ ext_val_p = &ext_val;
+ }
+
+ next = *tree;
+ while (1) {
+ if (val_opts & LYD_VALIDATE_PRESENT) {
+ mod = lyd_data_next_module(&next, &first);
+ } else {
+ mod = lyd_mod_next_module(next, module, ctx, &i, &first);
+ }
+ if (!mod) {
+ break;
+ }
+ if (!first || (first == *tree)) {
+ /* make sure first2 changes are carried to tree */
+ first2 = tree;
+ } else {
+ first2 = &first;
+ }
+
+ /* validate new top-level nodes of this module, autodelete */
+ ret = lyd_validate_new(first2, NULL, mod, diff);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* add all top-level defaults for this module, if going to validate subtree, do not add into unres sets
+ * (lyd_validate_subtree() adds all the nodes in that case) */
+ ret = lyd_new_implicit_r(NULL, first2, NULL, mod, validate_subtree ? NULL : node_when_p,
+ validate_subtree ? NULL : node_types_p, validate_subtree ? NULL : ext_node_p,
+ (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* our first module node pointer may no longer be the first */
+ first = *first2;
+ lyd_first_module_sibling(&first, mod);
+ if (!first || (first == *tree)) {
+ first2 = tree;
+ } else {
+ first2 = &first;
+ }
+
+ if (validate_subtree) {
+ /* process nested nodes */
+ LY_LIST_FOR(*first2, iter) {
+ if (lyd_owner_module(iter) != mod) {
+ break;
+ }
+
+ ret = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
+ (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+ /* finish incompletely validated terminal values/attributes and when conditions */
+ ret = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
+ ext_node_p, ext_val_p, val_opts, diff);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* perform final validation that assumes the data tree is final */
+ ret = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ ly_set_erase(&node_when, NULL);
+ ly_set_erase(&node_types, NULL);
+ ly_set_erase(&meta_types, NULL);
+ ly_set_erase(&ext_node, free);
+ ly_set_erase(&ext_val, free);
+ return ret;
+}
+
+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);
+ if (!ctx) {
+ ctx = LYD_CTX(*tree);
+ }
+ if (diff) {
+ *diff = NULL;
+ }
+
+ return lyd_validate(tree, NULL, ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL, diff);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, uint32_t val_opts, struct lyd_node **diff)
+{
+ LY_CHECK_ARG_RET(NULL, tree, *tree || module, LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+ if (diff) {
+ *diff = NULL;
+ }
+
+ return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL,
+ diff);
+}
+
+/**
+ * @brief Find nodes for merging an operation into data tree for validation.
+ *
+ * @param[in] op_tree Full operation data tree.
+ * @param[in] op_node Operation node itself.
+ * @param[in] tree Data tree to be merged into.
+ * @param[out] op_subtree Operation subtree to merge.
+ * @param[out] tree_sibling Data tree sibling to merge next to, is set if @p tree_parent is NULL.
+ * @param[out] tree_parent Data tree parent to merge into, is set if @p tree_sibling is NULL.
+ */
+static void
+lyd_val_op_merge_find(const struct lyd_node *op_tree, const struct lyd_node *op_node, const struct lyd_node *tree,
+ struct lyd_node **op_subtree, struct lyd_node **tree_sibling, struct lyd_node **tree_parent)
+{
+ const struct lyd_node *tree_iter, *op_iter;
+ struct lyd_node *match = NULL;
+ uint32_t i, cur_depth, op_depth;
+
+ *op_subtree = NULL;
+ *tree_sibling = NULL;
+ *tree_parent = NULL;
+
+ /* learn op depth (top-level being depth 0) */
+ op_depth = 0;
+ for (op_iter = op_node; op_iter != op_tree; op_iter = lyd_parent(op_iter)) {
+ ++op_depth;
+ }
+
+ /* find where to merge op */
+ tree_iter = tree;
+ cur_depth = op_depth;
+ while (cur_depth && tree_iter) {
+ /* find op iter in tree */
+ lyd_find_sibling_first(tree_iter, op_iter, &match);
+ if (!match) {
+ break;
+ }
+
+ /* move tree_iter */
+ tree_iter = lyd_child(match);
+
+ /* move depth */
+ --cur_depth;
+
+ /* find next op parent */
+ op_iter = op_node;
+ for (i = 0; i < cur_depth; ++i) {
+ op_iter = lyd_parent(op_iter);
+ }
+ }
+
+ assert(op_iter);
+ *op_subtree = (struct lyd_node *)op_iter;
+ if (!tree || tree_iter) {
+ /* there is no tree whatsoever or this is the last found sibling */
+ *tree_sibling = (struct lyd_node *)tree_iter;
+ } else {
+ /* matching parent was found but it has no children to insert next to */
+ assert(match);
+ *tree_parent = match;
+ }
+}
+
+/**
+ * @brief Validate an RPC/action request, reply, or notification.
+ *
+ * @param[in] op_tree Full operation data tree.
+ * @param[in] op_node Operation node itself.
+ * @param[in] dep_tree Tree to be used for validating references from the operation subtree.
+ * @param[in] int_opts Internal parser options.
+ * @param[in] data_type Type of validated data.
+ * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
+ * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
+ * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
+ * @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
+ * @param[in] ext_node_p Set of unres nodes with extensions to validate, if NULL a local set is used.
+ * @param[in] ext_val_p Set of parsed extension data to validate, if NULL a local set is used.
+ * @param[out] diff Optional diff with any changes made by the validation.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+static LY_ERR
+_lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struct lyd_node *dep_tree, enum lyd_type data_type,
+ uint32_t int_opts, ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p,
+ struct ly_set *meta_types_p, struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *op_sibling_before, *op_sibling_after, *child;
+ struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
+
+ assert(op_tree && op_node);
+ assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
+ (!node_when_p && !node_types_p && !meta_types_p && !ext_node_p && !ext_val_p));
+
+ if (!node_when_p) {
+ node_when_p = &node_when;
+ node_types_p = &node_types;
+ meta_types_p = &meta_types;
+ ext_node_p = &ext_node;
+ ext_val_p = &ext_val;
+ }
+
+ /* merge op_tree into dep_tree */
+ lyd_val_op_merge_find(op_tree, op_node, dep_tree, &op_subtree, &tree_sibling, &tree_parent);
+ op_sibling_before = op_subtree->prev->next ? op_subtree->prev : NULL;
+ op_sibling_after = op_subtree->next;
+ op_parent = lyd_parent(op_subtree);
+
+ lyd_unlink_tree(op_subtree);
+ lyd_insert_node(tree_parent, &tree_sibling, op_subtree, 0);
+ if (!dep_tree) {
+ dep_tree = tree_sibling;
+ }
+
+ LOG_LOCSET(NULL, op_node, NULL, NULL);
+
+ if (int_opts & LYD_INTOPT_REPLY) {
+ /* add output children defaults */
+ rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
+ ext_node_p, LYD_IMPLICIT_OUTPUT, diff);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (validate_subtree) {
+ /* skip validating the operation itself, go to children directly */
+ LY_LIST_FOR(lyd_child(op_node), child) {
+ rc = lyd_validate_subtree(child, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p, 0, diff);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+ } else {
+ if (validate_subtree) {
+ /* prevalidate whole operation subtree */
+ rc = lyd_validate_subtree(op_node, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p, 0, diff);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+ /* finish incompletely validated terminal values/attributes and when conditions on the full tree,
+ * account for unresolved 'when' that may appear in the non-validated dependency data tree */
+ LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, node_when_p, LYXP_IGNORE_WHEN,
+ node_types_p, meta_types_p, ext_node_p, ext_val_p, 0, diff), cleanup);
+
+ /* perform final validation of the operation/notification */
+ lyd_validate_obsolete(op_node);
+ LY_CHECK_GOTO(rc = lyd_validate_must(op_node, int_opts, LYXP_IGNORE_WHEN), cleanup);
+
+ /* final validation of all the descendants */
+ rc = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, int_opts, LYXP_IGNORE_WHEN);
+ LY_CHECK_GOTO(rc, cleanup);
+
+cleanup:
+ LOG_LOCBACK(0, 1, 0, 0);
+
+ /* restore operation tree */
+ lyd_unlink_tree(op_subtree);
+ if (op_sibling_before) {
+ lyd_insert_after_node(op_sibling_before, op_subtree);
+ } else if (op_sibling_after) {
+ lyd_insert_before_node(op_sibling_after, op_subtree);
+ } else if (op_parent) {
+ lyd_insert_node(op_parent, NULL, op_subtree, 0);
+ }
+
+ ly_set_erase(&node_when, NULL);
+ ly_set_erase(&node_types, NULL);
+ ly_set_erase(&meta_types, NULL);
+ ly_set_erase(&ext_node, free);
+ ly_set_erase(&ext_val, free);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *dep_tree, enum lyd_type data_type, struct lyd_node **diff)
+{
+ struct lyd_node *op_node;
+ uint32_t int_opts;
+ struct ly_set ext_val = {0};
+ LY_ERR rc;
+
+ LY_CHECK_ARG_RET(NULL, op_tree, !dep_tree || !dep_tree->parent, (data_type == LYD_TYPE_RPC_YANG) ||
+ (data_type == LYD_TYPE_NOTIF_YANG) || (data_type == LYD_TYPE_REPLY_YANG), LY_EINVAL);
+ if (diff) {
+ *diff = NULL;
+ }
+ if (data_type == LYD_TYPE_RPC_YANG) {
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ } else if (data_type == LYD_TYPE_NOTIF_YANG) {
+ int_opts = LYD_INTOPT_NOTIF;
+ } else {
+ int_opts = LYD_INTOPT_REPLY;
+ }
+
+ if (op_tree->schema && (op_tree->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) {
+ /* we have the operation/notification, adjust the pointers */
+ op_node = op_tree;
+ while (op_tree->parent) {
+ op_tree = lyd_parent(op_tree);
+ }
+ } else {
+ /* find the operation/notification */
+ while (op_tree->parent) {
+ op_tree = lyd_parent(op_tree);
+ }
+ LYD_TREE_DFS_BEGIN(op_tree, op_node) {
+ if (!op_node->schema) {
+ return lyd_parse_opaq_error(op_node);
+ } else if (op_node->flags & LYD_EXT) {
+ /* fully validate the rest using the extension instance callback */
+ LY_CHECK_RET(lyd_validate_nested_ext(op_node, &ext_val));
+ rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, NULL, 0, NULL, NULL, NULL,
+ &ext_val, 0, diff);
+ ly_set_erase(&ext_val, free);
+ return rc;
+ }
+
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) &&
+ (op_node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+ break;
+ } else if ((int_opts & LYD_INTOPT_NOTIF) && (op_node->schema->nodetype == LYS_NOTIF)) {
+ break;
+ }
+ LYD_TREE_DFS_END(op_tree, op_node);
+ }
+ }
+
+ if (int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) {
+ if (!op_node || !(op_node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+ LOGERR(LYD_CTX(op_tree), LY_EINVAL, "No RPC/action to validate found.");
+ return LY_EINVAL;
+ }
+ } else {
+ if (!op_node || (op_node->schema->nodetype != LYS_NOTIF)) {
+ LOGERR(LYD_CTX(op_tree), LY_EINVAL, "No notification to validate found.");
+ return LY_EINVAL;
+ }
+ }
+
+ /* validate */
+ return _lyd_validate_op(op_tree, op_node, dep_tree, data_type, int_opts, 1, NULL, NULL, NULL, NULL, NULL, diff);
+}
diff --git a/src/validation.h b/src/validation.h
new file mode 100644
index 0000000..c9f5da0
--- /dev/null
+++ b/src/validation.h
@@ -0,0 +1,107 @@
+/**
+ * @file validation.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Validation routines.
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_VALIDATION_H_
+#define LY_VALIDATION_H_
+
+#include <stdint.h>
+
+#include "diff.h"
+#include "log.h"
+#include "parser_data.h"
+
+struct ly_ctx;
+struct ly_set;
+struct lyd_node;
+struct lys_module;
+struct lysc_node;
+
+/**
+ * @brief Add new changes into a diff. They are always merged.
+ *
+ * @param[in] node Node/subtree to add.
+ * @param[in] op Operation of the change.
+ * @param[in,out] diff Diff to update.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff);
+
+/**
+ * @brief Finish validation of nodes and attributes. Specifically, when (is processed first) and type validation.
+ *
+ * !! It is assumed autodeleted nodes cannot be in the unresolved node type set !!
+ *
+ * @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
+ * @param[in] mod Module of the @p tree to take into consideration when deleting @p tree and moving it.
+ * If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
+ * the first top-level sibling.
+ * @param[in] data_type Validate data type.
+ * @param[in] node_when Set with nodes with "when" conditions, can be NULL.
+ * @param[in] when_xp_opts Additional XPath options to use for evaluating "when".
+ * @param[in] node_types Set with nodes with unresolved types, can be NULL
+ * @param[in] meta_types Set with metadata with unresolved types, can be NULL.
+ * @param[in] ext_node Set with nodes with extensions to validate, can be NULL.
+ * @param[in] ext_val Set with extension data to validate, can be NULL.
+ * @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum lyd_type data_type,
+ struct ly_set *node_when, uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types,
+ struct ly_set *ext_node, struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff);
+
+/**
+ * @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
+ *
+ * !! It is assumed autodeleted nodes cannot yet be in the unresolved node type set !!
+ *
+ * @param[in,out] first First sibling.
+ * @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
+ struct lyd_node **diff);
+
+/**
+ * @brief Validate data node with an extension instance, if any, by storing it in its unres set.
+ *
+ * @param[in] node Node to check for an extension instance with a node callback.
+ * @param[in,out] ext_node Set with data nodes to validate.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node);
+
+/**
+ * @brief Validate a data tree.
+ *
+ * @param[in,out] tree Data tree to validate, nodes may be autodeleted.
+ * @param[in] module Module whose data (and schema restrictions) to validate, NULL for all modules.
+ * @param[in] ctx libyang context.
+ * @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
+ * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
+ * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
+ * @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
+ * @param[in] ext_node_p Set of unres nodes with extensions to validate, if NULL a local set is used.
+ * @param[in] ext_val_p Set of unres extension data to validate, if NULL a local set is used.
+ * @param[out] diff Generated validation diff, not generated if NULL.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
+ ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
+ struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff);
+
+#endif /* LY_VALIDATION_H_ */
diff --git a/src/version.h.in b/src/version.h.in
new file mode 100644
index 0000000..1f56e10
--- /dev/null
+++ b/src/version.h.in
@@ -0,0 +1,23 @@
+/**
+ * @file version.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang version definitions
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_VERSION_H_
+#define LY_VERSION_H_
+
+#define LY_VERSION_MAJOR @LIBYANG_MAJOR_SOVERSION@ /**< libyang major version number */
+#define LY_VERSION_MINOR @LIBYANG_MINOR_SOVERSION@ /**< libyang minor version number */
+#define LY_VERSION_MICRO @LIBYANG_MICRO_SOVERSION@ /**< libyang micro version number */
+#define LY_VERSION "@LIBYANG_SOVERSION_FULL@" /**< libyang version string */
+
+#endif /* LY_VERSION_H_ */
diff --git a/src/xml.c b/src/xml.c
new file mode 100644
index 0000000..97e6abb
--- /dev/null
+++ b/src/xml.c
@@ -0,0 +1,1402 @@
+/**
+ * @file xml.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Generic XML parser implementation for libyang
+ *
+ * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "xml.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "in_internal.h"
+#include "out_internal.h"
+#include "tree.h"
+#include "tree_schema_internal.h"
+
+/* Move input p by s characters, if EOF log with lyxml_ctx c */
+#define move_input(c, s) \
+ ly_in_skip(c->in, s); \
+ LY_CHECK_ERR_RET(!c->in->current[0], LOGVAL(c->ctx, LY_VCODE_EOF), LY_EVALID)
+
+/* Ignore whitespaces in the input string p */
+#define ign_xmlws(c) \
+ while (is_xmlws(*(c)->in->current)) { \
+ if (*(c)->in->current == '\n') { \
+ LY_IN_NEW_LINE((c)->in); \
+ } \
+ ly_in_skip(c->in, 1); \
+ }
+
+static LY_ERR lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, size_t *value_len, ly_bool *ws_only,
+ ly_bool *dynamic);
+
+/**
+ * @brief Ignore and skip any characters until the delim of the size delim_len is read, including the delim
+ *
+ * @param[in] xmlctx XML parser context to provide input handler and libyang context
+ * @param[in] in input handler to read the data, it is updated only in case the section is correctly terminated.
+ * @param[in] delim Delimiter to detect end of the section.
+ * @param[in] delim_len Length of the delimiter string to use.
+ * @param[in] sectname Section name to refer in error message.
+ */
+LY_ERR
+skip_section(struct lyxml_ctx *xmlctx, const char *delim, size_t delim_len, const char *sectname)
+{
+ size_t i;
+ register const char *input, *a, *b;
+ uint64_t parsed = 0, newlines = 0;
+
+ for (input = xmlctx->in->current; *input; ++input, ++parsed) {
+ if (*input != *delim) {
+ if (*input == '\n') {
+ ++newlines;
+ }
+ continue;
+ }
+ a = input;
+ b = delim;
+ for (i = 0; i < delim_len; ++i) {
+ if (*a++ != *b++) {
+ break;
+ }
+ }
+ if (i == delim_len) {
+ /* delim found */
+ xmlctx->in->line += newlines;
+ ly_in_skip(xmlctx->in, parsed + delim_len);
+ return LY_SUCCESS;
+ }
+ }
+
+ /* delim not found,
+ * do not update input handler to refer to the beginning of the section in error message */
+ LOGVAL(xmlctx->ctx, LY_VCODE_NTERM, sectname);
+ return LY_EVALID;
+}
+
+/**
+ * @brief Check/Get an XML identifier from the input string.
+ *
+ * The identifier must have at least one valid character complying the name start character constraints.
+ * The identifier is terminated by the first character, which does not comply to the name character constraints.
+ *
+ * See https://www.w3.org/TR/xml-names/#NT-NCName
+ *
+ * @param[in] xmlctx XML context.
+ * @param[out] start Pointer to the start of the identifier.
+ * @param[out] end Pointer ot the end of the identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_parse_identifier(struct lyxml_ctx *xmlctx, const char **start, const char **end)
+{
+ const char *s, *in;
+ uint32_t c;
+ size_t parsed;
+ LY_ERR rc;
+
+ in = s = xmlctx->in->current;
+
+ /* check NameStartChar (minus colon) */
+ LY_CHECK_ERR_RET(ly_getutf8(&in, &c, &parsed),
+ LOGVAL(xmlctx->ctx, LY_VCODE_INCHAR, in[0]),
+ LY_EVALID);
+ LY_CHECK_ERR_RET(!is_xmlqnamestartchar(c),
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Identifier \"%s\" starts with an invalid character.", in - parsed),
+ LY_EVALID);
+
+ /* check rest of the identifier */
+ do {
+ /* move only successfully parsed bytes */
+ ly_in_skip(xmlctx->in, parsed);
+
+ rc = ly_getutf8(&in, &c, &parsed);
+ LY_CHECK_ERR_RET(rc, LOGVAL(xmlctx->ctx, LY_VCODE_INCHAR, in[0]), LY_EVALID);
+ } while (is_xmlqnamechar(c));
+
+ *start = s;
+ *end = xmlctx->in->current;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add namespace definition into XML context.
+ *
+ * Namespaces from a single element are supposed to be added sequentially together (not interleaved by a namespace from other
+ * element). This mimic namespace visibility, since the namespace defined in element E is not visible from its parents or
+ * siblings. On the other hand, namespace from a parent element can be redefined in a child element. This is also reflected
+ * by lyxml_ns_get() which returns the most recent namespace definition for the given prefix.
+ *
+ * When leaving processing of a subtree of some element (after it is removed from xmlctx->elements), caller is supposed to call
+ * lyxml_ns_rm() to remove all the namespaces defined in such an element from the context.
+ *
+ * @param[in] xmlctx XML context to work with.
+ * @param[in] prefix Pointer to the namespace prefix. Can be NULL for default namespace.
+ * @param[in] prefix_len Length of the prefix.
+ * @param[in] uri Namespace URI (value) to store directly. Value is always spent.
+ * @return LY_ERR values.
+ */
+LY_ERR
+lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxml_ns *ns;
+ uint32_t i;
+
+ /* check for duplicates */
+ if (xmlctx->ns.count) {
+ i = xmlctx->ns.count;
+ do {
+ --i;
+ ns = xmlctx->ns.objs[i];
+ if (ns->depth < xmlctx->elements.count) {
+ /* only namespaces of parents, no need to check further */
+ break;
+ } else if (prefix && ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
+ if (!strcmp(ns->uri, uri)) {
+ /* exact same prefix and namespace, ignore */
+ goto cleanup;
+ }
+
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Duplicate XML NS prefix \"%s\" used for namespaces \"%s\" and \"%s\".",
+ ns->prefix, ns->uri, uri);
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (!prefix && !ns->prefix) {
+ if (!strcmp(ns->uri, uri)) {
+ /* exact same default namespace, ignore */
+ goto cleanup;
+ }
+
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Duplicate default XML namespaces \"%s\" and \"%s\".", ns->uri, uri);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ } while (i);
+ }
+
+ ns = malloc(sizeof *ns);
+ LY_CHECK_ERR_GOTO(!ns, LOGMEM(xmlctx->ctx); rc = LY_EMEM, cleanup);
+
+ /* we need to connect the depth of the element where the namespace is defined with the
+ * namespace record to be able to maintain (remove) the record when the parser leaves
+ * (to its sibling or back to the parent) the element where the namespace was defined */
+ ns->depth = xmlctx->elements.count;
+
+ ns->uri = uri;
+ if (prefix) {
+ ns->prefix = strndup(prefix, prefix_len);
+ LY_CHECK_ERR_GOTO(!ns->prefix, LOGMEM(xmlctx->ctx); free(ns); rc = LY_EMEM, cleanup);
+ } else {
+ ns->prefix = NULL;
+ }
+
+ rc = ly_set_add(&xmlctx->ns, ns, 1, NULL);
+ LY_CHECK_ERR_GOTO(rc, free(ns->prefix); free(ns), cleanup);
+
+ /* successfully stored */
+ uri = NULL;
+
+cleanup:
+ free(uri);
+ return rc;
+}
+
+void
+lyxml_ns_rm(struct lyxml_ctx *xmlctx)
+{
+ for (uint32_t u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
+ if (((struct lyxml_ns *)xmlctx->ns.objs[u])->depth != xmlctx->elements.count + 1) {
+ /* we are done, the namespaces from a single element are supposed to be together */
+ break;
+ }
+ /* remove the ns structure */
+ free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
+ free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
+ free(xmlctx->ns.objs[u]);
+ --xmlctx->ns.count;
+ }
+
+ if (!xmlctx->ns.count) {
+ /* cleanup the xmlctx's namespaces storage */
+ ly_set_erase(&xmlctx->ns, NULL);
+ }
+}
+
+const struct lyxml_ns *
+lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
+{
+ struct lyxml_ns *ns;
+
+ for (uint32_t u = ns_set->count - 1; u + 1 > 0; --u) {
+ ns = (struct lyxml_ns *)ns_set->objs[u];
+ if (prefix && prefix_len) {
+ if (ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
+ return ns;
+ }
+ } else if (!ns->prefix) {
+ /* default namespace */
+ return ns;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Skip in the input until EOF or just after the opening tag.
+ * Handles special XML constructs (comment, cdata, doctype).
+ *
+ * @param[in] xmlctx XML context to use.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_skip_until_end_or_after_otag(struct lyxml_ctx *xmlctx)
+{
+ const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
+ const char *endtag, *sectname;
+ size_t endtag_len;
+
+ while (1) {
+ ign_xmlws(xmlctx);
+
+ if (xmlctx->in->current[0] == '\0') {
+ /* EOF */
+ if (xmlctx->elements.count) {
+ LOGVAL(ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+ } else if (xmlctx->in->current[0] != '<') {
+ LOGVAL(ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current), xmlctx->in->current,
+ "element tag start ('<')");
+ return LY_EVALID;
+ }
+ move_input(xmlctx, 1);
+
+ if (xmlctx->in->current[0] == '!') {
+ move_input(xmlctx, 1);
+ /* sections to ignore */
+ if (!strncmp(xmlctx->in->current, "--", 2)) {
+ /* comment */
+ move_input(xmlctx, 2);
+ sectname = "Comment";
+ endtag = "-->";
+ endtag_len = ly_strlen_const("-->");
+ } else if (!strncmp(xmlctx->in->current, "DOCTYPE", ly_strlen_const("DOCTYPE"))) {
+ /* Document type declaration - not supported */
+ LOGVAL(ctx, LY_VCODE_NSUPP, "Document Type Declaration");
+ return LY_EVALID;
+ } else {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unknown XML section \"%.20s\".", &xmlctx->in->current[-2]);
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(skip_section(xmlctx, endtag, endtag_len, sectname));
+ } else if (xmlctx->in->current[0] == '?') {
+ LY_CHECK_RET(skip_section(xmlctx, "?>", 2, "Declaration"));
+ } else {
+ /* other non-WS character */
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse QName.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[out] prefix Parsed prefix, may be NULL.
+ * @param[out] prefix_len Length of @p prefix.
+ * @param[out] name Parsed name.
+ * @param[out] name_len Length of @p name.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_parse_qname(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+ const char *start, *end;
+
+ *prefix = NULL;
+ *prefix_len = 0;
+
+ LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
+ if (end[0] == ':') {
+ /* we have prefixed identifier */
+ *prefix = start;
+ *prefix_len = end - start;
+
+ move_input(xmlctx, 1);
+ LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
+ }
+
+ *name = start;
+ *name_len = end - start;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Prepare buffer for new data.
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in,out] in XML input data.
+ * @param[in,out] offset Current offset in @p in.
+ * @param[in] need_space Needed additional free space that is allocated.
+ * @param[in,out] buf Dynamic buffer.
+ * @param[in,out] len Current @p buf length (used characters).
+ * @param[in,out] size Current @p buf size (allocated characters).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_parse_value_use_buf(const struct ly_ctx *ctx, const char **in, size_t *offset, size_t need_space, char **buf,
+ size_t *len, size_t *size)
+{
+#define BUFSIZE 24
+#define BUFSIZE_STEP 128
+
+ if (!*buf) {
+ /* prepare output buffer */
+ *buf = malloc(BUFSIZE);
+ LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
+ *size = BUFSIZE;
+ }
+
+ /* allocate needed space */
+ while (*len + *offset + need_space >= *size) {
+ *buf = ly_realloc(*buf, *size + BUFSIZE_STEP);
+ LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
+ *size += BUFSIZE_STEP;
+ }
+
+ if (*offset) {
+ /* store what we have so far */
+ memcpy(&(*buf)[*len], *in, *offset);
+ *len += *offset;
+ *in += *offset;
+ *offset = 0;
+ }
+
+ return LY_SUCCESS;
+
+#undef BUFSIZE
+#undef BUFSIZE_STEP
+}
+
+/**
+ * @brief Parse XML text content (value).
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[in] endchar Expected character to mark value end.
+ * @param[out] value Parsed value.
+ * @param[out] length Length of @p value.
+ * @param[out] ws_only Whether the value is empty/white-spaces only.
+ * @param[out] dynamic Whether the value was dynamically allocated.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *length, ly_bool *ws_only, ly_bool *dynamic)
+{
+ const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
+ const char *in = xmlctx->in->current, *start, *in_aux;
+ char *buf = NULL;
+ size_t offset; /* read offset in input buffer */
+ size_t len; /* length of the output string (write offset in output buffer) */
+ size_t size = 0; /* size of the output buffer */
+ void *p;
+ uint32_t n;
+ size_t u;
+ ly_bool ws = 1;
+
+ assert(xmlctx);
+
+ /* init */
+ start = in;
+ offset = len = 0;
+
+ /* parse */
+ while (in[offset]) {
+ if (in[offset] == '&') {
+ /* non WS */
+ ws = 0;
+
+ /* use buffer and allocate enough for the offset and next character,
+ * we will need 4 bytes at most since we support only the predefined
+ * (one-char) entities and character references */
+ LY_CHECK_RET(lyxml_parse_value_use_buf(ctx, &in, &offset, 4, &buf, &len, &size));
+
+ ++offset;
+ if (in[offset] != '#') {
+ /* entity reference - only predefined references are supported */
+ if (!strncmp(&in[offset], "lt;", ly_strlen_const("lt;"))) {
+ buf[len++] = '<';
+ in += ly_strlen_const("&lt;");
+ } else if (!strncmp(&in[offset], "gt;", ly_strlen_const("gt;"))) {
+ buf[len++] = '>';
+ in += ly_strlen_const("&gt;");
+ } else if (!strncmp(&in[offset], "amp;", ly_strlen_const("amp;"))) {
+ buf[len++] = '&';
+ in += ly_strlen_const("&amp;");
+ } else if (!strncmp(&in[offset], "apos;", ly_strlen_const("apos;"))) {
+ buf[len++] = '\'';
+ in += ly_strlen_const("&apos;");
+ } else if (!strncmp(&in[offset], "quot;", ly_strlen_const("quot;"))) {
+ buf[len++] = '\"';
+ in += ly_strlen_const("&quot;");
+ } else {
+ LOGVAL(ctx, LYVE_SYNTAX, "Entity reference \"%.*s\" not supported, only predefined references allowed.",
+ 10, &in[offset - 1]);
+ goto error;
+ }
+ offset = 0;
+ } else {
+ p = (void *)&in[offset - 1];
+ /* character reference */
+ ++offset;
+ if (isdigit(in[offset])) {
+ for (n = 0; isdigit(in[offset]); offset++) {
+ n = (LY_BASE_DEC * n) + (in[offset] - '0');
+ }
+ } else if ((in[offset] == 'x') && isxdigit(in[offset + 1])) {
+ for (n = 0, ++offset; isxdigit(in[offset]); offset++) {
+ if (isdigit(in[offset])) {
+ u = (in[offset] - '0');
+ } else if (in[offset] > 'F') {
+ u = LY_BASE_DEC + (in[offset] - 'a');
+ } else {
+ u = LY_BASE_DEC + (in[offset] - 'A');
+ }
+ n = (LY_BASE_HEX * n) + u;
+ }
+ } else {
+ LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\".", 12, p);
+ goto error;
+
+ }
+
+ if (in[offset] != ';') {
+ LOGVAL(ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(&in[offset]), &in[offset], ";");
+ goto error;
+ }
+ ++offset;
+ if (ly_pututf8(&buf[len], n, &u)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", 12, p, n);
+ goto error;
+ }
+ len += u;
+ in += offset;
+ offset = 0;
+ }
+ } else if (!strncmp(in + offset, "<![CDATA[", ly_strlen_const("<![CDATA["))) {
+ /* CDATA, find the end */
+ in_aux = strstr(in + offset + ly_strlen_const("<![CDATA["), "]]>");
+ if (!in_aux) {
+ LOGVAL(xmlctx->ctx, LY_VCODE_NTERM, "CDATA");
+ goto error;
+ }
+ u = in_aux - (in + offset + ly_strlen_const("<![CDATA["));
+
+ /* use buffer, allocate enough for the whole CDATA */
+ LY_CHECK_RET(lyxml_parse_value_use_buf(ctx, &in, &offset, u, &buf, &len, &size));
+
+ /* skip CDATA tag */
+ in += ly_strlen_const("<![CDATA[");
+ assert(!offset);
+
+ /* analyze CDATA for non WS and newline chars */
+ for (n = 0; n < u; ++n) {
+ if (in[n] == '\n') {
+ LY_IN_NEW_LINE(xmlctx->in);
+ } else if (!is_xmlws(in[n])) {
+ ws = 0;
+ }
+ }
+
+ /* copy CDATA */
+ memcpy(buf + len, in, u);
+ len += u;
+
+ /* move input skipping the end tag */
+ in += u + ly_strlen_const("]]>");
+ } else if (in[offset] == endchar) {
+ /* end of string */
+ if (buf) {
+ /* realloc exact size string */
+ buf = ly_realloc(buf, len + offset + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
+ size = len + offset + 1;
+ if (offset) {
+ memcpy(&buf[len], in, offset);
+ }
+
+ /* set terminating NULL byte */
+ buf[len + offset] = '\0';
+ }
+ len += offset;
+ in += offset;
+ goto success;
+ } else {
+ if (!is_xmlws(in[offset])) {
+ /* non WS */
+ ws = 0;
+ }
+
+ /* log lines */
+ if (in[offset] == '\n') {
+ LY_IN_NEW_LINE(xmlctx->in);
+ }
+
+ /* continue */
+ in_aux = &in[offset];
+ LY_CHECK_ERR_GOTO(ly_getutf8(&in_aux, &n, &u),
+ LOGVAL(ctx, LY_VCODE_INCHAR, in[offset]), error);
+ offset += u;
+ }
+ }
+
+ /* EOF reached before endchar */
+ LOGVAL(ctx, LY_VCODE_EOF);
+
+error:
+ free(buf);
+ return LY_EVALID;
+
+success:
+ if (buf) {
+ *value = buf;
+ *dynamic = 1;
+ } else {
+ *value = (char *)start;
+ *dynamic = 0;
+ }
+ *length = len;
+ *ws_only = ws;
+
+ xmlctx->in->current = in;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse XML closing element and match it to a stored starting element.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[in] prefix Expected closing element prefix.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] name Expected closing element name.
+ * @param[in] name_len Length of @p name.
+ * @param[in] empty Whether we are parsing a special "empty" element (with joined starting and closing tag) with no value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_close_element(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, const char *name, size_t name_len,
+ ly_bool empty)
+{
+ struct lyxml_elem *e;
+
+ /* match opening and closing element tags */
+ if (!xmlctx->elements.count) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").",
+ (int)name_len, name);
+ return LY_EVALID;
+ }
+
+ e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
+ if ((e->prefix_len != prefix_len) || (e->name_len != name_len) ||
+ (prefix_len && strncmp(prefix, e->prefix, e->prefix_len)) || strncmp(name, e->name, e->name_len)) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Opening (\"%.*s%s%.*s\") and closing (\"%.*s%s%.*s\") elements tag mismatch.",
+ (int)e->prefix_len, e->prefix ? e->prefix : "", e->prefix ? ":" : "", (int)e->name_len, e->name,
+ (int)prefix_len, prefix ? prefix : "", prefix ? ":" : "", (int)name_len, name);
+ return LY_EVALID;
+ }
+
+ /* opening and closing element tags matches, remove record from the opening tags list */
+ ly_set_rm_index(&xmlctx->elements, xmlctx->elements.count - 1, free);
+
+ /* remove also the namespaces connected with the element */
+ lyxml_ns_rm(xmlctx);
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ /* special "<elem/>" element */
+ if (empty && (xmlctx->in->current[0] == '/')) {
+ move_input(xmlctx, 1);
+ }
+
+ /* parse closing tag */
+ if (xmlctx->in->current[0] != '>') {
+ LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
+ xmlctx->in->current, "element tag termination ('>')");
+ return LY_EVALID;
+ }
+
+ /* move after closing tag without checking for EOF */
+ ly_in_skip(xmlctx->in, 1);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Store parsed opening element and parse any included namespaces.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[in] prefix Parsed starting element prefix.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] name Parsed starting element name.
+ * @param[in] name_len Length of @p name.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_open_element(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, const char *name, size_t name_len)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxml_elem *e;
+ const char *prev_input;
+ uint64_t prev_line;
+ char *value;
+ size_t parsed, value_len;
+ ly_bool ws_only, dynamic, is_ns;
+ uint32_t c;
+
+ /* store element opening tag information */
+ e = malloc(sizeof *e);
+ LY_CHECK_ERR_RET(!e, LOGMEM(xmlctx->ctx), LY_EMEM);
+ e->name = name;
+ e->prefix = prefix;
+ e->name_len = name_len;
+ e->prefix_len = prefix_len;
+
+ LY_CHECK_RET(ly_set_add(&xmlctx->elements, e, 1, NULL));
+ if (xmlctx->elements.count > LY_MAX_BLOCK_DEPTH) {
+ LOGERR(xmlctx->ctx, LY_EINVAL, "The maximum number of open elements has been exceeded.");
+ return LY_EINVAL;
+ }
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ /* parse and store all namespaces */
+ prev_input = xmlctx->in->current;
+ prev_line = xmlctx->in->line;
+ is_ns = 1;
+ while ((xmlctx->in->current[0] != '\0') && !(ret = ly_getutf8(&xmlctx->in->current, &c, &parsed))) {
+ if (!is_xmlqnamestartchar(c)) {
+ break;
+ }
+ xmlctx->in->current -= parsed;
+
+ /* parse attribute name */
+ LY_CHECK_GOTO(ret = lyxml_parse_qname(xmlctx, &prefix, &prefix_len, &name, &name_len), cleanup);
+
+ /* parse the value */
+ LY_CHECK_GOTO(ret = lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic), cleanup);
+
+ /* store every namespace */
+ if ((prefix && !ly_strncmp("xmlns", prefix, prefix_len)) || (!prefix && !ly_strncmp("xmlns", name, name_len))) {
+ ret = lyxml_ns_add(xmlctx, prefix ? name : NULL, prefix ? name_len : 0,
+ dynamic ? value : strndup(value, value_len));
+ dynamic = 0;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* not a namespace */
+ is_ns = 0;
+ }
+ if (dynamic) {
+ free(value);
+ }
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ if (is_ns) {
+ /* we can actually skip all the namespaces as there is no reason to parse them again */
+ prev_input = xmlctx->in->current;
+ prev_line = xmlctx->in->line;
+ }
+ }
+
+cleanup:
+ if (!ret) {
+ xmlctx->in->current = prev_input;
+ xmlctx->in->line = prev_line;
+ }
+ return ret;
+}
+
+/**
+ * @brief Move parser to the attribute content and parse it.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[out] value Parsed attribute value.
+ * @param[out] value_len Length of @p value.
+ * @param[out] ws_only Whether the value is empty/white-spaces only.
+ * @param[out] dynamic Whether the value was dynamically allocated.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, size_t *value_len, ly_bool *ws_only, ly_bool *dynamic)
+{
+ char quot;
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ /* skip '=' */
+ if (xmlctx->in->current[0] == '\0') {
+ LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+ } else if (xmlctx->in->current[0] != '=') {
+ LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
+ xmlctx->in->current, "'='");
+ return LY_EVALID;
+ }
+ move_input(xmlctx, 1);
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ /* find quotes */
+ if (xmlctx->in->current[0] == '\0') {
+ LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+ } else if ((xmlctx->in->current[0] != '\'') && (xmlctx->in->current[0] != '\"')) {
+ LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
+ xmlctx->in->current, "either single or double quotation mark");
+ return LY_EVALID;
+ }
+
+ /* remember quote */
+ quot = xmlctx->in->current[0];
+ move_input(xmlctx, 1);
+
+ /* parse attribute value */
+ LY_CHECK_RET(lyxml_parse_value(xmlctx, quot, (char **)value, value_len, ws_only, dynamic));
+
+ /* move after ending quote (without checking for EOF) */
+ ly_in_skip(xmlctx->in, 1);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move parser to the next attribute and parse it.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[out] prefix Parsed attribute prefix.
+ * @param[out] prefix_len Length of @p prefix.
+ * @param[out] name Parsed attribute name.
+ * @param[out] name_len Length of @p name.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_next_attribute(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+ const char *in;
+ char *value;
+ uint32_t c;
+ size_t parsed, value_len;
+ ly_bool ws_only, dynamic;
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ /* parse only possible attributes */
+ while ((xmlctx->in->current[0] != '>') && (xmlctx->in->current[0] != '/')) {
+ in = xmlctx->in->current;
+ if (in[0] == '\0') {
+ LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+ } else if ((ly_getutf8(&in, &c, &parsed) || !is_xmlqnamestartchar(c))) {
+ LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in - parsed), in - parsed,
+ "element tag end ('>' or '/>') or an attribute");
+ return LY_EVALID;
+ }
+
+ /* parse attribute name */
+ LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
+
+ if ((!*prefix || ly_strncmp("xmlns", *prefix, *prefix_len)) && (*prefix || ly_strncmp("xmlns", *name, *name_len))) {
+ /* standard attribute */
+ break;
+ }
+
+ /* namespace, skip it */
+ LY_CHECK_RET(lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic));
+ if (dynamic) {
+ free(value);
+ }
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move parser to the next element and parse it.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[out] prefix Parsed element prefix.
+ * @param[out] prefix_len Length of @p prefix.
+ * @param[out] name Parse element name.
+ * @param[out] name_len Length of @p name.
+ * @param[out] closing Flag if the element is closing (includes '/').
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyxml_next_element(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
+ ly_bool *closing)
+{
+ /* skip WS until EOF or after opening tag '<' */
+ LY_CHECK_RET(lyxml_skip_until_end_or_after_otag(xmlctx));
+ if (xmlctx->in->current[0] == '\0') {
+ /* set return values */
+ *prefix = *name = NULL;
+ *prefix_len = *name_len = 0;
+ return LY_SUCCESS;
+ }
+
+ if (xmlctx->in->current[0] == '/') {
+ move_input(xmlctx, 1);
+ *closing = 1;
+ } else {
+ *closing = 0;
+ }
+
+ /* skip WS */
+ ign_xmlws(xmlctx);
+
+ /* parse element name */
+ LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxml_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyxml_ctx **xmlctx_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx;
+ ly_bool closing;
+
+ /* new context */
+ xmlctx = calloc(1, sizeof *xmlctx);
+ LY_CHECK_ERR_RET(!xmlctx, LOGMEM(ctx), LY_EMEM);
+ xmlctx->ctx = ctx;
+ xmlctx->in = in;
+
+ LOG_LOCSET(NULL, NULL, NULL, in);
+
+ /* parse next element, if any */
+ LY_CHECK_GOTO(ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name,
+ &xmlctx->name_len, &closing), cleanup);
+
+ if (xmlctx->in->current[0] == '\0') {
+ /* update status */
+ xmlctx->status = LYXML_END;
+ } else if (closing) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").", (int)xmlctx->name_len, xmlctx->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else {
+ /* open an element, also parses all enclosed namespaces */
+ LY_CHECK_GOTO(ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len), cleanup);
+
+ /* update status */
+ xmlctx->status = LYXML_ELEMENT;
+ }
+
+cleanup:
+ if (ret) {
+ lyxml_ctx_free(xmlctx);
+ } else {
+ *xmlctx_p = xmlctx;
+ }
+ return ret;
+}
+
+LY_ERR
+lyxml_ctx_next(struct lyxml_ctx *xmlctx)
+{
+ LY_ERR ret = LY_SUCCESS;
+ ly_bool closing;
+ struct lyxml_elem *e;
+
+ /* if the value was not used, free it */
+ if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
+ free((char *)xmlctx->value);
+ xmlctx->value = NULL;
+ xmlctx->dynamic = 0;
+ }
+
+ switch (xmlctx->status) {
+ case LYXML_ELEM_CONTENT:
+ /* content |</elem> */
+
+ /* handle special case when empty content for "<elem/>" was returned */
+ if (xmlctx->in->current[0] == '/') {
+ assert(xmlctx->elements.count);
+ e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
+
+ /* close the element (parses closing tag) */
+ ret = lyxml_close_element(xmlctx, e->prefix, e->prefix_len, e->name, e->name_len, 1);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* update status */
+ xmlctx->status = LYXML_ELEM_CLOSE;
+ break;
+ }
+ /* fall through */
+ case LYXML_ELEM_CLOSE:
+ /* </elem>| <elem2>* */
+
+ /* parse next element, if any */
+ ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len, &closing);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (xmlctx->in->current[0] == '\0') {
+ /* update status */
+ xmlctx->status = LYXML_END;
+ } else if (closing) {
+ /* close an element (parses also closing tag) */
+ ret = lyxml_close_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* update status */
+ xmlctx->status = LYXML_ELEM_CLOSE;
+ } else {
+ /* open an element, also parses all enclosed namespaces */
+ ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* update status */
+ xmlctx->status = LYXML_ELEMENT;
+ }
+ break;
+
+ case LYXML_ELEMENT:
+ /* <elem| attr='val'* > content */
+ case LYXML_ATTR_CONTENT:
+ /* attr='val'| attr='val'* > content */
+
+ /* parse attribute name, if any */
+ ret = lyxml_next_attribute(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (xmlctx->in->current[0] == '>') {
+ /* no attributes but a closing tag */
+ ly_in_skip(xmlctx->in, 1);
+ if (!xmlctx->in->current[0]) {
+ LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parse element content */
+ ret = lyxml_parse_value(xmlctx, '<', (char **)&xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only,
+ &xmlctx->dynamic);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (!xmlctx->value_len) {
+ /* empty value should by alocated staticaly, but check for in any case */
+ if (xmlctx->dynamic) {
+ free((char *) xmlctx->value);
+ }
+ /* use empty value, easier to work with */
+ xmlctx->value = "";
+ xmlctx->dynamic = 0;
+ }
+
+ /* update status */
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ } else if (xmlctx->in->current[0] == '/') {
+ /* no content but we still return it */
+ xmlctx->value = "";
+ xmlctx->value_len = 0;
+ xmlctx->ws_only = 1;
+ xmlctx->dynamic = 0;
+
+ /* update status */
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ } else {
+ /* update status */
+ xmlctx->status = LYXML_ATTRIBUTE;
+ }
+ break;
+
+ case LYXML_ATTRIBUTE:
+ /* attr|='val' */
+
+ /* skip formatting and parse value */
+ ret = lyxml_next_attr_content(xmlctx, &xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only, &xmlctx->dynamic);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* update status */
+ xmlctx->status = LYXML_ATTR_CONTENT;
+ break;
+
+ case LYXML_END:
+ /* </elem> |EOF */
+ /* nothing to do */
+ break;
+ }
+
+cleanup:
+ if (ret) {
+ /* invalidate context */
+ xmlctx->status = LYXML_END;
+ }
+ return ret;
+}
+
+LY_ERR
+lyxml_ctx_peek(struct lyxml_ctx *xmlctx, enum LYXML_PARSER_STATUS *next)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *prefix, *name, *prev_input;
+ size_t prefix_len, name_len;
+ ly_bool closing;
+
+ prev_input = xmlctx->in->current;
+
+ switch (xmlctx->status) {
+ case LYXML_ELEM_CONTENT:
+ if (xmlctx->in->current[0] == '/') {
+ *next = LYXML_ELEM_CLOSE;
+ break;
+ }
+ /* fall through */
+ case LYXML_ELEM_CLOSE:
+ /* parse next element, if any */
+ ret = lyxml_next_element(xmlctx, &prefix, &prefix_len, &name, &name_len, &closing);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (xmlctx->in->current[0] == '\0') {
+ *next = LYXML_END;
+ } else if (closing) {
+ *next = LYXML_ELEM_CLOSE;
+ } else {
+ *next = LYXML_ELEMENT;
+ }
+ break;
+ case LYXML_ELEMENT:
+ case LYXML_ATTR_CONTENT:
+ /* parse attribute name, if any */
+ ret = lyxml_next_attribute(xmlctx, &prefix, &prefix_len, &name, &name_len);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if ((xmlctx->in->current[0] == '>') || (xmlctx->in->current[0] == '/')) {
+ *next = LYXML_ELEM_CONTENT;
+ } else {
+ *next = LYXML_ATTRIBUTE;
+ }
+ break;
+ case LYXML_ATTRIBUTE:
+ *next = LYXML_ATTR_CONTENT;
+ break;
+ case LYXML_END:
+ *next = LYXML_END;
+ break;
+ }
+
+cleanup:
+ xmlctx->in->current = prev_input;
+ return ret;
+}
+
+/**
+ * @brief Free all namespaces in XML context.
+ *
+ * @param[in] xmlctx XML context to use.
+ */
+static void
+lyxml_ns_rm_all(struct lyxml_ctx *xmlctx)
+{
+ struct lyxml_ns *ns;
+ uint32_t i;
+
+ for (i = 0; i < xmlctx->ns.count; ++i) {
+ ns = xmlctx->ns.objs[i];
+
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ }
+ ly_set_erase(&xmlctx->ns, NULL);
+}
+
+void
+lyxml_ctx_free(struct lyxml_ctx *xmlctx)
+{
+ if (!xmlctx) {
+ return;
+ }
+
+ LOG_LOCBACK(0, 0, 0, 1);
+
+ if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
+ free((char *)xmlctx->value);
+ }
+ ly_set_erase(&xmlctx->elements, free);
+ lyxml_ns_rm_all(xmlctx);
+ free(xmlctx);
+}
+
+/**
+ * @brief Duplicate an XML element.
+ *
+ * @param[in] elem Element to duplicate.
+ * @return Element duplicate.
+ * @return NULL on error.
+ */
+static struct lyxml_elem *
+lyxml_elem_dup(const struct lyxml_elem *elem)
+{
+ struct lyxml_elem *dup;
+
+ dup = malloc(sizeof *dup);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
+
+ memcpy(dup, elem, sizeof *dup);
+
+ return dup;
+}
+
+/**
+ * @brief Duplicate an XML namespace.
+ *
+ * @param[in] ns Namespace to duplicate.
+ * @return Namespace duplicate.
+ * @return NULL on error.
+ */
+static struct lyxml_ns *
+lyxml_ns_dup(const struct lyxml_ns *ns)
+{
+ struct lyxml_ns *dup;
+
+ dup = malloc(sizeof *dup);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
+
+ if (ns->prefix) {
+ dup->prefix = strdup(ns->prefix);
+ LY_CHECK_ERR_RET(!dup->prefix, LOGMEM(NULL); free(dup), NULL);
+ } else {
+ dup->prefix = NULL;
+ }
+ dup->uri = strdup(ns->uri);
+ LY_CHECK_ERR_RET(!dup->uri, LOGMEM(NULL); free(dup->prefix); free(dup), NULL);
+ dup->depth = ns->depth;
+
+ return dup;
+}
+
+LY_ERR
+lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
+{
+ uint32_t i;
+
+ /* first make shallow copy */
+ memcpy(backup, xmlctx, sizeof *backup);
+
+ if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
+ /* it was backed up, do not free */
+ xmlctx->dynamic = 0;
+ }
+
+ /* backup in */
+ backup->b_current = xmlctx->in->current;
+ backup->b_line = xmlctx->in->line;
+
+ /* duplicate elements */
+ backup->elements.objs = malloc(xmlctx->elements.size * sizeof(struct lyxml_elem));
+ LY_CHECK_ERR_RET(!backup->elements.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
+ for (i = 0; i < xmlctx->elements.count; ++i) {
+ backup->elements.objs[i] = lyxml_elem_dup(xmlctx->elements.objs[i]);
+ LY_CHECK_RET(!backup->elements.objs[i], LY_EMEM);
+ }
+
+ /* duplicate ns */
+ backup->ns.objs = malloc(xmlctx->ns.size * sizeof(struct lyxml_ns));
+ LY_CHECK_ERR_RET(!backup->ns.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
+ for (i = 0; i < xmlctx->ns.count; ++i) {
+ backup->ns.objs[i] = lyxml_ns_dup(xmlctx->ns.objs[i]);
+ LY_CHECK_RET(!backup->ns.objs[i], LY_EMEM);
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
+{
+ if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
+ /* free dynamic value */
+ free((char *)xmlctx->value);
+ }
+
+ /* free elements */
+ ly_set_erase(&xmlctx->elements, free);
+
+ /* free ns */
+ lyxml_ns_rm_all(xmlctx);
+
+ /* restore in */
+ xmlctx->in->current = backup->b_current;
+ xmlctx->in->line = backup->b_line;
+ backup->in = xmlctx->in;
+
+ /* restore backup */
+ memcpy(xmlctx, backup, sizeof *xmlctx);
+}
+
+LY_ERR
+lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute)
+{
+ LY_ERR ret;
+
+ if (!text) {
+ return 0;
+ }
+
+ for (uint64_t u = 0; text[u]; u++) {
+ switch (text[u]) {
+ case '&':
+ ret = ly_print_(out, "&amp;");
+ break;
+ case '<':
+ ret = ly_print_(out, "&lt;");
+ break;
+ case '>':
+ /* not needed, just for readability */
+ ret = ly_print_(out, "&gt;");
+ break;
+ case '"':
+ if (attribute) {
+ ret = ly_print_(out, "&quot;");
+ break;
+ }
+ /* fall through */
+ default:
+ ret = ly_write_(out, &text[u], 1);
+ break;
+ }
+ LY_CHECK_RET(ret);
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxml_value_compare(const struct ly_ctx *ctx1, const char *value1, void *val_prefix_data1,
+ const struct ly_ctx *ctx2, const char *value2, void *val_prefix_data2)
+{
+ const char *value1_iter, *value2_iter;
+ const char *value1_next, *value2_next;
+ uint32_t value1_len, value2_len;
+ ly_bool is_prefix1, is_prefix2;
+ const struct lys_module *mod1, *mod2;
+ LY_ERR ret;
+
+ if (!value1 && !value2) {
+ return LY_SUCCESS;
+ }
+ if ((value1 && !value2) || (!value1 && value2)) {
+ return LY_ENOT;
+ }
+
+ if (!ctx2) {
+ ctx2 = ctx1;
+ }
+
+ ret = LY_SUCCESS;
+ for (value1_iter = value1, value2_iter = value2;
+ value1_iter && value2_iter;
+ value1_iter = value1_next, value2_iter = value2_next) {
+ if ((ret = ly_value_prefix_next(value1_iter, NULL, &value1_len, &is_prefix1, &value1_next))) {
+ break;
+ }
+ if ((ret = ly_value_prefix_next(value2_iter, NULL, &value2_len, &is_prefix2, &value2_next))) {
+ break;
+ }
+
+ if (is_prefix1 != is_prefix2) {
+ ret = LY_ENOT;
+ break;
+ }
+
+ if (!is_prefix1) {
+ if (value1_len != value2_len) {
+ ret = LY_ENOT;
+ break;
+ }
+ if (strncmp(value1_iter, value2_iter, value1_len)) {
+ ret = LY_ENOT;
+ break;
+ }
+ continue;
+ }
+
+ mod1 = mod2 = NULL;
+ if (val_prefix_data1) {
+ /* find module of the first prefix, if any */
+ mod1 = ly_resolve_prefix(ctx1, value1_iter, value1_len, LY_VALUE_XML, val_prefix_data1);
+ }
+ if (val_prefix_data2) {
+ mod2 = ly_resolve_prefix(ctx2, value2_iter, value2_len, LY_VALUE_XML, val_prefix_data2);
+ }
+ if (!mod1 || !mod2) {
+ /* not a prefix or maps to different namespaces */
+ ret = LY_ENOT;
+ break;
+ }
+
+ if (mod1->ctx == mod2->ctx) {
+ /* same contexts */
+ if ((mod1->name != mod2->name) || (mod1->revision != mod2->revision)) {
+ ret = LY_ENOT;
+ break;
+ }
+ } else {
+ /* different contexts */
+ if (strcmp(mod1->name, mod2->name)) {
+ ret = LY_ENOT;
+ break;
+ }
+
+ if (mod1->revision || mod2->revision) {
+ if (!mod1->revision || !mod2->revision) {
+ ret = LY_ENOT;
+ break;
+ }
+ if (strcmp(mod1->revision, mod2->revision)) {
+ ret = LY_ENOT;
+ break;
+ }
+ }
+ }
+ }
+
+ if (value1_iter || value2_iter) {
+ ret = LY_ENOT;
+ }
+
+ return ret;
+}
diff --git a/src/xml.h b/src/xml.h
new file mode 100644
index 0000000..fc86a81
--- /dev/null
+++ b/src/xml.h
@@ -0,0 +1,209 @@
+/**
+ * @file xml.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Generic XML parser routines.
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_XML_H_
+#define LY_XML_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "set.h"
+
+struct ly_ctx;
+struct ly_in;
+struct ly_out;
+
+/* Macro to test if character is whitespace */
+#define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
+
+/* Macro to test if character is allowed to be a first character of an qualified identifier */
+#define is_xmlqnamestartchar(c) ((c >= 'a' && c <= 'z') || c == '_' || \
+ (c >= 'A' && c <= 'Z') || /* c == ':' || */ \
+ (c >= 0x370 && c <= 0x1fff && c != 0x37e ) || \
+ (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
+ c == 0x200d || (c >= 0x2070 && c <= 0x218f) || \
+ (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
+ (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+ (c >= 0x10000 && c <= 0xeffff))
+
+/* Macro to test if character is allowed to be used in an qualified identifier */
+#define is_xmlqnamechar(c) ((c >= 'a' && c <= 'z') || c == '_' || c == '-' || \
+ (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || /* c == ':' || */ \
+ c == '.' || c == 0xb7 || (c >= 0x370 && c <= 0x1fff && c != 0x37e ) ||\
+ (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
+ c == 0x200d || (c >= 0x300 && c <= 0x36f) || \
+ (c >= 0x2070 && c <= 0x218f) || (c >= 0x203f && c <= 0x2040) || \
+ (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
+ (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+ (c >= 0x10000 && c <= 0xeffff))
+
+struct lyxml_ns {
+ char *prefix; /* prefix of the namespace, NULL for the default namespace */
+ char *uri; /* namespace URI */
+ uint32_t depth; /* depth level of the element to maintain the list of accessible namespace definitions */
+};
+
+/* element tag identifier for matching opening and closing tags */
+struct lyxml_elem {
+ const char *prefix; /**< only pointer, not in dictionary */
+ const char *name; /**< only pointer, not in dictionary */
+ size_t prefix_len;
+ size_t name_len;
+};
+
+/**
+ * @brief Status of the parser providing information what is expected next (which function is supposed to be called).
+ */
+enum LYXML_PARSER_STATUS {
+ LYXML_ELEMENT, /* opening XML element parsed */
+ LYXML_ELEM_CLOSE, /* closing XML element parsed */
+ LYXML_ELEM_CONTENT, /* XML element context parsed */
+ LYXML_ATTRIBUTE, /* XML attribute parsed */
+ LYXML_ATTR_CONTENT, /* XML attribute content parsed */
+ LYXML_END /* end of input data */
+};
+
+struct lyxml_ctx {
+ const struct ly_ctx *ctx;
+ struct ly_in *in; /* input structure */
+
+ enum LYXML_PARSER_STATUS status; /* status providing information about the last parsed object, following attributes
+ are filled based on it */
+
+ union {
+ const char *prefix; /* LYXML_ELEMENT, LYXML_ATTRIBUTE - elem/attr prefix */
+ const char *value; /* LYXML_ELEM_CONTENT, LYXML_ATTR_CONTENT - elem/attr value */
+ };
+ union {
+ size_t prefix_len; /* LYXML_ELEMENT, LYXML_ATTRIBUTE - elem/attr prefix length */
+ size_t value_len; /* LYXML_ELEM_CONTENT, LYXML_ATTR_CONTENT - elem/attr value length */
+ };
+ union {
+ const char *name; /* LYXML_ELEMENT, LYXML_ATTRIBUTE - elem/attr name */
+ ly_bool ws_only; /* LYXML_ELEM_CONTENT, LYXML_ATTR_CONTENT - whether elem/attr value is empty/white-space only */
+ };
+ union {
+ size_t name_len; /* LYXML_ELEMENT, LYXML_ATTRIBUTE - elem/attr name length */
+ ly_bool dynamic; /* LYXML_ELEM_CONTENT, LYXML_ATTR_CONTENT - whether elem/attr value is dynamically allocated */
+ };
+
+ struct ly_set elements; /* list of not-yet-closed elements */
+ struct ly_set ns; /* handled with LY_SET_OPT_USEASLIST */
+
+ /* backup in members */
+ const char *b_current;
+ uint64_t b_line;
+};
+
+/**
+ * @brief Create a new XML parser context and start parsing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] in Input structure.
+ * @param[out] xmlctx New XML context with status ::LYXML_ELEMENT.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxml_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyxml_ctx **xmlctx);
+
+/**
+ * @brief Move to the next XML artefact and update parser status.
+ *
+ * LYXML_ELEMENT (-> LYXML_ATTRIBUTE -> LYXML_ATTR_CONTENT)* -> LYXML_ELEM_CONTENT -> LYXML_ELEM_CLOSE ...
+ * -> LYXML_ELEMENT ...
+ *
+ * @param[in] xmlctx XML context to move.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxml_ctx_next(struct lyxml_ctx *xmlctx);
+
+/**
+ * @brief Peek at the next XML parser status without changing it.
+ *
+ * @param[in] xmlctx XML context to use.
+ * @param[out] next Next XML parser status.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxml_ctx_peek(struct lyxml_ctx *xmlctx, enum LYXML_PARSER_STATUS *next);
+
+/**
+ * @brief Remove all the namespaces defined in the element recently closed (removed from the xmlctx->elements).
+ *
+ * @param[in] xmlctx XML context to work with.
+ */
+void lyxml_ns_rm(struct lyxml_ctx *xmlctx);
+
+/**
+ * @brief Get a namespace record for the given prefix in the current context.
+ *
+ * @param[in] ns_set Set with namespaces from the XML context.
+ * @param[in] prefix Pointer to the namespace prefix. Can be NULL for default namespace.
+ * @param[in] prefix_len Length of the prefix string (since it might not be NULL-terminated).
+ * @return The namespace record or NULL if the record for the specified prefix not found.
+ */
+const struct lyxml_ns *lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len);
+
+/**
+ * @brief Print the given @p text as XML string which replaces some of the characters which cannot appear in XML data.
+ *
+ * @param[in] out Output structure for printing.
+ * @param[in] text String to print.
+ * @param[in] attribute Flag for attribute's value where a double quotes must be replaced.
+ * @return LY_ERR values.
+ */
+LY_ERR lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute);
+
+/**
+ * @brief Remove the allocated working memory of the context.
+ *
+ * @param[in] xmlctx XML context to clear.
+ */
+void lyxml_ctx_free(struct lyxml_ctx *xmlctx);
+
+/**
+ * @brief Create a backup of XML context.
+ *
+ * @param[in] xmlctx XML context to back up.
+ * @param[out] backup Backup XML context.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup);
+
+/**
+ * @brief Restore previous backup of XML context.
+ *
+ * @param[in,out] xmlctx XML context to restore.
+ * @param[in] backup Backup XML context to restore, is unusable afterwards.
+ */
+void lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup);
+
+/**
+ * @brief Compare values and their prefix mappings.
+ *
+ * @param[in] ctx1 Libyang context for resolving prefixes in @p value1.
+ * @param[in] value1 First value.
+ * @param[in] val_prefix_data1 First value prefix data.
+ * @param[in] ctx2 Libyang context for resolving prefixes in @p value2.
+ * Can be set to NULL if @p ctx1 is equal to @p ctx2.
+ * @param[in] value2 Second value.
+ * @param[in] val_prefix_data2 Second value prefix data.
+ * @return LY_SUCCESS if values are equal.
+ * @return LY_ENOT if values are not equal.
+ * @return LY_ERR on error.
+ */
+LY_ERR lyxml_value_compare(const struct ly_ctx *ctx1, const char *value1, void *val_prefix_data1,
+ const struct ly_ctx *ctx2, const char *value2, void *val_prefix_data2);
+
+#endif /* LY_XML_H_ */
diff --git a/src/xpath.c b/src/xpath.c
new file mode 100644
index 0000000..ab7921e
--- /dev/null
+++ b/src/xpath.c
@@ -0,0 +1,9883 @@
+/**
+ * @file xpath.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG XPath evaluation functions
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "xpath.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "hash_table.h"
+#include "out.h"
+#include "parser_data.h"
+#include "path.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_types.h"
+#include "printer_data.h"
+#include "schema_compile_node.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_edit.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+
+static LY_ERR set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+ enum lyxp_axis axis, uint32_t *index_p);
+static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth);
+static LY_ERR eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype,
+ struct lyxp_set *set, uint32_t options);
+static LY_ERR moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_set *set,
+ const struct lysc_node *ctx_scnode, const struct lys_module **moveto_mod);
+static LY_ERR moveto_axis_node_next(const struct lyd_node **iter, enum lyxp_node_type *iter_type,
+ const struct lyd_node *node, enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set);
+static LY_ERR moveto_node(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname,
+ enum lyxp_axis axis, uint32_t options);
+static LY_ERR moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname,
+ enum lyxp_axis axis, uint32_t options);
+static LY_ERR moveto_op_comp(struct lyxp_set *set1, struct lyxp_set *set2, const char *op, ly_bool *result);
+
+/* Functions are divided into the following basic classes:
+ *
+ * (re)parse functions:
+ * Parse functions parse the expression into
+ * tokens (syntactic analysis).
+ * Reparse functions perform semantic analysis
+ * (do not save the result, just a check) of
+ * the expression and fill repeat indices.
+ *
+ * warn functions:
+ * Warn functions check specific reasonable conditions for schema XPath
+ * and print a warning if they are not satisfied.
+ *
+ * moveto functions:
+ * They and only they actually change the context (set).
+ *
+ * eval functions:
+ * They execute a parsed XPath expression on some data subtree.
+ */
+
+/**
+ * @brief Print the type of an XPath \p set.
+ *
+ * @param[in] set Set to use.
+ * @return Set type string.
+ */
+static const char *
+print_set_type(struct lyxp_set *set)
+{
+ switch (set->type) {
+ case LYXP_SET_NODE_SET:
+ return "node set";
+ case LYXP_SET_SCNODE_SET:
+ return "schema node set";
+ case LYXP_SET_BOOLEAN:
+ return "boolean";
+ case LYXP_SET_NUMBER:
+ return "number";
+ case LYXP_SET_STRING:
+ return "string";
+ }
+
+ return NULL;
+}
+
+const char *
+lyxp_token2str(enum lyxp_token tok)
+{
+ switch (tok) {
+ case LYXP_TOKEN_PAR1:
+ return "(";
+ case LYXP_TOKEN_PAR2:
+ return ")";
+ case LYXP_TOKEN_BRACK1:
+ return "[";
+ case LYXP_TOKEN_BRACK2:
+ return "]";
+ case LYXP_TOKEN_DOT:
+ return ".";
+ case LYXP_TOKEN_DDOT:
+ return "..";
+ case LYXP_TOKEN_AT:
+ return "@";
+ case LYXP_TOKEN_COMMA:
+ return ",";
+ case LYXP_TOKEN_NAMETEST:
+ return "NameTest";
+ case LYXP_TOKEN_NODETYPE:
+ return "NodeType";
+ case LYXP_TOKEN_VARREF:
+ return "VariableReference";
+ case LYXP_TOKEN_FUNCNAME:
+ return "FunctionName";
+ case LYXP_TOKEN_OPER_LOG:
+ return "Operator(Logic)";
+ case LYXP_TOKEN_OPER_EQUAL:
+ return "Operator(Equal)";
+ case LYXP_TOKEN_OPER_NEQUAL:
+ return "Operator(Non-equal)";
+ case LYXP_TOKEN_OPER_COMP:
+ return "Operator(Comparison)";
+ case LYXP_TOKEN_OPER_MATH:
+ return "Operator(Math)";
+ case LYXP_TOKEN_OPER_UNI:
+ return "Operator(Union)";
+ case LYXP_TOKEN_OPER_PATH:
+ return "Operator(Path)";
+ case LYXP_TOKEN_OPER_RPATH:
+ return "Operator(Recursive Path)";
+ case LYXP_TOKEN_LITERAL:
+ return "Literal";
+ case LYXP_TOKEN_NUMBER:
+ return "Number";
+ default:
+ LOGINT(NULL);
+ return "";
+ }
+}
+
+/**
+ * @brief Transform string into an axis.
+ *
+ * @param[in] str String to transform.
+ * @param[in] str_len Length of @p str.
+ * @return Transformed axis.
+ */
+static enum lyxp_axis
+str2axis(const char *str, uint32_t str_len)
+{
+ switch (str_len) {
+ case 4:
+ assert(!strncmp("self", str, str_len));
+ return LYXP_AXIS_SELF;
+ case 5:
+ assert(!strncmp("child", str, str_len));
+ return LYXP_AXIS_CHILD;
+ case 6:
+ assert(!strncmp("parent", str, str_len));
+ return LYXP_AXIS_PARENT;
+ case 8:
+ assert(!strncmp("ancestor", str, str_len));
+ return LYXP_AXIS_ANCESTOR;
+ case 9:
+ if (str[0] == 'a') {
+ assert(!strncmp("attribute", str, str_len));
+ return LYXP_AXIS_ATTRIBUTE;
+ } else if (str[0] == 'f') {
+ assert(!strncmp("following", str, str_len));
+ return LYXP_AXIS_FOLLOWING;
+ } else {
+ assert(!strncmp("preceding", str, str_len));
+ return LYXP_AXIS_PRECEDING;
+ }
+ break;
+ case 10:
+ assert(!strncmp("descendant", str, str_len));
+ return LYXP_AXIS_DESCENDANT;
+ case 16:
+ assert(!strncmp("ancestor-or-self", str, str_len));
+ return LYXP_AXIS_ANCESTOR_OR_SELF;
+ case 17:
+ if (str[0] == 'f') {
+ assert(!strncmp("following-sibling", str, str_len));
+ return LYXP_AXIS_FOLLOWING_SIBLING;
+ } else {
+ assert(!strncmp("preceding-sibling", str, str_len));
+ return LYXP_AXIS_PRECEDING_SIBLING;
+ }
+ break;
+ case 18:
+ assert(!strncmp("descendant-or-self", str, str_len));
+ return LYXP_AXIS_DESCENDANT_OR_SELF;
+ }
+
+ LOGINT(NULL);
+ return 0;
+}
+
+/**
+ * @brief Print the whole expression \p exp to debug output.
+ *
+ * @param[in] exp Expression to use.
+ */
+static void
+print_expr_struct_debug(const struct lyxp_expr *exp)
+{
+#define MSG_BUFFER_SIZE 128
+ char tmp[MSG_BUFFER_SIZE];
+ uint32_t i, j;
+
+ if (!exp || (ly_ll < LY_LLDBG)) {
+ return;
+ }
+
+ LOGDBG(LY_LDGXPATH, "expression \"%s\":", exp->expr);
+ for (i = 0; i < exp->used; ++i) {
+ sprintf(tmp, "\ttoken %s, in expression \"%.*s\"", lyxp_token2str(exp->tokens[i]), exp->tok_len[i],
+ &exp->expr[exp->tok_pos[i]]);
+ if (exp->repeat && exp->repeat[i]) {
+ sprintf(tmp + strlen(tmp), " (repeat %d", exp->repeat[i][0]);
+ for (j = 1; exp->repeat[i][j]; ++j) {
+ sprintf(tmp + strlen(tmp), ", %d", exp->repeat[i][j]);
+ }
+ strcat(tmp, ")");
+ }
+ LOGDBG(LY_LDGXPATH, tmp);
+ }
+#undef MSG_BUFFER_SIZE
+}
+
+#ifndef NDEBUG
+
+/**
+ * @brief Print XPath set content to debug output.
+ *
+ * @param[in] set Set to print.
+ */
+static void
+print_set_debug(struct lyxp_set *set)
+{
+ uint32_t i;
+ char *str;
+ struct lyxp_set_node *item;
+ struct lyxp_set_scnode *sitem;
+
+ if (ly_ll < LY_LLDBG) {
+ return;
+ }
+
+ switch (set->type) {
+ case LYXP_SET_NODE_SET:
+ LOGDBG(LY_LDGXPATH, "set NODE SET:");
+ for (i = 0; i < set->used; ++i) {
+ item = &set->val.nodes[i];
+
+ switch (item->type) {
+ case LYXP_NODE_NONE:
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): NONE", i + 1, item->pos);
+ break;
+ case LYXP_NODE_ROOT:
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT", i + 1, item->pos);
+ break;
+ case LYXP_NODE_ROOT_CONFIG:
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT CONFIG", i + 1, item->pos);
+ break;
+ case LYXP_NODE_ELEM:
+ if ((item->node->schema->nodetype == LYS_LIST) && (lyd_child(item->node)->schema->nodetype == LYS_LEAF)) {
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (1st child val: %s)", i + 1, item->pos,
+ item->node->schema->name, lyd_get_value(lyd_child(item->node)));
+ } else if (item->node->schema->nodetype == LYS_LEAFLIST) {
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (val: %s)", i + 1, item->pos,
+ item->node->schema->name, lyd_get_value(item->node));
+ } else {
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, item->node->schema->name);
+ }
+ break;
+ case LYXP_NODE_TEXT:
+ if (item->node->schema->nodetype & LYS_ANYDATA) {
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT <%s>", i + 1, item->pos,
+ item->node->schema->nodetype == LYS_ANYXML ? "anyxml" : "anydata");
+ } else {
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT %s", i + 1, item->pos, lyd_get_value(item->node));
+ }
+ break;
+ case LYXP_NODE_META:
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): META %s = %s", i + 1, item->pos, set->val.meta[i].meta->name,
+ set->val.meta[i].meta->value);
+ break;
+ }
+ }
+ break;
+
+ case LYXP_SET_SCNODE_SET:
+ LOGDBG(LY_LDGXPATH, "set SCNODE SET:");
+ for (i = 0; i < set->used; ++i) {
+ sitem = &set->val.scnodes[i];
+
+ switch (sitem->type) {
+ case LYXP_NODE_ROOT:
+ LOGDBG(LY_LDGXPATH, "\t%d (%u): ROOT", i + 1, sitem->in_ctx);
+ break;
+ case LYXP_NODE_ROOT_CONFIG:
+ LOGDBG(LY_LDGXPATH, "\t%d (%u): ROOT CONFIG", i + 1, sitem->in_ctx);
+ break;
+ case LYXP_NODE_ELEM:
+ LOGDBG(LY_LDGXPATH, "\t%d (%u): ELEM %s", i + 1, sitem->in_ctx, sitem->scnode->name);
+ break;
+ default:
+ LOGINT(NULL);
+ break;
+ }
+ }
+ break;
+
+ case LYXP_SET_BOOLEAN:
+ LOGDBG(LY_LDGXPATH, "set BOOLEAN");
+ LOGDBG(LY_LDGXPATH, "\t%s", (set->val.bln ? "true" : "false"));
+ break;
+
+ case LYXP_SET_STRING:
+ LOGDBG(LY_LDGXPATH, "set STRING");
+ LOGDBG(LY_LDGXPATH, "\t%s", set->val.str);
+ break;
+
+ case LYXP_SET_NUMBER:
+ LOGDBG(LY_LDGXPATH, "set NUMBER");
+
+ if (isnan(set->val.num)) {
+ str = strdup("NaN");
+ } else if ((set->val.num == 0) || (set->val.num == -0.0f)) {
+ str = strdup("0");
+ } else if (isinf(set->val.num) && !signbit(set->val.num)) {
+ str = strdup("Infinity");
+ } else if (isinf(set->val.num) && signbit(set->val.num)) {
+ str = strdup("-Infinity");
+ } else if ((long long)set->val.num == set->val.num) {
+ if (asprintf(&str, "%lld", (long long)set->val.num) == -1) {
+ str = NULL;
+ }
+ } else {
+ if (asprintf(&str, "%03.1Lf", set->val.num) == -1) {
+ str = NULL;
+ }
+ }
+ LY_CHECK_ERR_RET(!str, LOGMEM(NULL), );
+
+ LOGDBG(LY_LDGXPATH, "\t%s", str);
+ free(str);
+ }
+}
+
+#endif
+
+/**
+ * @brief Realloc the string \p str.
+ *
+ * @param[in] ctx libyang context for logging.
+ * @param[in] needed How much free space is required.
+ * @param[in,out] str Pointer to the string to use.
+ * @param[in,out] used Used bytes in \p str.
+ * @param[in,out] size Allocated bytes in \p str.
+ * @return LY_ERR
+ */
+static LY_ERR
+cast_string_realloc(const struct ly_ctx *ctx, uint64_t needed, char **str, uint32_t *used, uint32_t *size)
+{
+ if (*size - (unsigned)*used < needed) {
+ do {
+ if ((UINT32_MAX - *size) < LYXP_STRING_CAST_SIZE_STEP) {
+ LOGERR(ctx, LY_EINVAL, "XPath string length limit (%" PRIu32 ") reached.", UINT32_MAX);
+ return LY_EINVAL;
+ }
+ *size += LYXP_STRING_CAST_SIZE_STEP;
+ } while (*size - (unsigned)*used < needed);
+ *str = ly_realloc(*str, *size * sizeof(char));
+ LY_CHECK_ERR_RET(!(*str), LOGMEM(ctx), LY_EMEM);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Cast nodes recursively to one string @p str.
+ *
+ * @param[in] node Node to cast, NULL if root.
+ * @param[in] set XPath set.
+ * @param[in] indent Current indent.
+ * @param[in,out] str Resulting string.
+ * @param[in,out] used Used bytes in @p str.
+ * @param[in,out] size Allocated bytes in @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint32_t indent, char **str, uint32_t *used,
+ uint32_t *size)
+{
+ char *buf, *line, *ptr = NULL;
+ const char *value_str;
+ const struct lyd_node *child;
+ enum lyxp_node_type child_type;
+ struct lyd_node *tree;
+ struct lyd_node_any *any;
+ LY_ERR rc;
+
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && (node->schema->flags & LYS_CONFIG_R)) {
+ return LY_SUCCESS;
+ }
+
+ if (!node) {
+ /* fake container */
+ LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
+ strcpy(*str + (*used - 1), "\n");
+ ++(*used);
+
+ ++indent;
+
+ /* print all the top-level nodes */
+ child = NULL;
+ child_type = 0;
+ while (!moveto_axis_node_next(&child, &child_type, NULL, set->root_type, LYXP_AXIS_CHILD, set)) {
+ LY_CHECK_RET(cast_string_recursive(child, set, indent, str, used, size));
+ }
+
+ /* end fake container */
+ LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
+ strcpy(*str + (*used - 1), "\n");
+ ++(*used);
+
+ --indent;
+ } else {
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_RPC:
+ case LYS_NOTIF:
+ LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
+ strcpy(*str + (*used - 1), "\n");
+ ++(*used);
+
+ for (child = lyd_child(node); child; child = child->next) {
+ LY_CHECK_RET(cast_string_recursive(child, set, indent + 1, str, used, size));
+ }
+
+ break;
+
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ value_str = lyd_get_value(node);
+
+ /* print indent */
+ LY_CHECK_RET(cast_string_realloc(set->ctx, indent * 2 + strlen(value_str) + 1, str, used, size));
+ memset(*str + (*used - 1), ' ', indent * 2);
+ *used += indent * 2;
+
+ /* print value */
+ if (*used == 1) {
+ sprintf(*str + (*used - 1), "%s", value_str);
+ *used += strlen(value_str);
+ } else {
+ sprintf(*str + (*used - 1), "%s\n", value_str);
+ *used += strlen(value_str) + 1;
+ }
+
+ break;
+
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ any = (struct lyd_node_any *)node;
+ if (!(void *)any->value.tree) {
+ /* no content */
+ buf = strdup("");
+ LY_CHECK_ERR_RET(!buf, LOGMEM(set->ctx), LY_EMEM);
+ } else {
+ struct ly_out *out;
+
+ if (any->value_type == LYD_ANYDATA_LYB) {
+ /* try to parse it into a data tree */
+ if (lyd_parse_data_mem((struct ly_ctx *)set->ctx, any->value.mem, LYD_LYB,
+ LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
+ /* successfully parsed */
+ free(any->value.mem);
+ any->value.tree = tree;
+ any->value_type = LYD_ANYDATA_DATATREE;
+ }
+ /* error is covered by the following switch where LYD_ANYDATA_LYB causes failure */
+ }
+
+ switch (any->value_type) {
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ buf = strdup(any->value.json);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(set->ctx), LY_EMEM);
+ break;
+ case LYD_ANYDATA_DATATREE:
+ LY_CHECK_RET(ly_out_new_memory(&buf, 0, &out));
+ rc = lyd_print_all(out, any->value.tree, LYD_XML, 0);
+ ly_out_free(out, NULL, 0);
+ LY_CHECK_RET(rc < 0, -rc);
+ break;
+ case LYD_ANYDATA_LYB:
+ LOGERR(set->ctx, LY_EINVAL, "Cannot convert LYB anydata into string.");
+ return LY_EINVAL;
+ }
+ }
+
+ line = strtok_r(buf, "\n", &ptr);
+ do {
+ rc = cast_string_realloc(set->ctx, indent * 2 + strlen(line) + 1, str, used, size);
+ if (rc != LY_SUCCESS) {
+ free(buf);
+ return rc;
+ }
+ memset(*str + (*used - 1), ' ', indent * 2);
+ *used += indent * 2;
+
+ strcpy(*str + (*used - 1), line);
+ *used += strlen(line);
+
+ strcpy(*str + (*used - 1), "\n");
+ *used += 1;
+ } while ((line = strtok_r(NULL, "\n", &ptr)));
+
+ free(buf);
+ break;
+
+ default:
+ LOGINT_RET(set->ctx);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Cast an element into a string.
+ *
+ * @param[in] node Node to cast, NULL if root.
+ * @param[in] set XPath set.
+ * @param[out] str Element cast to dynamically-allocated string.
+ * @return LY_ERR
+ */
+static LY_ERR
+cast_string_elem(const struct lyd_node *node, struct lyxp_set *set, char **str)
+{
+ uint32_t used, size;
+ LY_ERR rc;
+
+ *str = malloc(LYXP_STRING_CAST_SIZE_START * sizeof(char));
+ LY_CHECK_ERR_RET(!*str, LOGMEM(set->ctx), LY_EMEM);
+ (*str)[0] = '\0';
+ used = 1;
+ size = LYXP_STRING_CAST_SIZE_START;
+
+ rc = cast_string_recursive(node, set, 0, str, &used, &size);
+ if (rc != LY_SUCCESS) {
+ free(*str);
+ return rc;
+ }
+
+ if (size > used) {
+ *str = ly_realloc(*str, used * sizeof(char));
+ LY_CHECK_ERR_RET(!*str, LOGMEM(set->ctx), LY_EMEM);
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Cast a LYXP_SET_NODE_SET set into a string.
+ * Context position aware.
+ *
+ * @param[in] set Set to cast.
+ * @param[out] str Cast dynamically-allocated string.
+ * @return LY_ERR
+ */
+static LY_ERR
+cast_node_set_to_string(struct lyxp_set *set, char **str)
+{
+ if (!set->used) {
+ *str = strdup("");
+ if (!*str) {
+ LOGMEM_RET(set->ctx);
+ }
+ return LY_SUCCESS;
+ }
+
+ switch (set->val.nodes[0].type) {
+ case LYXP_NODE_NONE:
+ /* invalid */
+ LOGINT_RET(set->ctx);
+ case LYXP_NODE_ROOT:
+ case LYXP_NODE_ROOT_CONFIG:
+ case LYXP_NODE_ELEM:
+ case LYXP_NODE_TEXT:
+ return cast_string_elem(set->val.nodes[0].node, set, str);
+ case LYXP_NODE_META:
+ *str = strdup(lyd_get_meta_value(set->val.meta[0].meta));
+ if (!*str) {
+ LOGMEM_RET(set->ctx);
+ }
+ return LY_SUCCESS;
+ }
+
+ LOGINT_RET(set->ctx);
+}
+
+/**
+ * @brief Cast a string into an XPath number.
+ *
+ * @param[in] str String to use.
+ * @return Cast number.
+ */
+static long double
+cast_string_to_number(const char *str)
+{
+ long double num;
+ char *ptr;
+
+ errno = 0;
+ num = strtold(str, &ptr);
+ if (errno || *ptr) {
+ num = NAN;
+ }
+ return num;
+}
+
+/**
+ * @brief Callback for checking value equality.
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ *
+ * @param[in] val1_p First value.
+ * @param[in] val2_p Second value.
+ * @param[in] mod Whether hash table is being modified.
+ * @param[in] cb_data Callback data.
+ * @return Boolean value whether values are equal or not.
+ */
+static ly_bool
+set_values_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct lyxp_set_hash_node *val1, *val2;
+
+ val1 = (struct lyxp_set_hash_node *)val1_p;
+ val2 = (struct lyxp_set_hash_node *)val2_p;
+
+ if ((val1->node == val2->node) && (val1->type == val2->type)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Insert node and its hash into set.
+ *
+ * @param[in] set et to insert to.
+ * @param[in] node Node with hash.
+ * @param[in] type Node type.
+ */
+static void
+set_insert_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type)
+{
+ LY_ERR r;
+ uint32_t i, hash;
+ struct lyxp_set_hash_node hnode;
+
+ if (!set->ht && (set->used >= LYD_HT_MIN_ITEMS)) {
+ /* create hash table and add all the nodes */
+ set->ht = lyht_new(1, sizeof(struct lyxp_set_hash_node), set_values_equal_cb, NULL, 1);
+ for (i = 0; i < set->used; ++i) {
+ hnode.node = set->val.nodes[i].node;
+ hnode.type = set->val.nodes[i].type;
+
+ hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ r = lyht_insert(set->ht, &hnode, hash, NULL);
+ assert(!r);
+ (void)r;
+
+ if (hnode.node == node) {
+ /* it was just added, do not add it twice */
+ node = NULL;
+ }
+ }
+ }
+
+ if (set->ht && node) {
+ /* add the new node into hash table */
+ hnode.node = node;
+ hnode.type = type;
+
+ hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ r = lyht_insert(set->ht, &hnode, hash, NULL);
+ assert(!r);
+ (void)r;
+ }
+}
+
+/**
+ * @brief Remove node and its hash from set.
+ *
+ * @param[in] set Set to remove from.
+ * @param[in] node Node to remove.
+ * @param[in] type Node type.
+ */
+static void
+set_remove_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type)
+{
+ LY_ERR r;
+ struct lyxp_set_hash_node hnode;
+ uint32_t hash;
+
+ if (set->ht) {
+ hnode.node = node;
+ hnode.type = type;
+
+ hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ r = lyht_remove(set->ht, &hnode, hash);
+ assert(!r);
+ (void)r;
+
+ if (!set->ht->used) {
+ lyht_free(set->ht);
+ set->ht = NULL;
+ }
+ }
+}
+
+/**
+ * @brief Check whether node is in set based on its hash.
+ *
+ * @param[in] set Set to search in.
+ * @param[in] node Node to search for.
+ * @param[in] type Node type.
+ * @param[in] skip_idx Index in @p set to skip.
+ * @return LY_ERR
+ */
+static LY_ERR
+set_dup_node_hash_check(const struct lyxp_set *set, struct lyd_node *node, enum lyxp_node_type type, int skip_idx)
+{
+ struct lyxp_set_hash_node hnode, *match_p;
+ uint32_t hash;
+
+ hnode.node = node;
+ hnode.type = type;
+
+ hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ if (!lyht_find(set->ht, &hnode, hash, (void **)&match_p)) {
+ if ((skip_idx > -1) && (set->val.nodes[skip_idx].node == match_p->node) && (set->val.nodes[skip_idx].type == match_p->type)) {
+ /* we found it on the index that should be skipped, find another */
+ hnode = *match_p;
+ if (lyht_find_next(set->ht, &hnode, hash, (void **)&match_p)) {
+ /* none other found */
+ return LY_SUCCESS;
+ }
+ }
+
+ return LY_EEXIST;
+ }
+
+ /* not found */
+ return LY_SUCCESS;
+}
+
+void
+lyxp_set_free_content(struct lyxp_set *set)
+{
+ if (!set) {
+ return;
+ }
+
+ if (set->type == LYXP_SET_NODE_SET) {
+ free(set->val.nodes);
+ lyht_free(set->ht);
+ } else if (set->type == LYXP_SET_SCNODE_SET) {
+ free(set->val.scnodes);
+ lyht_free(set->ht);
+ } else {
+ if (set->type == LYXP_SET_STRING) {
+ free(set->val.str);
+ }
+ set->type = LYXP_SET_NODE_SET;
+ }
+
+ set->val.nodes = NULL;
+ set->used = 0;
+ set->size = 0;
+ set->ht = NULL;
+ set->ctx_pos = 0;
+ set->ctx_size = 0;
+}
+
+/**
+ * @brief Free dynamically-allocated set.
+ *
+ * @param[in] set Set to free.
+ */
+static void
+lyxp_set_free(struct lyxp_set *set)
+{
+ if (!set) {
+ return;
+ }
+
+ lyxp_set_free_content(set);
+ free(set);
+}
+
+/**
+ * @brief Initialize set context.
+ *
+ * @param[in] new Set to initialize.
+ * @param[in] set Arbitrary initialized set.
+ */
+static void
+set_init(struct lyxp_set *new, const struct lyxp_set *set)
+{
+ memset(new, 0, sizeof *new);
+ if (set) {
+ new->non_child_axis = set->non_child_axis;
+ new->ctx = set->ctx;
+ new->cur_node = set->cur_node;
+ new->root_type = set->root_type;
+ new->context_op = set->context_op;
+ new->tree = set->tree;
+ new->cur_mod = set->cur_mod;
+ new->format = set->format;
+ new->prefix_data = set->prefix_data;
+ new->vars = set->vars;
+ }
+}
+
+/**
+ * @brief Create a deep copy of a set.
+ *
+ * @param[in] set Set to copy.
+ * @return Copy of @p set.
+ */
+static struct lyxp_set *
+set_copy(struct lyxp_set *set)
+{
+ struct lyxp_set *ret;
+ uint32_t i;
+
+ if (!set) {
+ return NULL;
+ }
+
+ ret = malloc(sizeof *ret);
+ LY_CHECK_ERR_RET(!ret, LOGMEM(set->ctx), NULL);
+ set_init(ret, set);
+
+ if (set->type == LYXP_SET_SCNODE_SET) {
+ ret->type = set->type;
+
+ for (i = 0; i < set->used; ++i) {
+ if ((set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) ||
+ (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START)) {
+ uint32_t idx;
+
+ LY_CHECK_ERR_RET(set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
+ set->val.scnodes[i].axis, &idx), lyxp_set_free(ret), NULL);
+ /* coverity seems to think scnodes can be NULL */
+ if (!ret->val.scnodes) {
+ lyxp_set_free(ret);
+ return NULL;
+ }
+ ret->val.scnodes[idx].in_ctx = set->val.scnodes[i].in_ctx;
+ }
+ }
+ } else if (set->type == LYXP_SET_NODE_SET) {
+ ret->type = set->type;
+ if (set->used) {
+ ret->val.nodes = malloc(set->used * sizeof *ret->val.nodes);
+ LY_CHECK_ERR_RET(!ret->val.nodes, LOGMEM(set->ctx); free(ret), NULL);
+ memcpy(ret->val.nodes, set->val.nodes, set->used * sizeof *ret->val.nodes);
+ } else {
+ ret->val.nodes = NULL;
+ }
+
+ ret->used = ret->size = set->used;
+ ret->ctx_pos = set->ctx_pos;
+ ret->ctx_size = set->ctx_size;
+ if (set->ht) {
+ ret->ht = lyht_dup(set->ht);
+ }
+ } else {
+ memcpy(ret, set, sizeof *ret);
+ if (set->type == LYXP_SET_STRING) {
+ ret->val.str = strdup(set->val.str);
+ LY_CHECK_ERR_RET(!ret->val.str, LOGMEM(set->ctx); free(ret), NULL);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Fill XPath set with a string. Any current data are disposed of.
+ *
+ * @param[in] set Set to fill.
+ * @param[in] string String to fill into \p set.
+ * @param[in] str_len Length of \p string. 0 is a valid value!
+ */
+static void
+set_fill_string(struct lyxp_set *set, const char *string, uint32_t str_len)
+{
+ lyxp_set_free_content(set);
+
+ set->type = LYXP_SET_STRING;
+ if ((str_len == 0) && (string[0] != '\0')) {
+ string = "";
+ }
+ set->val.str = strndup(string, str_len);
+}
+
+/**
+ * @brief Fill XPath set with a number. Any current data are disposed of.
+ *
+ * @param[in] set Set to fill.
+ * @param[in] number Number to fill into \p set.
+ */
+static void
+set_fill_number(struct lyxp_set *set, long double number)
+{
+ lyxp_set_free_content(set);
+
+ set->type = LYXP_SET_NUMBER;
+ set->val.num = number;
+}
+
+/**
+ * @brief Fill XPath set with a boolean. Any current data are disposed of.
+ *
+ * @param[in] set Set to fill.
+ * @param[in] boolean Boolean to fill into \p set.
+ */
+static void
+set_fill_boolean(struct lyxp_set *set, ly_bool boolean)
+{
+ lyxp_set_free_content(set);
+
+ set->type = LYXP_SET_BOOLEAN;
+ set->val.bln = boolean;
+}
+
+/**
+ * @brief Fill XPath set with the value from another set (deep assign).
+ * Any current data are disposed of.
+ *
+ * @param[in] trg Set to fill.
+ * @param[in] src Source set to copy into \p trg.
+ */
+static void
+set_fill_set(struct lyxp_set *trg, const struct lyxp_set *src)
+{
+ if (!trg || !src) {
+ return;
+ }
+
+ if (trg->type == LYXP_SET_NODE_SET) {
+ free(trg->val.nodes);
+ } else if (trg->type == LYXP_SET_STRING) {
+ free(trg->val.str);
+ }
+ set_init(trg, src);
+
+ if (src->type == LYXP_SET_SCNODE_SET) {
+ trg->type = LYXP_SET_SCNODE_SET;
+ trg->used = src->used;
+ trg->size = src->used;
+
+ if (trg->size) {
+ trg->val.scnodes = ly_realloc(trg->val.scnodes, trg->size * sizeof *trg->val.scnodes);
+ LY_CHECK_ERR_RET(!trg->val.scnodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
+ memcpy(trg->val.scnodes, src->val.scnodes, src->used * sizeof *src->val.scnodes);
+ } else {
+ trg->val.scnodes = NULL;
+ }
+ } else if (src->type == LYXP_SET_BOOLEAN) {
+ set_fill_boolean(trg, src->val.bln);
+ } else if (src->type == LYXP_SET_NUMBER) {
+ set_fill_number(trg, src->val.num);
+ } else if (src->type == LYXP_SET_STRING) {
+ set_fill_string(trg, src->val.str, strlen(src->val.str));
+ } else {
+ if (trg->type == LYXP_SET_NODE_SET) {
+ free(trg->val.nodes);
+ } else if (trg->type == LYXP_SET_STRING) {
+ free(trg->val.str);
+ }
+
+ assert(src->type == LYXP_SET_NODE_SET);
+
+ trg->type = LYXP_SET_NODE_SET;
+ trg->used = src->used;
+ trg->size = src->used;
+ trg->ctx_pos = src->ctx_pos;
+ trg->ctx_size = src->ctx_size;
+
+ if (trg->size) {
+ trg->val.nodes = malloc(trg->size * sizeof *trg->val.nodes);
+ LY_CHECK_ERR_RET(!trg->val.nodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
+ memcpy(trg->val.nodes, src->val.nodes, src->used * sizeof *src->val.nodes);
+ } else {
+ trg->val.nodes = NULL;
+ }
+ if (src->ht) {
+ trg->ht = lyht_dup(src->ht);
+ } else {
+ trg->ht = NULL;
+ }
+ }
+}
+
+/**
+ * @brief Clear context of all schema nodes.
+ *
+ * @param[in] set Set to clear.
+ * @param[in] new_ctx New context state for all the nodes currently in the context.
+ */
+static void
+set_scnode_clear_ctx(struct lyxp_set *set, int32_t new_ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ set->val.scnodes[i].in_ctx = new_ctx;
+ } else if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START) {
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_START_USED;
+ }
+ }
+}
+
+/**
+ * @brief Remove a node from a set. Removing last node changes
+ * set into LYXP_SET_EMPTY. Context position aware.
+ *
+ * @param[in] set Set to use.
+ * @param[in] idx Index from @p set of the node to be removed.
+ */
+static void
+set_remove_node(struct lyxp_set *set, uint32_t idx)
+{
+ assert(set && (set->type == LYXP_SET_NODE_SET));
+ assert(idx < set->used);
+
+ set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
+
+ --set->used;
+ if (idx < set->used) {
+ memmove(&set->val.nodes[idx], &set->val.nodes[idx + 1], (set->used - idx) * sizeof *set->val.nodes);
+ } else if (!set->used) {
+ lyxp_set_free_content(set);
+ }
+}
+
+/**
+ * @brief Remove a node from a set by setting its type to LYXP_NODE_NONE.
+ *
+ * @param[in] set Set to use.
+ * @param[in] idx Index from @p set of the node to be removed.
+ */
+static void
+set_remove_node_none(struct lyxp_set *set, uint32_t idx)
+{
+ assert(set && (set->type == LYXP_SET_NODE_SET));
+ assert(idx < set->used);
+
+ if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
+ set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
+ }
+ set->val.nodes[idx].type = LYXP_NODE_NONE;
+}
+
+/**
+ * @brief Remove all LYXP_NODE_NONE nodes from a set. Removing last node changes
+ * set into LYXP_SET_EMPTY. Context position aware.
+ *
+ * @param[in] set Set to consolidate.
+ */
+static void
+set_remove_nodes_none(struct lyxp_set *set)
+{
+ uint32_t i, orig_used, end = 0;
+ int64_t start;
+
+ assert(set);
+
+ orig_used = set->used;
+ set->used = 0;
+ for (i = 0; i < orig_used; ) {
+ start = -1;
+ do {
+ if ((set->val.nodes[i].type != LYXP_NODE_NONE) && (start == -1)) {
+ start = i;
+ } else if ((start > -1) && (set->val.nodes[i].type == LYXP_NODE_NONE)) {
+ end = i;
+ ++i;
+ break;
+ }
+
+ ++i;
+ if (i == orig_used) {
+ end = i;
+ }
+ } while (i < orig_used);
+
+ if (start > -1) {
+ /* move the whole chunk of valid nodes together */
+ if (set->used != (unsigned)start) {
+ memmove(&set->val.nodes[set->used], &set->val.nodes[start], (end - start) * sizeof *set->val.nodes);
+ }
+ set->used += end - start;
+ }
+ }
+}
+
+/**
+ * @brief Check for duplicates in a node set.
+ *
+ * @param[in] set Set to check.
+ * @param[in] node Node to look for in @p set.
+ * @param[in] node_type Type of @p node.
+ * @param[in] skip_idx Index from @p set to skip.
+ * @return LY_ERR
+ */
+static LY_ERR
+set_dup_node_check(const struct lyxp_set *set, const struct lyd_node *node, enum lyxp_node_type node_type, int skip_idx)
+{
+ uint32_t i;
+
+ if (set->ht && node) {
+ return set_dup_node_hash_check(set, (struct lyd_node *)node, node_type, skip_idx);
+ }
+
+ for (i = 0; i < set->used; ++i) {
+ if ((skip_idx > -1) && (i == (unsigned)skip_idx)) {
+ continue;
+ }
+
+ if ((set->val.nodes[i].node == node) && (set->val.nodes[i].type == node_type)) {
+ return LY_EEXIST;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+ly_bool
+lyxp_set_scnode_contains(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type, int skip_idx,
+ uint32_t *index_p)
+{
+ uint32_t i;
+
+ for (i = 0; i < set->used; ++i) {
+ if ((skip_idx > -1) && (i == (unsigned)skip_idx)) {
+ continue;
+ }
+
+ if ((set->val.scnodes[i].scnode == node) && (set->val.scnodes[i].type == node_type)) {
+ if (index_p) {
+ *index_p = i;
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2)
+{
+ uint32_t orig_used, i, j;
+
+ assert((set1->type == LYXP_SET_SCNODE_SET) && (set2->type == LYXP_SET_SCNODE_SET));
+
+ if (!set2->used) {
+ return;
+ }
+
+ if (!set1->used) {
+ /* release hidden allocated data (lyxp_set.size) */
+ lyxp_set_free_content(set1);
+ /* direct copying of the entire structure */
+ memcpy(set1, set2, sizeof *set1);
+ return;
+ }
+
+ if (set1->used + set2->used > set1->size) {
+ set1->size = set1->used + set2->used;
+ set1->val.scnodes = ly_realloc(set1->val.scnodes, set1->size * sizeof *set1->val.scnodes);
+ LY_CHECK_ERR_RET(!set1->val.scnodes, LOGMEM(set1->ctx), );
+ }
+
+ orig_used = set1->used;
+
+ for (i = 0; i < set2->used; ++i) {
+ for (j = 0; j < orig_used; ++j) {
+ /* detect duplicities */
+ if (set1->val.scnodes[j].scnode == set2->val.scnodes[i].scnode) {
+ break;
+ }
+ }
+
+ if (j < orig_used) {
+ /* node is there, but update its status if needed */
+ if (set1->val.scnodes[j].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ set1->val.scnodes[j].in_ctx = set2->val.scnodes[i].in_ctx;
+ } else if ((set1->val.scnodes[j].in_ctx == LYXP_SET_SCNODE_ATOM_NODE) &&
+ (set2->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
+ set1->val.scnodes[j].in_ctx = set2->val.scnodes[i].in_ctx;
+ }
+ } else {
+ memcpy(&set1->val.scnodes[set1->used], &set2->val.scnodes[i], sizeof *set2->val.scnodes);
+ ++set1->used;
+ }
+ }
+
+ lyxp_set_free_content(set2);
+ set2->type = LYXP_SET_SCNODE_SET;
+}
+
+/**
+ * @brief Insert a node into a set. Context position aware.
+ *
+ * @param[in] set Set to use.
+ * @param[in] node Node to insert to @p set.
+ * @param[in] pos Sort position of @p node. If left 0, it is filled just before sorting.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] idx Index in @p set to insert into.
+ */
+static void
+set_insert_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos, enum lyxp_node_type node_type, uint32_t idx)
+{
+ assert(set && (set->type == LYXP_SET_NODE_SET));
+
+ if (!set->size) {
+ /* first item */
+ if (idx) {
+ /* no real harm done, but it is a bug */
+ LOGINT(set->ctx);
+ idx = 0;
+ }
+ set->val.nodes = malloc(LYXP_SET_SIZE_START * sizeof *set->val.nodes);
+ LY_CHECK_ERR_RET(!set->val.nodes, LOGMEM(set->ctx), );
+ set->type = LYXP_SET_NODE_SET;
+ set->used = 0;
+ set->size = LYXP_SET_SIZE_START;
+ set->ctx_pos = 1;
+ set->ctx_size = 1;
+ set->ht = NULL;
+ } else {
+ /* not an empty set */
+ if (set->used == set->size) {
+
+ /* set is full */
+ set->val.nodes = ly_realloc(set->val.nodes, (set->size * LYXP_SET_SIZE_MUL_STEP) * sizeof *set->val.nodes);
+ LY_CHECK_ERR_RET(!set->val.nodes, LOGMEM(set->ctx), );
+ set->size *= LYXP_SET_SIZE_MUL_STEP;
+ }
+
+ if (idx > set->used) {
+ LOGINT(set->ctx);
+ idx = set->used;
+ }
+
+ /* make space for the new node */
+ if (idx < set->used) {
+ memmove(&set->val.nodes[idx + 1], &set->val.nodes[idx], (set->used - idx) * sizeof *set->val.nodes);
+ }
+ }
+
+ /* finally assign the value */
+ set->val.nodes[idx].node = (struct lyd_node *)node;
+ set->val.nodes[idx].type = node_type;
+ set->val.nodes[idx].pos = pos;
+ ++set->used;
+
+ /* add into hash table */
+ set_insert_node_hash(set, (struct lyd_node *)node, node_type);
+}
+
+/**
+ * @brief Insert schema node into set.
+ *
+ * @param[in] set Set to insert into.
+ * @param[in] node Node to insert.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] axis Axis that @p node was reached on.
+ * @param[out] index_p Optional pointer to store index if the inserted @p node.
+ * @return LY_SUCCESS on success.
+ * @return LY_EMEM on memory allocation failure.
+ */
+static LY_ERR
+set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+ enum lyxp_axis axis, uint32_t *index_p)
+{
+ uint32_t index;
+
+ assert(set->type == LYXP_SET_SCNODE_SET);
+
+ if (!set->size) {
+ /* first item */
+ set->val.scnodes = malloc(LYXP_SET_SIZE_START * sizeof *set->val.scnodes);
+ LY_CHECK_ERR_RET(!set->val.scnodes, LOGMEM(set->ctx), LY_EMEM);
+ set->type = LYXP_SET_SCNODE_SET;
+ set->used = 0;
+ set->size = LYXP_SET_SIZE_START;
+ set->ctx_pos = 1;
+ set->ctx_size = 1;
+ set->ht = NULL;
+ }
+
+ if (lyxp_set_scnode_contains(set, node, node_type, -1, &index)) {
+ /* BUG if axes differs, this new one is thrown away */
+ set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ } else {
+ if (set->used == set->size) {
+ set->val.scnodes = ly_realloc(set->val.scnodes, (set->size * LYXP_SET_SIZE_MUL_STEP) * sizeof *set->val.scnodes);
+ LY_CHECK_ERR_RET(!set->val.scnodes, LOGMEM(set->ctx), LY_EMEM);
+ set->size *= LYXP_SET_SIZE_MUL_STEP;
+ }
+
+ index = set->used;
+ set->val.scnodes[index].scnode = (struct lysc_node *)node;
+ set->val.scnodes[index].type = node_type;
+ set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ set->val.scnodes[index].axis = axis;
+ ++set->used;
+ }
+
+ if (index_p) {
+ *index_p = index;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Set all nodes with ctx 1 to a new unique context value.
+ *
+ * @param[in] set Set to modify.
+ * @return New context value.
+ */
+static int32_t
+set_scnode_new_in_ctx(struct lyxp_set *set)
+{
+ uint32_t i;
+ int32_t ret_ctx;
+
+ assert(set->type == LYXP_SET_SCNODE_SET);
+
+ ret_ctx = LYXP_SET_SCNODE_ATOM_PRED_CTX;
+retry:
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx >= ret_ctx) {
+ ret_ctx = set->val.scnodes[i].in_ctx + 1;
+ goto retry;
+ }
+ }
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ set->val.scnodes[i].in_ctx = ret_ctx;
+ }
+ }
+
+ return ret_ctx;
+}
+
+/**
+ * @brief Get unique @p node position in the data.
+ *
+ * @param[in] node Node to find.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] root Root node.
+ * @param[in] root_type Type of the XPath @p root node.
+ * @param[in] prev Node that we think is before @p node in DFS from @p root. Can optionally
+ * be used to increase efficiency and start the DFS from this node.
+ * @param[in] prev_pos Node @p prev position. Optional, but must be set if @p prev is set.
+ * @return Node position.
+ */
+static uint32_t
+get_node_pos(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyd_node *root,
+ enum lyxp_node_type root_type, const struct lyd_node **prev, uint32_t *prev_pos)
+{
+ const struct lyd_node *elem = NULL, *top_sibling;
+ uint32_t pos = 1;
+ ly_bool found = 0;
+
+ assert(prev && prev_pos && !root->prev->next);
+
+ if ((node_type == LYXP_NODE_ROOT) || (node_type == LYXP_NODE_ROOT_CONFIG)) {
+ return 0;
+ }
+
+ if (*prev) {
+ /* start from the previous element instead from the root */
+ pos = *prev_pos;
+ for (top_sibling = *prev; top_sibling->parent; top_sibling = lyd_parent(top_sibling)) {}
+ goto dfs_search;
+ }
+
+ LY_LIST_FOR(root, top_sibling) {
+ LYD_TREE_DFS_BEGIN(top_sibling, elem) {
+dfs_search:
+ LYD_TREE_DFS_continue = 0;
+
+ if (*prev && !elem) {
+ /* resume previous DFS */
+ elem = LYD_TREE_DFS_next = (struct lyd_node *)*prev;
+ LYD_TREE_DFS_continue = 0;
+ }
+
+ if (!elem->schema || ((root_type == LYXP_NODE_ROOT_CONFIG) && (elem->schema->flags & LYS_CONFIG_R))) {
+ /* skip */
+ LYD_TREE_DFS_continue = 1;
+ } else {
+ if (elem == node) {
+ found = 1;
+ break;
+ }
+ ++pos;
+ }
+
+ LYD_TREE_DFS_END(top_sibling, elem);
+ }
+
+ /* node found */
+ if (found) {
+ break;
+ }
+ }
+
+ if (!found) {
+ if (!(*prev)) {
+ /* we went from root and failed to find it, cannot be */
+ LOGINT(LYD_CTX(node));
+ return 0;
+ } else {
+ /* start the search again from the beginning */
+ *prev = root;
+
+ top_sibling = root;
+ pos = 1;
+ goto dfs_search;
+ }
+ }
+
+ /* remember the last found node for next time */
+ *prev = node;
+ *prev_pos = pos;
+
+ return pos;
+}
+
+/**
+ * @brief Assign (fill) missing node positions.
+ *
+ * @param[in] set Set to fill positions in.
+ * @param[in] root Context root node.
+ * @param[in] root_type Context root type.
+ * @return LY_ERR
+ */
+static LY_ERR
+set_assign_pos(struct lyxp_set *set, const struct lyd_node *root, enum lyxp_node_type root_type)
+{
+ const struct lyd_node *prev = NULL, *tmp_node;
+ uint32_t i, tmp_pos = 0;
+
+ for (i = 0; i < set->used; ++i) {
+ if (!set->val.nodes[i].pos) {
+ tmp_node = NULL;
+ switch (set->val.nodes[i].type) {
+ case LYXP_NODE_META:
+ tmp_node = set->val.meta[i].meta->parent;
+ if (!tmp_node) {
+ LOGINT_RET(root->schema->module->ctx);
+ }
+ /* fall through */
+ case LYXP_NODE_ELEM:
+ case LYXP_NODE_TEXT:
+ if (!tmp_node) {
+ tmp_node = set->val.nodes[i].node;
+ }
+ set->val.nodes[i].pos = get_node_pos(tmp_node, set->val.nodes[i].type, root, root_type, &prev, &tmp_pos);
+ break;
+ default:
+ /* all roots have position 0 */
+ break;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get unique @p meta position in the parent metadata.
+ *
+ * @param[in] meta Metadata to use.
+ * @return Metadata position.
+ */
+static uint32_t
+get_meta_pos(struct lyd_meta *meta)
+{
+ uint32_t pos = 0;
+ struct lyd_meta *meta2;
+
+ for (meta2 = meta->parent->meta; meta2 && (meta2 != meta); meta2 = meta2->next) {
+ ++pos;
+ }
+
+ assert(meta2);
+ return pos;
+}
+
+/**
+ * @brief Compare 2 nodes in respect to XPath document order.
+ *
+ * @param[in] item1 1st node.
+ * @param[in] item2 2nd node.
+ * @return If 1st > 2nd returns 1, 1st == 2nd returns 0, and 1st < 2nd returns -1.
+ */
+static int
+set_sort_compare(struct lyxp_set_node *item1, struct lyxp_set_node *item2)
+{
+ uint32_t meta_pos1 = 0, meta_pos2 = 0;
+
+ if (item1->pos < item2->pos) {
+ return -1;
+ }
+
+ if (item1->pos > item2->pos) {
+ return 1;
+ }
+
+ /* node positions are equal, the fun case */
+
+ /* 1st ELEM - == - 2nd TEXT, 1st TEXT - == - 2nd ELEM */
+ /* special case since text nodes are actually saved as their parents */
+ if ((item1->node == item2->node) && (item1->type != item2->type)) {
+ if (item1->type == LYXP_NODE_ELEM) {
+ assert(item2->type == LYXP_NODE_TEXT);
+ return -1;
+ } else {
+ assert((item1->type == LYXP_NODE_TEXT) && (item2->type == LYXP_NODE_ELEM));
+ return 1;
+ }
+ }
+
+ /* we need meta positions now */
+ if (item1->type == LYXP_NODE_META) {
+ meta_pos1 = get_meta_pos((struct lyd_meta *)item1->node);
+ }
+ if (item2->type == LYXP_NODE_META) {
+ meta_pos2 = get_meta_pos((struct lyd_meta *)item2->node);
+ }
+
+ /* 1st ROOT - 2nd ROOT, 1st ELEM - 2nd ELEM, 1st TEXT - 2nd TEXT, 1st META - =pos= - 2nd META */
+ /* check for duplicates */
+ if (item1->node == item2->node) {
+ assert((item1->type == item2->type) && ((item1->type != LYXP_NODE_META) || (meta_pos1 == meta_pos2)));
+ return 0;
+ }
+
+ /* 1st ELEM - 2nd TEXT, 1st ELEM - any pos - 2nd META */
+ /* elem is always first, 2nd node is after it */
+ if (item1->type == LYXP_NODE_ELEM) {
+ assert(item2->type != LYXP_NODE_ELEM);
+ return -1;
+ }
+
+ /* 1st TEXT - 2nd ELEM, 1st TEXT - any pos - 2nd META, 1st META - any pos - 2nd ELEM, 1st META - >pos> - 2nd META */
+ /* 2nd is before 1st */
+ if (((item1->type == LYXP_NODE_TEXT) &&
+ ((item2->type == LYXP_NODE_ELEM) || (item2->type == LYXP_NODE_META))) ||
+ ((item1->type == LYXP_NODE_META) && (item2->type == LYXP_NODE_ELEM)) ||
+ (((item1->type == LYXP_NODE_META) && (item2->type == LYXP_NODE_META)) &&
+ (meta_pos1 > meta_pos2))) {
+ return 1;
+ }
+
+ /* 1st META - any pos - 2nd TEXT, 1st META <pos< - 2nd META */
+ /* 2nd is after 1st */
+ return -1;
+}
+
+/**
+ * @brief Set cast for comparisons.
+ *
+ * @param[in,out] trg Target set to cast source into.
+ * @param[in] src Source set.
+ * @param[in] type Target set type.
+ * @param[in] src_idx Source set node index.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+set_comp_cast(struct lyxp_set *trg, const struct lyxp_set *src, enum lyxp_set_type type, uint32_t src_idx)
+{
+ assert(src->type == LYXP_SET_NODE_SET);
+
+ set_init(trg, src);
+
+ /* insert node into target set */
+ set_insert_node(trg, src->val.nodes[src_idx].node, src->val.nodes[src_idx].pos, src->val.nodes[src_idx].type, 0);
+
+ /* cast target set appropriately */
+ return lyxp_set_cast(trg, type);
+}
+
+/**
+ * @brief Set content canonization for comparisons.
+ *
+ * @param[in,out] set Set to canonize.
+ * @param[in] xp_node Source XPath node/meta to use for canonization.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+set_comp_canonize(struct lyxp_set *set, const struct lyxp_set_node *xp_node)
+{
+ const struct lysc_type *type = NULL;
+ struct lyd_value val;
+ struct ly_err_item *err = NULL;
+ LY_ERR rc;
+
+ /* is there anything to canonize even? */
+ if (set->type == LYXP_SET_STRING) {
+ /* do we have a type to use for canonization? */
+ if ((xp_node->type == LYXP_NODE_ELEM) && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
+ type = ((struct lyd_node_term *)xp_node->node)->value.realtype;
+ } else if (xp_node->type == LYXP_NODE_META) {
+ type = ((struct lyd_meta *)xp_node->node)->value.realtype;
+ }
+ }
+ if (!type) {
+ /* no canonization needed/possible */
+ return LY_SUCCESS;
+ }
+
+ /* check for built-in types without required canonization */
+ if ((type->basetype == LY_TYPE_STRING) && (type->plugin->store == lyplg_type_store_string)) {
+ /* string */
+ return LY_SUCCESS;
+ }
+ if ((type->basetype == LY_TYPE_BOOL) && (type->plugin->store == lyplg_type_store_boolean)) {
+ /* boolean */
+ return LY_SUCCESS;
+ }
+ if ((type->basetype == LY_TYPE_ENUM) && (type->plugin->store == lyplg_type_store_enum)) {
+ /* enumeration */
+ return LY_SUCCESS;
+ }
+
+ /* print canonized string, ignore errors, the value may not satisfy schema constraints */
+ rc = type->plugin->store(set->ctx, type, set->val.str, strlen(set->val.str), 0, set->format, set->prefix_data,
+ LYD_HINT_DATA, xp_node->node->schema, &val, NULL, &err);
+ ly_err_free(err);
+ if (rc) {
+ /* invalid value, function store automaticaly dealloc value when fail */
+ return LY_SUCCESS;
+ }
+
+ /* use the canonized string value */
+ free(set->val.str);
+ set->val.str = strdup(lyd_value_get_canonical(set->ctx, &val));
+ type->plugin->free(set->ctx, &val);
+ LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), LY_EMEM);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Bubble sort @p set into XPath document order.
+ * Context position aware.
+ *
+ * @param[in] set Set to sort.
+ * @return How many times the whole set was traversed - 1 (if set was sorted, returns 0).
+ */
+static int
+set_sort(struct lyxp_set *set)
+{
+ uint32_t i, j;
+ int ret = 0, cmp;
+ ly_bool inverted, change;
+ const struct lyd_node *root;
+ struct lyxp_set_node item;
+ struct lyxp_set_hash_node hnode;
+ uint64_t hash;
+
+ if ((set->type != LYXP_SET_NODE_SET) || (set->used < 2)) {
+ return 0;
+ }
+
+ /* find first top-level node to be used as anchor for positions */
+ for (root = set->tree; root->parent; root = lyd_parent(root)) {}
+ for ( ; root->prev->next; root = root->prev) {}
+
+ /* fill positions */
+ if (set_assign_pos(set, root, set->root_type)) {
+ return -1;
+ }
+
+#ifndef NDEBUG
+ LOGDBG(LY_LDGXPATH, "SORT BEGIN");
+ print_set_debug(set);
+#endif
+
+ for (i = 0; i < set->used; ++i) {
+ inverted = 0;
+ change = 0;
+
+ for (j = 1; j < set->used - i; ++j) {
+ /* compare node positions */
+ if (inverted) {
+ cmp = set_sort_compare(&set->val.nodes[j], &set->val.nodes[j - 1]);
+ } else {
+ cmp = set_sort_compare(&set->val.nodes[j - 1], &set->val.nodes[j]);
+ }
+
+ /* swap if needed */
+ if ((inverted && (cmp < 0)) || (!inverted && (cmp > 0))) {
+ change = 1;
+
+ item = set->val.nodes[j - 1];
+ set->val.nodes[j - 1] = set->val.nodes[j];
+ set->val.nodes[j] = item;
+ } else {
+ /* whether node_pos1 should be smaller than node_pos2 or the other way around */
+ inverted = !inverted;
+ }
+ }
+
+ ++ret;
+
+ if (!change) {
+ break;
+ }
+ }
+
+#ifndef NDEBUG
+ LOGDBG(LY_LDGXPATH, "SORT END %d", ret);
+ print_set_debug(set);
+#endif
+
+ /* check node hashes */
+ if (set->used >= LYD_HT_MIN_ITEMS) {
+ assert(set->ht);
+ for (i = 0; i < set->used; ++i) {
+ hnode.node = set->val.nodes[i].node;
+ hnode.type = set->val.nodes[i].type;
+
+ hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = dict_hash_multi(hash, NULL, 0);
+
+ assert(!lyht_find(set->ht, &hnode, hash, NULL));
+ }
+ }
+
+ return ret - 1;
+}
+
+/**
+ * @brief Merge 2 sorted sets into one.
+ *
+ * @param[in,out] trg Set to merge into. Duplicates are removed.
+ * @param[in] src Set to be merged into @p trg. It is cast to #LYXP_SET_EMPTY on success.
+ * @return LY_ERR
+ */
+static LY_ERR
+set_sorted_merge(struct lyxp_set *trg, struct lyxp_set *src)
+{
+ uint32_t i, j, k, count, dup_count;
+ int cmp;
+ const struct lyd_node *root;
+
+ if ((trg->type != LYXP_SET_NODE_SET) || (src->type != LYXP_SET_NODE_SET)) {
+ return LY_EINVAL;
+ }
+
+ if (!src->used) {
+ return LY_SUCCESS;
+ } else if (!trg->used) {
+ set_fill_set(trg, src);
+ lyxp_set_free_content(src);
+ return LY_SUCCESS;
+ }
+
+ /* find first top-level node to be used as anchor for positions */
+ for (root = trg->tree; root->parent; root = lyd_parent(root)) {}
+ for ( ; root->prev->next; root = root->prev) {}
+
+ /* fill positions */
+ if (set_assign_pos(trg, root, trg->root_type) || set_assign_pos(src, root, src->root_type)) {
+ return LY_EINT;
+ }
+
+#ifndef NDEBUG
+ LOGDBG(LY_LDGXPATH, "MERGE target");
+ print_set_debug(trg);
+ LOGDBG(LY_LDGXPATH, "MERGE source");
+ print_set_debug(src);
+#endif
+
+ /* make memory for the merge (duplicates are not detected yet, so space
+ * will likely be wasted on them, too bad) */
+ if (trg->size - trg->used < src->used) {
+ trg->size = trg->used + src->used;
+
+ trg->val.nodes = ly_realloc(trg->val.nodes, trg->size * sizeof *trg->val.nodes);
+ LY_CHECK_ERR_RET(!trg->val.nodes, LOGMEM(src->ctx), LY_EMEM);
+ }
+
+ i = 0;
+ j = 0;
+ count = 0;
+ dup_count = 0;
+ do {
+ cmp = set_sort_compare(&src->val.nodes[i], &trg->val.nodes[j]);
+ if (!cmp) {
+ if (!count) {
+ /* duplicate, just skip it */
+ ++i;
+ ++j;
+ } else {
+ /* we are copying something already, so let's copy the duplicate too,
+ * we are hoping that afterwards there are some more nodes to
+ * copy and this way we can copy them all together */
+ ++count;
+ ++dup_count;
+ ++i;
+ ++j;
+ }
+ } else if (cmp < 0) {
+ /* inserting src node into trg, just remember it for now */
+ ++count;
+ ++i;
+
+ /* insert the hash now */
+ set_insert_node_hash(trg, src->val.nodes[i - 1].node, src->val.nodes[i - 1].type);
+ } else if (count) {
+copy_nodes:
+ /* time to actually copy the nodes, we have found the largest block of nodes */
+ memmove(&trg->val.nodes[j + (count - dup_count)],
+ &trg->val.nodes[j],
+ (trg->used - j) * sizeof *trg->val.nodes);
+ memcpy(&trg->val.nodes[j - dup_count], &src->val.nodes[i - count], count * sizeof *src->val.nodes);
+
+ trg->used += count - dup_count;
+ /* do not change i, except the copying above, we are basically doing exactly what is in the else branch below */
+ j += count - dup_count;
+
+ count = 0;
+ dup_count = 0;
+ } else {
+ ++j;
+ }
+ } while ((i < src->used) && (j < trg->used));
+
+ if ((i < src->used) || count) {
+ /* insert all the hashes first */
+ for (k = i; k < src->used; ++k) {
+ set_insert_node_hash(trg, src->val.nodes[k].node, src->val.nodes[k].type);
+ }
+
+ /* loop ended, but we need to copy something at trg end */
+ count += src->used - i;
+ i = src->used;
+ goto copy_nodes;
+ }
+
+ /* we are inserting hashes before the actual node insert, which causes
+ * situations when there were initially not enough items for a hash table,
+ * but even after some were inserted, hash table was not created (during
+ * insertion the number of items is not updated yet) */
+ if (!trg->ht && (trg->used >= LYD_HT_MIN_ITEMS)) {
+ set_insert_node_hash(trg, NULL, 0);
+ }
+
+#ifndef NDEBUG
+ LOGDBG(LY_LDGXPATH, "MERGE result");
+ print_set_debug(trg);
+#endif
+
+ lyxp_set_free_content(src);
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxp_check_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_token want_tok)
+{
+ if (exp->used == tok_idx) {
+ if (ctx) {
+ LOGVAL(ctx, LY_VCODE_XP_EOF);
+ }
+ return LY_EINCOMPLETE;
+ }
+
+ if (want_tok && (exp->tokens[tok_idx] != want_tok)) {
+ if (ctx) {
+ LOGVAL(ctx, LY_VCODE_XP_INTOK2, lyxp_token2str(exp->tokens[tok_idx]),
+ &exp->expr[exp->tok_pos[tok_idx]], lyxp_token2str(want_tok));
+ }
+ return LY_ENOT;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxp_next_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_token want_tok)
+{
+ LY_CHECK_RET(lyxp_check_token(ctx, exp, *tok_idx, want_tok));
+
+ /* skip the token */
+ ++(*tok_idx);
+
+ return LY_SUCCESS;
+}
+
+/* just like lyxp_check_token() but tests for 2 tokens */
+static LY_ERR
+exp_check_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_token want_tok1,
+ enum lyxp_token want_tok2)
+{
+ if (exp->used == tok_idx) {
+ if (ctx) {
+ LOGVAL(ctx, LY_VCODE_XP_EOF);
+ }
+ return LY_EINCOMPLETE;
+ }
+
+ if ((exp->tokens[tok_idx] != want_tok1) && (exp->tokens[tok_idx] != want_tok2)) {
+ if (ctx) {
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[tok_idx]),
+ &exp->expr[exp->tok_pos[tok_idx]]);
+ }
+ return LY_ENOT;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_token want_tok1,
+ enum lyxp_token want_tok2)
+{
+ LY_CHECK_RET(exp_check_token2(ctx, exp, *tok_idx, want_tok1, want_tok2));
+
+ /* skip the token */
+ ++(*tok_idx);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Stack operation push on the repeat array.
+ *
+ * @param[in] exp Expression to use.
+ * @param[in] tok_idx Position in the expresion \p exp.
+ * @param[in] repeat_op_idx Index from \p exp of the operator token. This value is pushed.
+ */
+static void
+exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, uint32_t repeat_op_idx)
+{
+ uint32_t i;
+
+ if (exp->repeat[tok_idx]) {
+ for (i = 0; exp->repeat[tok_idx][i]; ++i) {}
+ exp->repeat[tok_idx] = realloc(exp->repeat[tok_idx], (i + 2) * sizeof *exp->repeat[tok_idx]);
+ LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
+ exp->repeat[tok_idx][i] = repeat_op_idx;
+ exp->repeat[tok_idx][i + 1] = 0;
+ } else {
+ exp->repeat[tok_idx] = calloc(2, sizeof *exp->repeat[tok_idx]);
+ LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
+ exp->repeat[tok_idx][0] = repeat_op_idx;
+ }
+}
+
+/**
+ * @brief Reparse Predicate. Logs directly on error.
+ *
+ * [7] Predicate ::= '[' Expr ']'
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_predicate(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ LY_ERR rc;
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK1);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+ rc = reparse_or_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK2);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse RelativeLocationPath. Logs directly on error.
+ *
+ * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+ * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression \p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR (LY_EINCOMPLETE on forward reference)
+ */
+static LY_ERR
+reparse_relative_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ LY_ERR rc;
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
+ LY_CHECK_RET(rc);
+
+ goto step;
+ do {
+ /* '/' or '//' */
+ ++(*tok_idx);
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
+ LY_CHECK_RET(rc);
+step:
+ /* Step */
+ switch (exp->tokens[*tok_idx]) {
+ case LYXP_TOKEN_DOT:
+ ++(*tok_idx);
+ break;
+
+ case LYXP_TOKEN_DDOT:
+ ++(*tok_idx);
+ break;
+
+ case LYXP_TOKEN_AXISNAME:
+ ++(*tok_idx);
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_DCOLON);
+ LY_CHECK_RET(rc);
+
+ /* fall through */
+ case LYXP_TOKEN_AT:
+ ++(*tok_idx);
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
+ LY_CHECK_RET(rc);
+ if ((exp->tokens[*tok_idx] != LYXP_TOKEN_NAMETEST) && (exp->tokens[*tok_idx] != LYXP_TOKEN_NODETYPE)) {
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EVALID;
+ }
+ if (exp->tokens[*tok_idx] == LYXP_TOKEN_NODETYPE) {
+ goto reparse_nodetype;
+ }
+ /* fall through */
+ case LYXP_TOKEN_NAMETEST:
+ ++(*tok_idx);
+ goto reparse_predicate;
+
+ case LYXP_TOKEN_NODETYPE:
+reparse_nodetype:
+ ++(*tok_idx);
+
+ /* '(' */
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+ /* ')' */
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+reparse_predicate:
+ /* Predicate* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+ rc = reparse_predicate(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+ break;
+ default:
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EVALID;
+ }
+ } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse AbsoluteLocationPath. Logs directly on error.
+ *
+ * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression \p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_absolute_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ LY_ERR rc;
+
+ LY_CHECK_RET(exp_check_token2(ctx, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
+
+ /* '/' RelativeLocationPath? */
+ if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
+ /* '/' */
+ ++(*tok_idx);
+
+ if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
+ return LY_SUCCESS;
+ }
+ switch (exp->tokens[*tok_idx]) {
+ case LYXP_TOKEN_DOT:
+ case LYXP_TOKEN_DDOT:
+ case LYXP_TOKEN_AXISNAME:
+ case LYXP_TOKEN_AT:
+ case LYXP_TOKEN_NAMETEST:
+ case LYXP_TOKEN_NODETYPE:
+ rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ /* fall through */
+ default:
+ break;
+ }
+
+ } else {
+ /* '//' RelativeLocationPath */
+ ++(*tok_idx);
+
+ rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse FunctionCall. Logs directly on error.
+ *
+ * [9] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_function_call(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ int8_t min_arg_count = -1;
+ uint32_t arg_count, max_arg_count = 0, func_tok_idx;
+ LY_ERR rc;
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME);
+ LY_CHECK_RET(rc);
+ func_tok_idx = *tok_idx;
+ switch (exp->tok_len[*tok_idx]) {
+ case 3:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ }
+ break;
+ case 4:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
+ min_arg_count = 0;
+ max_arg_count = 0;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
+ min_arg_count = 0;
+ max_arg_count = 0;
+ }
+ break;
+ case 5:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
+ min_arg_count = 0;
+ max_arg_count = 0;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ }
+ break;
+ case 6:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
+ min_arg_count = 2;
+ max_arg_count = UINT32_MAX;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ }
+ break;
+ case 7:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
+ min_arg_count = 0;
+ max_arg_count = 0;
+ }
+ break;
+ case 8:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
+ min_arg_count = 0;
+ max_arg_count = 0;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ case 9:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
+ min_arg_count = 2;
+ max_arg_count = 3;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
+ min_arg_count = 3;
+ max_arg_count = 3;
+ }
+ break;
+ case 10:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
+ min_arg_count = 1;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ case 11:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ case 12:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ case 13:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ }
+ break;
+ case 15:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
+ min_arg_count = 0;
+ max_arg_count = 1;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ case 16:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ case 20:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
+ min_arg_count = 2;
+ max_arg_count = 2;
+ }
+ break;
+ }
+ if (min_arg_count == -1) {
+ LOGVAL(ctx, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EINVAL;
+ }
+ ++(*tok_idx);
+
+ /* '(' */
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+ /* ( Expr ( ',' Expr )* )? */
+ arg_count = 0;
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
+ LY_CHECK_RET(rc);
+ if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
+ ++arg_count;
+ rc = reparse_or_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
+ ++(*tok_idx);
+
+ ++arg_count;
+ rc = reparse_or_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+
+ /* ')' */
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+ if ((arg_count < (uint32_t)min_arg_count) || (arg_count > max_arg_count)) {
+ LOGVAL(ctx, LY_VCODE_XP_INARGCOUNT, arg_count, exp->tok_len[func_tok_idx], &exp->expr[exp->tok_pos[func_tok_idx]]);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse PathExpr. Logs directly on error.
+ *
+ * [10] PathExpr ::= LocationPath | PrimaryExpr Predicate*
+ * | PrimaryExpr Predicate* '/' RelativeLocationPath
+ * | PrimaryExpr Predicate* '//' RelativeLocationPath
+ * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+ * [8] PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_path_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ LY_ERR rc;
+
+ if (lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE)) {
+ return LY_EVALID;
+ }
+
+ switch (exp->tokens[*tok_idx]) {
+ case LYXP_TOKEN_PAR1:
+ /* '(' Expr ')' Predicate* */
+ ++(*tok_idx);
+
+ rc = reparse_or_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+
+ rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+ goto predicate;
+ case LYXP_TOKEN_DOT:
+ case LYXP_TOKEN_DDOT:
+ case LYXP_TOKEN_AXISNAME:
+ case LYXP_TOKEN_AT:
+ case LYXP_TOKEN_NAMETEST:
+ case LYXP_TOKEN_NODETYPE:
+ /* RelativeLocationPath */
+ rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ break;
+ case LYXP_TOKEN_VARREF:
+ /* VariableReference */
+ ++(*tok_idx);
+ goto predicate;
+ case LYXP_TOKEN_FUNCNAME:
+ /* FunctionCall */
+ rc = reparse_function_call(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ goto predicate;
+ case LYXP_TOKEN_OPER_PATH:
+ case LYXP_TOKEN_OPER_RPATH:
+ /* AbsoluteLocationPath */
+ rc = reparse_absolute_location_path(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ break;
+ case LYXP_TOKEN_LITERAL:
+ /* Literal */
+ ++(*tok_idx);
+ goto predicate;
+ case LYXP_TOKEN_NUMBER:
+ /* Number */
+ ++(*tok_idx);
+ goto predicate;
+ default:
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+
+predicate:
+ /* Predicate* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+ rc = reparse_predicate(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+
+ /* ('/' or '//') RelativeLocationPath */
+ if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
+
+ /* '/' or '//' */
+ ++(*tok_idx);
+
+ rc = reparse_relative_location_path(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse UnaryExpr. Logs directly on error.
+ *
+ * [17] UnaryExpr ::= UnionExpr | '-' UnaryExpr
+ * [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_unary_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ uint32_t prev_exp;
+ LY_ERR rc;
+
+ /* ('-')* */
+ prev_exp = *tok_idx;
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) &&
+ (exp->expr[exp->tok_pos[*tok_idx]] == '-')) {
+ exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNARY);
+ ++(*tok_idx);
+ }
+
+ /* PathExpr */
+ prev_exp = *tok_idx;
+ rc = reparse_path_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+
+ /* ('|' PathExpr)* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_UNI)) {
+ exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNION);
+ ++(*tok_idx);
+
+ rc = reparse_path_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse AdditiveExpr. Logs directly on error.
+ *
+ * [15] AdditiveExpr ::= MultiplicativeExpr
+ * | AdditiveExpr '+' MultiplicativeExpr
+ * | AdditiveExpr '-' MultiplicativeExpr
+ * [16] MultiplicativeExpr ::= UnaryExpr
+ * | MultiplicativeExpr '*' UnaryExpr
+ * | MultiplicativeExpr 'div' UnaryExpr
+ * | MultiplicativeExpr 'mod' UnaryExpr
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_additive_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ uint32_t prev_add_exp, prev_mul_exp;
+ LY_ERR rc;
+
+ prev_add_exp = *tok_idx;
+ goto reparse_multiplicative_expr;
+
+ /* ('+' / '-' MultiplicativeExpr)* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) &&
+ ((exp->expr[exp->tok_pos[*tok_idx]] == '+') || (exp->expr[exp->tok_pos[*tok_idx]] == '-'))) {
+ exp_repeat_push(exp, prev_add_exp, LYXP_EXPR_ADDITIVE);
+ ++(*tok_idx);
+
+reparse_multiplicative_expr:
+ /* UnaryExpr */
+ prev_mul_exp = *tok_idx;
+ rc = reparse_unary_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+
+ /* ('*' / 'div' / 'mod' UnaryExpr)* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) &&
+ ((exp->expr[exp->tok_pos[*tok_idx]] == '*') || (exp->tok_len[*tok_idx] == 3))) {
+ exp_repeat_push(exp, prev_mul_exp, LYXP_EXPR_MULTIPLICATIVE);
+ ++(*tok_idx);
+
+ rc = reparse_unary_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse EqualityExpr. Logs directly on error.
+ *
+ * [13] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
+ * | EqualityExpr '!=' RelationalExpr
+ * [14] RelationalExpr ::= AdditiveExpr
+ * | RelationalExpr '<' AdditiveExpr
+ * | RelationalExpr '>' AdditiveExpr
+ * | RelationalExpr '<=' AdditiveExpr
+ * | RelationalExpr '>=' AdditiveExpr
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_equality_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ uint32_t prev_eq_exp, prev_rel_exp;
+ LY_ERR rc;
+
+ prev_eq_exp = *tok_idx;
+ goto reparse_additive_expr;
+
+ /* ('=' / '!=' RelationalExpr)* */
+ while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_EQUAL, LYXP_TOKEN_OPER_NEQUAL)) {
+ exp_repeat_push(exp, prev_eq_exp, LYXP_EXPR_EQUALITY);
+ ++(*tok_idx);
+
+reparse_additive_expr:
+ /* AdditiveExpr */
+ prev_rel_exp = *tok_idx;
+ rc = reparse_additive_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+
+ /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_COMP)) {
+ exp_repeat_push(exp, prev_rel_exp, LYXP_EXPR_RELATIONAL);
+ ++(*tok_idx);
+
+ rc = reparse_additive_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Reparse OrExpr. Logs directly on error.
+ *
+ * [11] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+ * [12] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] depth Current number of nested expressions.
+ * @return LY_ERR
+ */
+static LY_ERR
+reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth)
+{
+ uint32_t prev_or_exp, prev_and_exp;
+ LY_ERR rc;
+
+ ++depth;
+ LY_CHECK_ERR_RET(depth > LYXP_MAX_BLOCK_DEPTH, LOGVAL(ctx, LY_VCODE_XP_DEPTH), LY_EINVAL);
+
+ prev_or_exp = *tok_idx;
+ goto reparse_equality_expr;
+
+ /* ('or' AndExpr)* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*tok_idx] == 2)) {
+ exp_repeat_push(exp, prev_or_exp, LYXP_EXPR_OR);
+ ++(*tok_idx);
+
+reparse_equality_expr:
+ /* EqualityExpr */
+ prev_and_exp = *tok_idx;
+ rc = reparse_equality_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+
+ /* ('and' EqualityExpr)* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*tok_idx] == 3)) {
+ exp_repeat_push(exp, prev_and_exp, LYXP_EXPR_AND);
+ ++(*tok_idx);
+
+ rc = reparse_equality_expr(ctx, exp, tok_idx, depth);
+ LY_CHECK_RET(rc);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse NCName.
+ *
+ * @param[in] ncname Name to parse.
+ * @return Length of @p ncname valid bytes.
+ */
+static ssize_t
+parse_ncname(const char *ncname)
+{
+ uint32_t uc;
+ size_t size;
+ ssize_t len = 0;
+
+ LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
+ if (!is_xmlqnamestartchar(uc) || (uc == ':')) {
+ return len;
+ }
+
+ do {
+ len += size;
+ if (!*ncname) {
+ break;
+ }
+ LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), -len);
+ } while (is_xmlqnamechar(uc) && (uc != ':'));
+
+ return len;
+}
+
+/**
+ * @brief Add @p token into the expression @p exp.
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] exp Expression to use.
+ * @param[in] token Token to add.
+ * @param[in] tok_pos Token position in the XPath expression.
+ * @param[in] tok_len Token length in the XPath expression.
+ * @return LY_ERR
+ */
+static LY_ERR
+exp_add_token(const struct ly_ctx *ctx, struct lyxp_expr *exp, enum lyxp_token token, uint32_t tok_pos, uint32_t tok_len)
+{
+ uint32_t prev;
+
+ if (exp->used == exp->size) {
+ prev = exp->size;
+ exp->size += LYXP_EXPR_SIZE_STEP;
+ if (prev > exp->size) {
+ LOGINT(ctx);
+ return LY_EINT;
+ }
+
+ exp->tokens = ly_realloc(exp->tokens, exp->size * sizeof *exp->tokens);
+ LY_CHECK_ERR_RET(!exp->tokens, LOGMEM(ctx), LY_EMEM);
+ exp->tok_pos = ly_realloc(exp->tok_pos, exp->size * sizeof *exp->tok_pos);
+ LY_CHECK_ERR_RET(!exp->tok_pos, LOGMEM(ctx), LY_EMEM);
+ exp->tok_len = ly_realloc(exp->tok_len, exp->size * sizeof *exp->tok_len);
+ LY_CHECK_ERR_RET(!exp->tok_len, LOGMEM(ctx), LY_EMEM);
+ }
+
+ exp->tokens[exp->used] = token;
+ exp->tok_pos[exp->used] = tok_pos;
+ exp->tok_len[exp->used] = tok_len;
+ ++exp->used;
+ return LY_SUCCESS;
+}
+
+void
+lyxp_expr_free(const struct ly_ctx *ctx, struct lyxp_expr *expr)
+{
+ uint32_t i;
+
+ if (!expr) {
+ return;
+ }
+
+ lydict_remove(ctx, expr->expr);
+ free(expr->tokens);
+ free(expr->tok_pos);
+ free(expr->tok_len);
+ if (expr->repeat) {
+ for (i = 0; i < expr->used; ++i) {
+ free(expr->repeat[i]);
+ }
+ }
+ free(expr->repeat);
+ free(expr);
+}
+
+/**
+ * @brief Parse Axis name.
+ *
+ * @param[in] str String to parse.
+ * @param[in] str_len Length of @p str.
+ * @return LY_SUCCESS if an axis.
+ * @return LY_ENOT otherwise.
+ */
+static LY_ERR
+expr_parse_axis(const char *str, size_t str_len)
+{
+ switch (str_len) {
+ case 4:
+ if (!strncmp("self", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 5:
+ if (!strncmp("child", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 6:
+ if (!strncmp("parent", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 8:
+ if (!strncmp("ancestor", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 9:
+ if (!strncmp("attribute", str, str_len)) {
+ return LY_SUCCESS;
+ } else if (!strncmp("following", str, str_len)) {
+ return LY_SUCCESS;
+ } else if (!strncmp("namespace", str, str_len)) {
+ LOGERR(NULL, LY_EINVAL, "Axis \"namespace\" not supported.");
+ return LY_ENOT;
+ } else if (!strncmp("preceding", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 10:
+ if (!strncmp("descendant", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 16:
+ if (!strncmp("ancestor-or-self", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 17:
+ if (!strncmp("following-sibling", str, str_len)) {
+ return LY_SUCCESS;
+ } else if (!strncmp("preceding-sibling", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ case 18:
+ if (!strncmp("descendant-or-self", str, str_len)) {
+ return LY_SUCCESS;
+ }
+ break;
+ }
+
+ return LY_ENOT;
+}
+
+LY_ERR
+lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len, ly_bool reparse, struct lyxp_expr **expr_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *expr;
+ size_t parsed = 0, tok_len;
+ enum lyxp_token tok_type;
+ ly_bool prev_func_check = 0, prev_ntype_check = 0, has_axis;
+ uint32_t tok_idx = 0;
+ ssize_t ncname_len;
+
+ assert(expr_p);
+
+ if (!expr_str[0]) {
+ LOGVAL(ctx, LY_VCODE_XP_EOF);
+ return LY_EVALID;
+ }
+
+ if (!expr_len) {
+ expr_len = strlen(expr_str);
+ }
+ if (expr_len > UINT32_MAX) {
+ LOGVAL(ctx, LYVE_XPATH, "XPath expression cannot be longer than %" PRIu32 " characters.", UINT32_MAX);
+ return LY_EVALID;
+ }
+
+ /* init lyxp_expr structure */
+ expr = calloc(1, sizeof *expr);
+ LY_CHECK_ERR_GOTO(!expr, LOGMEM(ctx); ret = LY_EMEM, error);
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, expr_str, expr_len, &expr->expr), error);
+ expr->used = 0;
+ expr->size = LYXP_EXPR_SIZE_START;
+ expr->tokens = malloc(expr->size * sizeof *expr->tokens);
+ LY_CHECK_ERR_GOTO(!expr->tokens, LOGMEM(ctx); ret = LY_EMEM, error);
+
+ expr->tok_pos = malloc(expr->size * sizeof *expr->tok_pos);
+ LY_CHECK_ERR_GOTO(!expr->tok_pos, LOGMEM(ctx); ret = LY_EMEM, error);
+
+ expr->tok_len = malloc(expr->size * sizeof *expr->tok_len);
+ LY_CHECK_ERR_GOTO(!expr->tok_len, LOGMEM(ctx); ret = LY_EMEM, error);
+
+ /* make expr 0-terminated */
+ expr_str = expr->expr;
+
+ while (is_xmlws(expr_str[parsed])) {
+ ++parsed;
+ }
+
+ do {
+ if (expr_str[parsed] == '(') {
+
+ /* '(' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_PAR1;
+
+ if (prev_ntype_check && expr->used && (expr->tokens[expr->used - 1] == LYXP_TOKEN_NAMETEST) &&
+ (((expr->tok_len[expr->used - 1] == 4) &&
+ (!strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "node", 4) ||
+ !strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "text", 4))) ||
+ ((expr->tok_len[expr->used - 1] == 7) &&
+ !strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "comment", 7)))) {
+ /* it is NodeType after all */
+ expr->tokens[expr->used - 1] = LYXP_TOKEN_NODETYPE;
+
+ prev_ntype_check = 0;
+ prev_func_check = 0;
+ } else if (prev_func_check && expr->used && (expr->tokens[expr->used - 1] == LYXP_TOKEN_NAMETEST)) {
+ /* it is FunctionName after all */
+ expr->tokens[expr->used - 1] = LYXP_TOKEN_FUNCNAME;
+
+ prev_ntype_check = 0;
+ prev_func_check = 0;
+ }
+
+ } else if (expr_str[parsed] == ')') {
+
+ /* ')' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_PAR2;
+
+ } else if (expr_str[parsed] == '[') {
+
+ /* '[' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_BRACK1;
+
+ } else if (expr_str[parsed] == ']') {
+
+ /* ']' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_BRACK2;
+
+ } else if (!strncmp(&expr_str[parsed], "..", 2)) {
+
+ /* '..' */
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_DDOT;
+
+ } else if ((expr_str[parsed] == '.') && (!isdigit(expr_str[parsed + 1]))) {
+
+ /* '.' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_DOT;
+
+ } else if (expr_str[parsed] == '@') {
+
+ /* '@' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_AT;
+
+ } else if (expr_str[parsed] == ',') {
+
+ /* ',' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_COMMA;
+
+ } else if (expr_str[parsed] == '\'') {
+
+ /* Literal with ' */
+ for (tok_len = 1; (expr_str[parsed + tok_len] != '\0') && (expr_str[parsed + tok_len] != '\''); ++tok_len) {}
+ LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == '\0',
+ LOGVAL(ctx, LY_VCODE_XP_EOE, expr_str[parsed], &expr_str[parsed]); ret = LY_EVALID,
+ error);
+ ++tok_len;
+ tok_type = LYXP_TOKEN_LITERAL;
+
+ } else if (expr_str[parsed] == '\"') {
+
+ /* Literal with " */
+ for (tok_len = 1; (expr_str[parsed + tok_len] != '\0') && (expr_str[parsed + tok_len] != '\"'); ++tok_len) {}
+ LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == '\0',
+ LOGVAL(ctx, LY_VCODE_XP_EOE, expr_str[parsed], &expr_str[parsed]); ret = LY_EVALID,
+ error);
+ ++tok_len;
+ tok_type = LYXP_TOKEN_LITERAL;
+
+ } else if ((expr_str[parsed] == '.') || (isdigit(expr_str[parsed]))) {
+
+ /* Number */
+ for (tok_len = 0; isdigit(expr_str[parsed + tok_len]); ++tok_len) {}
+ if (expr_str[parsed + tok_len] == '.') {
+ ++tok_len;
+ for ( ; isdigit(expr_str[parsed + tok_len]); ++tok_len) {}
+ }
+ tok_type = LYXP_TOKEN_NUMBER;
+
+ } else if (expr_str[parsed] == '$') {
+
+ /* VariableReference */
+ parsed++;
+ ncname_len = parse_ncname(&expr_str[parsed]);
+ LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
+ parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ tok_len = ncname_len;
+ LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == ':',
+ LOGVAL(ctx, LYVE_XPATH, "Variable with prefix is not supported."); ret = LY_EVALID,
+ error);
+ tok_type = LYXP_TOKEN_VARREF;
+
+ } else if (expr_str[parsed] == '/') {
+
+ /* Operator '/', '//' */
+ if (!strncmp(&expr_str[parsed], "//", 2)) {
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_OPER_RPATH;
+ } else {
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPER_PATH;
+ }
+
+ } else if (!strncmp(&expr_str[parsed], "!=", 2)) {
+
+ /* Operator '!=' */
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_OPER_NEQUAL;
+
+ } else if (!strncmp(&expr_str[parsed], "<=", 2) || !strncmp(&expr_str[parsed], ">=", 2)) {
+
+ /* Operator '<=', '>=' */
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_OPER_COMP;
+
+ } else if (expr_str[parsed] == '|') {
+
+ /* Operator '|' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPER_UNI;
+
+ } else if ((expr_str[parsed] == '+') || (expr_str[parsed] == '-')) {
+
+ /* Operator '+', '-' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPER_MATH;
+
+ } else if (expr_str[parsed] == '=') {
+
+ /* Operator '=' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPER_EQUAL;
+
+ } else if ((expr_str[parsed] == '<') || (expr_str[parsed] == '>')) {
+
+ /* Operator '<', '>' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPER_COMP;
+
+ } else if (expr->used && (expr->tokens[expr->used - 1] != LYXP_TOKEN_AT) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_PAR1) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_BRACK1) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_COMMA) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_LOG) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_EQUAL) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_NEQUAL) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_COMP) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_MATH) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_UNI) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_PATH) &&
+ (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_RPATH)) {
+
+ /* Operator '*', 'or', 'and', 'mod', or 'div' */
+ if (expr_str[parsed] == '*') {
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPER_MATH;
+
+ } else if (!strncmp(&expr_str[parsed], "or", 2)) {
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_OPER_LOG;
+
+ } else if (!strncmp(&expr_str[parsed], "and", 3)) {
+ tok_len = 3;
+ tok_type = LYXP_TOKEN_OPER_LOG;
+
+ } else if (!strncmp(&expr_str[parsed], "mod", 3) || !strncmp(&expr_str[parsed], "div", 3)) {
+ tok_len = 3;
+ tok_type = LYXP_TOKEN_OPER_MATH;
+
+ } else if (prev_ntype_check || prev_func_check) {
+ LOGVAL(ctx, LYVE_XPATH, "Invalid character 0x%x ('%c'), perhaps \"%.*s\" is supposed to be a function call.",
+ expr_str[parsed], expr_str[parsed], expr->tok_len[expr->used - 1], &expr->expr[expr->tok_pos[expr->used - 1]]);
+ ret = LY_EVALID;
+ goto error;
+ } else {
+ LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str);
+ ret = LY_EVALID;
+ goto error;
+ }
+ } else {
+
+ /* (AxisName '::')? ((NCName ':')? '*' | QName) or NodeType/FunctionName */
+ if (expr_str[parsed] == '*') {
+ ncname_len = 1;
+ } else {
+ ncname_len = parse_ncname(&expr_str[parsed]);
+ LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
+ parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ }
+ tok_len = ncname_len;
+
+ has_axis = 0;
+ if (!strncmp(&expr_str[parsed + tok_len], "::", 2)) {
+ /* axis */
+ LY_CHECK_ERR_GOTO(expr_parse_axis(&expr_str[parsed], ncname_len),
+ LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str); ret = LY_EVALID, error);
+ tok_type = LYXP_TOKEN_AXISNAME;
+
+ LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
+ parsed += tok_len;
+
+ /* '::' */
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_DCOLON;
+
+ LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
+ parsed += tok_len;
+
+ if (expr_str[parsed] == '*') {
+ ncname_len = 1;
+ } else {
+ ncname_len = parse_ncname(&expr_str[parsed]);
+ LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
+ parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ }
+ tok_len = ncname_len;
+
+ has_axis = 1;
+ }
+
+ if (expr_str[parsed + tok_len] == ':') {
+ ++tok_len;
+ if (expr_str[parsed + tok_len] == '*') {
+ ++tok_len;
+ } else {
+ ncname_len = parse_ncname(&expr_str[parsed + tok_len]);
+ LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
+ parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ tok_len += ncname_len;
+ }
+ /* remove old flags to prevent ambiguities */
+ prev_ntype_check = 0;
+ prev_func_check = 0;
+ tok_type = LYXP_TOKEN_NAMETEST;
+ } else {
+ /* if not '*', there is no prefix so it can still be NodeType/FunctionName, we can't finally decide now */
+ prev_ntype_check = (expr_str[parsed] == '*') ? 0 : 1;
+ prev_func_check = (prev_ntype_check && !has_axis) ? 1 : 0;
+ tok_type = LYXP_TOKEN_NAMETEST;
+ }
+ }
+
+ /* store the token, move on to the next one */
+ LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
+ parsed += tok_len;
+ while (is_xmlws(expr_str[parsed])) {
+ ++parsed;
+ }
+
+ } while (expr_str[parsed]);
+
+ if (reparse) {
+ /* prealloc repeat */
+ expr->repeat = calloc(expr->size, sizeof *expr->repeat);
+ LY_CHECK_ERR_GOTO(!expr->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
+
+ /* fill repeat */
+ LY_CHECK_ERR_GOTO(reparse_or_expr(ctx, expr, &tok_idx, 0), ret = LY_EVALID, error);
+ if (expr->used > tok_idx) {
+ LOGVAL(ctx, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of an XPath expression.",
+ &expr->expr[expr->tok_pos[tok_idx]]);
+ ret = LY_EVALID;
+ goto error;
+ }
+ }
+
+ print_expr_struct_debug(expr);
+ *expr_p = expr;
+ return LY_SUCCESS;
+
+error:
+ lyxp_expr_free(ctx, expr);
+ return ret;
+}
+
+LY_ERR
+lyxp_expr_dup(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t start_idx, uint32_t end_idx,
+ struct lyxp_expr **dup_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *dup = NULL;
+ uint32_t used = 0, i, j, expr_len;
+ const char *expr_start;
+
+ assert((!start_idx && !end_idx) || ((start_idx < exp->used) && (end_idx < exp->used) && (start_idx <= end_idx)));
+
+ if (!exp) {
+ goto cleanup;
+ }
+
+ if (!start_idx && !end_idx) {
+ end_idx = exp->used - 1;
+ }
+
+ expr_start = exp->expr + exp->tok_pos[start_idx];
+ expr_len = (exp->tok_pos[end_idx] + exp->tok_len[end_idx]) - exp->tok_pos[start_idx];
+
+ dup = calloc(1, sizeof *dup);
+ LY_CHECK_ERR_GOTO(!dup, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+
+ if (exp->used) {
+ used = (end_idx - start_idx) + 1;
+
+ dup->tokens = malloc(used * sizeof *dup->tokens);
+ LY_CHECK_ERR_GOTO(!dup->tokens, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup->tokens, exp->tokens + start_idx, used * sizeof *dup->tokens);
+
+ dup->tok_pos = malloc(used * sizeof *dup->tok_pos);
+ LY_CHECK_ERR_GOTO(!dup->tok_pos, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup->tok_pos, exp->tok_pos + start_idx, used * sizeof *dup->tok_pos);
+
+ if (start_idx) {
+ /* fix the indices in the expression */
+ for (i = 0; i < used; ++i) {
+ dup->tok_pos[i] -= expr_start - exp->expr;
+ }
+ }
+
+ dup->tok_len = malloc(used * sizeof *dup->tok_len);
+ LY_CHECK_ERR_GOTO(!dup->tok_len, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup->tok_len, exp->tok_len + start_idx, used * sizeof *dup->tok_len);
+
+ if (exp->repeat) {
+ dup->repeat = malloc(used * sizeof *dup->repeat);
+ LY_CHECK_ERR_GOTO(!dup->repeat, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ for (i = start_idx; i <= end_idx; ++i) {
+ if (!exp->repeat[i]) {
+ dup->repeat[i - start_idx] = NULL;
+ } else {
+ for (j = 0; exp->repeat[i][j]; ++j) {}
+ /* the ending 0 as well */
+ ++j;
+
+ dup->repeat[i - start_idx] = malloc(j * sizeof **dup->repeat);
+ LY_CHECK_ERR_GOTO(!dup->repeat[i - start_idx], LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup->repeat[i - start_idx], exp->repeat[i], j * sizeof **dup->repeat);
+ }
+ }
+ }
+ }
+
+ dup->used = used;
+ dup->size = used;
+
+ /* copy only subexpression */
+ LY_CHECK_GOTO(ret = lydict_insert(ctx, expr_start, expr_len, &dup->expr), cleanup);
+
+cleanup:
+ if (ret) {
+ lyxp_expr_free(ctx, dup);
+ } else {
+ *dup_p = dup;
+ }
+ return ret;
+}
+
+/**
+ * @brief Get the last-added schema node that is currently in the context.
+ *
+ * @param[in] set Set to search in.
+ * @return Last-added schema context node, NULL if no node is in context.
+ */
+static struct lysc_node *
+warn_get_scnode_in_ctx(struct lyxp_set *set)
+{
+ uint32_t i;
+
+ if (!set || (set->type != LYXP_SET_SCNODE_SET)) {
+ return NULL;
+ }
+
+ i = set->used;
+ do {
+ --i;
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ /* if there are more, simply return the first found (last added) */
+ return set->val.scnodes[i].scnode;
+ }
+ } while (i);
+
+ return NULL;
+}
+
+/**
+ * @brief Test whether a type is numeric - integer type or decimal64.
+ *
+ * @param[in] type Type to test.
+ * @return Boolean value whether @p type is numeric type or not.
+ */
+static ly_bool
+warn_is_numeric_type(struct lysc_type *type)
+{
+ struct lysc_type_union *uni;
+ ly_bool ret;
+ LY_ARRAY_COUNT_TYPE u;
+
+ switch (type->basetype) {
+ case LY_TYPE_DEC64:
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ return 1;
+ case LY_TYPE_UNION:
+ uni = (struct lysc_type_union *)type;
+ LY_ARRAY_FOR(uni->types, u) {
+ ret = warn_is_numeric_type(uni->types[u]);
+ if (ret) {
+ /* found a suitable type */
+ return ret;
+ }
+ }
+ /* did not find any suitable type */
+ return 0;
+ case LY_TYPE_LEAFREF:
+ return warn_is_numeric_type(((struct lysc_type_leafref *)type)->realtype);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * @brief Test whether a type is string-like - no integers, decimal64 or binary.
+ *
+ * @param[in] type Type to test.
+ * @return Boolean value whether @p type's basetype is string type or not.
+ */
+static ly_bool
+warn_is_string_type(struct lysc_type *type)
+{
+ struct lysc_type_union *uni;
+ ly_bool ret;
+ LY_ARRAY_COUNT_TYPE u;
+
+ switch (type->basetype) {
+ case LY_TYPE_BITS:
+ case LY_TYPE_ENUM:
+ case LY_TYPE_IDENT:
+ case LY_TYPE_INST:
+ case LY_TYPE_STRING:
+ return 1;
+ case LY_TYPE_UNION:
+ uni = (struct lysc_type_union *)type;
+ LY_ARRAY_FOR(uni->types, u) {
+ ret = warn_is_string_type(uni->types[u]);
+ if (ret) {
+ /* found a suitable type */
+ return ret;
+ }
+ }
+ /* did not find any suitable type */
+ return 0;
+ case LY_TYPE_LEAFREF:
+ return warn_is_string_type(((struct lysc_type_leafref *)type)->realtype);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * @brief Test whether a type is one specific type.
+ *
+ * @param[in] type Type to test.
+ * @param[in] base Expected type.
+ * @return Boolean value whether the given @p type is of the specific basetype @p base.
+ */
+static ly_bool
+warn_is_specific_type(struct lysc_type *type, LY_DATA_TYPE base)
+{
+ struct lysc_type_union *uni;
+ ly_bool ret;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (type->basetype == base) {
+ return 1;
+ } else if (type->basetype == LY_TYPE_UNION) {
+ uni = (struct lysc_type_union *)type;
+ LY_ARRAY_FOR(uni->types, u) {
+ ret = warn_is_specific_type(uni->types[u], base);
+ if (ret) {
+ /* found a suitable type */
+ return ret;
+ }
+ }
+ /* did not find any suitable type */
+ return 0;
+ } else if (type->basetype == LY_TYPE_LEAFREF) {
+ return warn_is_specific_type(((struct lysc_type_leafref *)type)->realtype, base);
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Get next type of a (union) type.
+ *
+ * @param[in] type Base type.
+ * @param[in] prev_type Previously returned type.
+ * @return Next type or NULL.
+ */
+static struct lysc_type *
+warn_is_equal_type_next_type(struct lysc_type *type, struct lysc_type *prev_type)
+{
+ struct lysc_type_union *uni;
+ ly_bool found = 0;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (type->basetype == LY_TYPE_UNION) {
+ uni = (struct lysc_type_union *)type;
+ if (!prev_type) {
+ return uni->types[0];
+ }
+ LY_ARRAY_FOR(uni->types, u) {
+ if (found) {
+ return uni->types[u];
+ }
+ if (prev_type == uni->types[u]) {
+ found = 1;
+ }
+ }
+ return NULL;
+ } else {
+ if (prev_type) {
+ assert(type == prev_type);
+ return NULL;
+ } else {
+ return type;
+ }
+ }
+}
+
+/**
+ * @brief Test whether 2 types have a common type.
+ *
+ * @param[in] type1 First type.
+ * @param[in] type2 Second type.
+ * @return 1 if they do, 0 otherwise.
+ */
+static int
+warn_is_equal_type(struct lysc_type *type1, struct lysc_type *type2)
+{
+ struct lysc_type *t1, *rt1, *t2, *rt2;
+
+ t1 = NULL;
+ while ((t1 = warn_is_equal_type_next_type(type1, t1))) {
+ if (t1->basetype == LY_TYPE_LEAFREF) {
+ rt1 = ((struct lysc_type_leafref *)t1)->realtype;
+ } else {
+ rt1 = t1;
+ }
+
+ t2 = NULL;
+ while ((t2 = warn_is_equal_type_next_type(type2, t2))) {
+ if (t2->basetype == LY_TYPE_LEAFREF) {
+ rt2 = ((struct lysc_type_leafref *)t2)->realtype;
+ } else {
+ rt2 = t2;
+ }
+
+ if (rt2->basetype == rt1->basetype) {
+ /* match found */
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Print warning with information about the XPath subexpression that caused previous warning.
+ *
+ * @param[in] ctx Context for logging.
+ * @param[in] tok_pos Index of the subexpression in the whole expression.
+ * @param[in] subexpr Subexpression start.
+ * @param[in] subexpr_len Length of @p subexpr to print.
+ * @param[in] cur_scnode Expression context node.
+ */
+static void
+warn_subexpr_log(const struct ly_ctx *ctx, uint32_t tok_pos, const char *subexpr, int subexpr_len,
+ const struct lysc_node *cur_scnode)
+{
+ char *path;
+
+ path = lysc_path(cur_scnode, LYSC_PATH_LOG, NULL, 0);
+ LOGWRN(ctx, "Previous warning generated by XPath subexpression[%" PRIu32 "] \"%.*s\" with context node \"%s\".",
+ tok_pos, subexpr_len, subexpr, path);
+ free(path);
+}
+
+/**
+ * @brief Check both operands of comparison operators.
+ *
+ * @param[in] ctx Context for errors.
+ * @param[in] set1 First operand set.
+ * @param[in] set2 Second operand set.
+ * @param[in] numbers_only Whether accept only numbers or other types are fine too (for '=' and '!=').
+ * @param[in] expr Start of the expression to print with the warning.
+ * @param[in] tok_pos Token position.
+ */
+static void
+warn_operands(struct ly_ctx *ctx, struct lyxp_set *set1, struct lyxp_set *set2, ly_bool numbers_only, const char *expr,
+ uint32_t tok_pos)
+{
+ struct lysc_node_leaf *node1, *node2;
+ ly_bool leaves = 1, warning = 0;
+
+ node1 = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set1);
+ node2 = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set2);
+
+ if (!node1 && !node2) {
+ /* no node-sets involved, nothing to do */
+ return;
+ }
+
+ if (node1) {
+ if (!(node1->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(ctx, "Node type %s \"%s\" used as operand.", lys_nodetype2str(node1->nodetype), node1->name);
+ warning = 1;
+ leaves = 0;
+ } else if (numbers_only && !warn_is_numeric_type(node1->type)) {
+ LOGWRN(ctx, "Node \"%s\" is not of a numeric type, but used where it was expected.", node1->name);
+ warning = 1;
+ }
+ }
+
+ if (node2) {
+ if (!(node2->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(ctx, "Node type %s \"%s\" used as operand.", lys_nodetype2str(node2->nodetype), node2->name);
+ warning = 1;
+ leaves = 0;
+ } else if (numbers_only && !warn_is_numeric_type(node2->type)) {
+ LOGWRN(ctx, "Node \"%s\" is not of a numeric type, but used where it was expected.", node2->name);
+ warning = 1;
+ }
+ }
+
+ if (node1 && node2 && leaves && !numbers_only) {
+ if ((warn_is_numeric_type(node1->type) && !warn_is_numeric_type(node2->type)) ||
+ (!warn_is_numeric_type(node1->type) && warn_is_numeric_type(node2->type)) ||
+ (!warn_is_numeric_type(node1->type) && !warn_is_numeric_type(node2->type) &&
+ !warn_is_equal_type(node1->type, node2->type))) {
+ LOGWRN(ctx, "Incompatible types of operands \"%s\" and \"%s\" for comparison.", node1->name, node2->name);
+ warning = 1;
+ }
+ }
+
+ if (warning) {
+ warn_subexpr_log(ctx, tok_pos, expr + tok_pos, 20, set1->cur_scnode);
+ }
+}
+
+/**
+ * @brief Check that a value is valid for a leaf. If not applicable, does nothing.
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] set Set with the leaf/leaf-list.
+ * @param[in] val_exp Index of the value (literal/number) in @p exp.
+ * @param[in] equal_exp Index of the start of the equality expression in @p exp.
+ * @param[in] last_equal_exp Index of the end of the equality expression in @p exp.
+ */
+static void
+warn_equality_value(const struct lyxp_expr *exp, struct lyxp_set *set, uint32_t val_exp, uint32_t equal_exp,
+ uint32_t last_equal_exp)
+{
+ struct lysc_node *scnode;
+ struct lysc_type *type;
+ char *value;
+ struct lyd_value storage;
+ LY_ERR rc;
+ struct ly_err_item *err = NULL;
+
+ if ((scnode = warn_get_scnode_in_ctx(set)) && (scnode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) &&
+ ((exp->tokens[val_exp] == LYXP_TOKEN_LITERAL) || (exp->tokens[val_exp] == LYXP_TOKEN_NUMBER))) {
+ /* check that the node can have the specified value */
+ if (exp->tokens[val_exp] == LYXP_TOKEN_LITERAL) {
+ value = strndup(exp->expr + exp->tok_pos[val_exp] + 1, exp->tok_len[val_exp] - 2);
+ } else {
+ value = strndup(exp->expr + exp->tok_pos[val_exp], exp->tok_len[val_exp]);
+ }
+ if (!value) {
+ LOGMEM(set->ctx);
+ return;
+ }
+
+ if ((((struct lysc_node_leaf *)scnode)->type->basetype == LY_TYPE_IDENT) && !strchr(value, ':')) {
+ LOGWRN(set->ctx, "Identityref \"%s\" comparison with identity \"%s\" without prefix, consider adding"
+ " a prefix or best using \"derived-from(-or-self)()\" functions.", scnode->name, value);
+ warn_subexpr_log(set->ctx, exp->tok_pos[equal_exp], exp->expr + exp->tok_pos[equal_exp],
+ (exp->tok_pos[last_equal_exp] - exp->tok_pos[equal_exp]) + exp->tok_len[last_equal_exp],
+ set->cur_scnode);
+ }
+
+ type = ((struct lysc_node_leaf *)scnode)->type;
+ if (type->basetype != LY_TYPE_IDENT) {
+ rc = type->plugin->store(set->ctx, type, value, strlen(value), 0, set->format, set->prefix_data,
+ LYD_HINT_DATA, scnode, &storage, NULL, &err);
+ if (rc == LY_EINCOMPLETE) {
+ rc = LY_SUCCESS;
+ }
+
+ if (err) {
+ LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type (%s).", value, err->msg);
+ ly_err_free(err);
+ } else if (rc != LY_SUCCESS) {
+ LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type.", value);
+ }
+ if (rc != LY_SUCCESS) {
+ warn_subexpr_log(set->ctx, exp->tok_pos[equal_exp], exp->expr + exp->tok_pos[equal_exp],
+ (exp->tok_pos[last_equal_exp] - exp->tok_pos[equal_exp]) + exp->tok_len[last_equal_exp],
+ set->cur_scnode);
+ } else {
+ type->plugin->free(set->ctx, &storage);
+ }
+ }
+ free(value);
+ }
+}
+
+/*
+ * XPath functions
+ */
+
+/**
+ * @brief Execute the YANG 1.1 bit-is-set(node-set, string) function. Returns LYXP_SET_BOOLEAN
+ * depending on whether the first node bit value from the second argument is set.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_bit_is_set(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lyd_node_term *leaf;
+ struct lysc_node_leaf *sleaf;
+ struct lyd_value_bits *bits;
+ LY_ERR rc = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_BITS)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"bits\".", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "bit-is-set(node-set, string)");
+ return LY_EVALID;
+ }
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ set_fill_boolean(set, 0);
+ if (args[0]->used) {
+ 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);
+ LY_ARRAY_FOR(bits->items, u) {
+ if (!strcmp(bits->items[u]->name, args[1]->val.str)) {
+ set_fill_boolean(set, 1);
+ break;
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath boolean(object) function. Returns LYXP_SET_BOOLEAN
+ * with the argument converted to boolean.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_boolean(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
+ LY_CHECK_RET(rc);
+ set_fill_set(set, args[0]);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath ceiling(number) function. Returns LYXP_SET_NUMBER
+ * with the first argument rounded up to the nearest integer.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_ceiling(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ if ((long long)args[0]->val.num != args[0]->val.num) {
+ set_fill_number(set, ((long long)args[0]->val.num) + 1);
+ } else {
+ set_fill_number(set, args[0]->val.num);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath concat(string, string, string*) function.
+ * Returns LYXP_SET_STRING with the concatenation of all the arguments.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_concat(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ uint32_t i;
+ char *str = NULL;
+ size_t used = 1;
+ LY_ERR rc = LY_SUCCESS;
+ struct lysc_node_leaf *sleaf;
+
+ if (options & LYXP_SCNODE_ALL) {
+ for (i = 0; i < arg_count; ++i) {
+ if ((args[i]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[i]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #%u of %s is a %s node \"%s\".",
+ i + 1, __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #%u of %s is node \"%s\", not of string-type.", i + 1, __func__, sleaf->name);
+ }
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ for (i = 0; i < arg_count; ++i) {
+ rc = lyxp_set_cast(args[i], LYXP_SET_STRING);
+ if (rc != LY_SUCCESS) {
+ free(str);
+ return rc;
+ }
+
+ str = ly_realloc(str, (used + strlen(args[i]->val.str)) * sizeof(char));
+ LY_CHECK_ERR_RET(!str, LOGMEM(set->ctx), LY_EMEM);
+ strcpy(str + used - 1, args[i]->val.str);
+ used += strlen(args[i]->val.str);
+ }
+
+ /* free, kind of */
+ lyxp_set_free_content(set);
+ set->type = LYXP_SET_STRING;
+ set->val.str = str;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath contains(string, string) function.
+ * Returns LYXP_SET_BOOLEAN whether the second argument can
+ * be found in the first or not.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_contains(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ if (strstr(args[0]->val.str, args[1]->val.str)) {
+ set_fill_boolean(set, 1);
+ } else {
+ set_fill_boolean(set, 0);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath count(node-set) function. Returns LYXP_SET_NUMBER
+ * with the size of the node-set from the argument.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_count(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return rc;
+ }
+
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "count(node-set)");
+ return LY_EVALID;
+ }
+
+ set_fill_number(set, args[0]->used);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath current() function. Returns LYXP_SET_NODE_SET
+ * with the context with the intial node.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_current(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ if (arg_count || args) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGCOUNT, arg_count, LY_PRI_LENSTR("current()"));
+ return LY_EVALID;
+ }
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+
+ if (set->cur_scnode) {
+ LY_CHECK_RET(set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ } else {
+ /* root node */
+ LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ }
+ } else {
+ lyxp_set_free_content(set);
+
+ if (set->cur_node) {
+ /* position is filled later */
+ set_insert_node(set, set->cur_node, 0, LYXP_NODE_ELEM, 0);
+ } else {
+ /* root node */
+ set_insert_node(set, NULL, 0, set->root_type, 0);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the YANG 1.1 deref(node-set) function. Returns LYXP_SET_NODE_SET with either
+ * leafref or instance-identifier target node(s).
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lyd_node_term *leaf;
+ struct lysc_node_leaf *sleaf = NULL;
+ struct lysc_type_leafref *lref;
+ const struct lysc_node *target;
+ struct ly_path *p;
+ struct lyd_node *node;
+ char *errmsg = NULL;
+ uint8_t oper;
+ LY_ERR r;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & LYD_NODE_TERM)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_LEAFREF) &&
+ !warn_is_specific_type(sleaf->type, LY_TYPE_INST)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"leafref\" nor \"instance-identifier\".",
+ __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ if (sleaf && (sleaf->nodetype & LYD_NODE_TERM) && (sleaf->type->basetype == LY_TYPE_LEAFREF)) {
+ lref = (struct lysc_type_leafref *)sleaf->type;
+ oper = (sleaf->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
+
+ /* it was already evaluated on schema, it must succeed */
+ r = ly_path_compile_leafref(set->ctx, &sleaf->node, NULL, lref->path, oper, LY_PATH_TARGET_MANY,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, &p);
+ if (!r) {
+ /* get the target node */
+ target = p[LY_ARRAY_COUNT(p) - 1].node;
+ ly_path_free(set->ctx, p);
+
+ LY_CHECK_RET(set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ } /* else the target was found before but is disabled so it was removed */
+ }
+
+ return LY_SUCCESS;
+ }
+
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "deref(node-set)");
+ return LY_EVALID;
+ }
+
+ lyxp_set_free_content(set);
+ if (args[0]->used) {
+ leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
+ sleaf = (struct lysc_node_leaf *)leaf->schema;
+ if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
+ /* find leafref target */
+ if (lyplg_type_resolve_leafref((struct lysc_type_leafref *)sleaf->type, &leaf->node, &leaf->value, set->tree,
+ &node, &errmsg)) {
+ LOGERR(set->ctx, LY_EVALID, "%s", errmsg);
+ free(errmsg);
+ return LY_EVALID;
+ }
+ } else {
+ assert(sleaf->type->basetype == LY_TYPE_INST);
+ if (ly_path_eval(leaf->value.target, set->tree, &node)) {
+ LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.",
+ lyd_get_value(&leaf->node));
+ return LY_EVALID;
+ }
+ }
+
+ /* insert it */
+ set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+xpath_derived_(struct lyxp_set **args, struct lyxp_set *set, uint32_t options, ly_bool self_match, const char *func)
+{
+ uint32_t i, id_len;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_node_term *leaf;
+ struct lysc_node_leaf *sleaf;
+ struct lyd_meta *meta;
+ struct lyd_value *val;
+ const struct lys_module *mod;
+ const char *id_name;
+ struct lysc_ident *id;
+ LY_ERR rc = LY_SUCCESS;
+ ly_bool found;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", func);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", func, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_IDENT)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"identityref\".", func, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", func, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", func, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "derived-from(-or-self)(node-set, string)");
+ return LY_EVALID;
+ }
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ /* parse the identity */
+ id_name = args[1]->val.str;
+ id_len = strlen(id_name);
+ rc = moveto_resolve_model(&id_name, &id_len, set, set->cur_node ? set->cur_node->schema : NULL, &mod);
+ LY_CHECK_RET(rc);
+ if (!mod) {
+ LOGVAL(set->ctx, LYVE_XPATH, "Identity \"%.*s\" without a prefix.", (int)id_len, id_name);
+ return LY_EVALID;
+ }
+
+ /* find the identity */
+ found = 0;
+ LY_ARRAY_FOR(mod->identities, u) {
+ if (!ly_strncmp(mod->identities[u].name, id_name, id_len)) {
+ /* we have match */
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ LOGVAL(set->ctx, LYVE_XPATH, "Identity \"%.*s\" not found in module \"%s\".", (int)id_len, id_name, mod->name);
+ return LY_EVALID;
+ }
+ id = &mod->identities[u];
+
+ set_fill_boolean(set, 0);
+ found = 0;
+ for (i = 0; i < args[0]->used; ++i) {
+ if ((args[0]->val.nodes[i].type != LYXP_NODE_ELEM) && (args[0]->val.nodes[i].type != LYXP_NODE_META)) {
+ continue;
+ }
+
+ if (args[0]->val.nodes[i].type == LYXP_NODE_ELEM) {
+ leaf = (struct lyd_node_term *)args[0]->val.nodes[i].node;
+ sleaf = (struct lysc_node_leaf *)leaf->schema;
+ val = &leaf->value;
+ if (!(sleaf->nodetype & LYD_NODE_TERM) || (leaf->value.realtype->basetype != LY_TYPE_IDENT)) {
+ /* uninteresting */
+ continue;
+ }
+ } else {
+ meta = args[0]->val.meta[i].meta;
+ val = &meta->value;
+ if (val->realtype->basetype != LY_TYPE_IDENT) {
+ /* uninteresting */
+ continue;
+ }
+ }
+
+ /* check the identity itself */
+ if (self_match && (id == val->ident)) {
+ set_fill_boolean(set, 1);
+ found = 1;
+ }
+ if (!found && !lyplg_type_identity_isderived(id, val->ident)) {
+ set_fill_boolean(set, 1);
+ found = 1;
+ }
+
+ if (found) {
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the YANG 1.1 derived-from(node-set, string) function. Returns LYXP_SET_BOOLEAN depending
+ * on whether the first argument nodes contain a node of an identity derived from the second
+ * argument identity.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_derived_from(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ return xpath_derived_(args, set, options, 0, __func__);
+}
+
+/**
+ * @brief Execute the YANG 1.1 derived-from-or-self(node-set, string) function. Returns LYXP_SET_BOOLEAN depending
+ * on whether the first argument nodes contain a node of an identity that either is or is derived from
+ * the second argument identity.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_derived_from_or_self(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ return xpath_derived_(args, set, options, 1, __func__);
+}
+
+/**
+ * @brief Execute the YANG 1.1 enum-value(node-set) function. Returns LYXP_SET_NUMBER
+ * with the integer value of the first node's enum value, otherwise NaN.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_enum_value(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lyd_node_term *leaf;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_ENUM)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"enumeration\".", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "enum-value(node-set)");
+ return LY_EVALID;
+ }
+
+ set_fill_number(set, NAN);
+ if (args[0]->used) {
+ leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
+ sleaf = (struct lysc_node_leaf *)leaf->schema;
+ if ((sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && (sleaf->type->basetype == LY_TYPE_ENUM)) {
+ set_fill_number(set, leaf->value.enum_item->value);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath false() function. Returns LYXP_SET_BOOLEAN
+ * with false value.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_false(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ set_fill_boolean(set, 0);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath floor(number) function. Returns LYXP_SET_NUMBER
+ * with the first argument floored (truncated).
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_floor(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t UNUSED(options))
+{
+ LY_ERR rc;
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ if (isfinite(args[0]->val.num)) {
+ set_fill_number(set, (long long)args[0]->val.num);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath lang(string) function. Returns LYXP_SET_BOOLEAN
+ * whether the language of the text matches the one from the argument.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_lang(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ const struct lyd_node *node;
+ struct lysc_node_leaf *sleaf;
+ struct lyd_meta *meta = NULL;
+ const char *val;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "lang(string)");
+ return LY_EVALID;
+ } else if (!set->used) {
+ set_fill_boolean(set, 0);
+ return LY_SUCCESS;
+ }
+
+ switch (set->val.nodes[0].type) {
+ case LYXP_NODE_ELEM:
+ case LYXP_NODE_TEXT:
+ node = set->val.nodes[0].node;
+ break;
+ case LYXP_NODE_META:
+ node = set->val.meta[0].meta->parent;
+ break;
+ default:
+ /* nothing to do with roots */
+ set_fill_boolean(set, 0);
+ return LY_SUCCESS;
+ }
+
+ /* find lang metadata */
+ for ( ; node; node = lyd_parent(node)) {
+ for (meta = node->meta; meta; meta = meta->next) {
+ /* annotations */
+ if (meta->name && !strcmp(meta->name, "lang") && !strcmp(meta->annotation->module->name, "xml")) {
+ break;
+ }
+ }
+
+ if (meta) {
+ break;
+ }
+ }
+
+ /* compare languages */
+ if (!meta) {
+ set_fill_boolean(set, 0);
+ } else {
+ uint64_t i;
+
+ val = lyd_get_meta_value(meta);
+ for (i = 0; args[0]->val.str[i]; ++i) {
+ if (tolower(args[0]->val.str[i]) != tolower(val[i])) {
+ set_fill_boolean(set, 0);
+ break;
+ }
+ }
+ if (!args[0]->val.str[i]) {
+ if (!val[i] || (val[i] == '-')) {
+ set_fill_boolean(set, 1);
+ } else {
+ set_fill_boolean(set, 0);
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath last() function. Returns LYXP_SET_NUMBER
+ * with the context size.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_last(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "last()");
+ return LY_EVALID;
+ } else if (!set->used) {
+ set_fill_number(set, 0);
+ return LY_SUCCESS;
+ }
+
+ set_fill_number(set, set->ctx_size);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath local-name(node-set?) function. Returns LYXP_SET_STRING
+ * with the node name without namespace from the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_local_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ struct lyxp_set_node *item;
+
+ /* suppress unused variable warning */
+ (void)options;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ if (arg_count) {
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]),
+ "local-name(node-set?)");
+ return LY_EVALID;
+ } else if (!args[0]->used) {
+ set_fill_string(set, "", 0);
+ return LY_SUCCESS;
+ }
+
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(args[0]));
+
+ item = &args[0]->val.nodes[0];
+ } else {
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "local-name(node-set?)");
+ return LY_EVALID;
+ } else if (!set->used) {
+ set_fill_string(set, "", 0);
+ return LY_SUCCESS;
+ }
+
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(set));
+
+ item = &set->val.nodes[0];
+ }
+
+ switch (item->type) {
+ case LYXP_NODE_NONE:
+ LOGINT_RET(set->ctx);
+ case LYXP_NODE_ROOT:
+ case LYXP_NODE_ROOT_CONFIG:
+ case LYXP_NODE_TEXT:
+ set_fill_string(set, "", 0);
+ break;
+ case LYXP_NODE_ELEM:
+ set_fill_string(set, item->node->schema->name, strlen(item->node->schema->name));
+ break;
+ case LYXP_NODE_META:
+ set_fill_string(set, ((struct lyd_meta *)item->node)->name, strlen(((struct lyd_meta *)item->node)->name));
+ break;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath name(node-set?) function. Returns LYXP_SET_STRING
+ * with the node name fully qualified (with namespace) from the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ struct lyxp_set_node *item;
+ struct lys_module *mod = NULL;
+ char *str;
+ const char *name = NULL;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ if (arg_count) {
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "name(node-set?)");
+ return LY_EVALID;
+ } else if (!args[0]->used) {
+ set_fill_string(set, "", 0);
+ return LY_SUCCESS;
+ }
+
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(args[0]));
+
+ item = &args[0]->val.nodes[0];
+ } else {
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "name(node-set?)");
+ return LY_EVALID;
+ } else if (!set->used) {
+ set_fill_string(set, "", 0);
+ return LY_SUCCESS;
+ }
+
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(set));
+
+ item = &set->val.nodes[0];
+ }
+
+ switch (item->type) {
+ case LYXP_NODE_NONE:
+ LOGINT_RET(set->ctx);
+ case LYXP_NODE_ROOT:
+ case LYXP_NODE_ROOT_CONFIG:
+ case LYXP_NODE_TEXT:
+ /* keep NULL */
+ break;
+ case LYXP_NODE_ELEM:
+ mod = item->node->schema->module;
+ name = item->node->schema->name;
+ break;
+ case LYXP_NODE_META:
+ mod = ((struct lyd_meta *)item->node)->annotation->module;
+ name = ((struct lyd_meta *)item->node)->name;
+ break;
+ }
+
+ if (mod && name) {
+ int rc = asprintf(&str, "%s:%s", ly_get_prefix(mod, set->format, set->prefix_data), name);
+
+ LY_CHECK_ERR_RET(rc == -1, LOGMEM(set->ctx), LY_EMEM);
+ set_fill_string(set, str, strlen(str));
+ free(str);
+ } else {
+ set_fill_string(set, "", 0);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath namespace-uri(node-set?) function. Returns LYXP_SET_STRING
+ * with the namespace of the node from the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINVAL for wrong arguments on schema)
+ */
+static LY_ERR
+xpath_namespace_uri(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ struct lyxp_set_node *item;
+ struct lys_module *mod;
+
+ /* suppress unused variable warning */
+ (void)options;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return LY_SUCCESS;
+ }
+
+ if (arg_count) {
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]),
+ "namespace-uri(node-set?)");
+ return LY_EVALID;
+ } else if (!args[0]->used) {
+ set_fill_string(set, "", 0);
+ return LY_SUCCESS;
+ }
+
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(args[0]));
+
+ item = &args[0]->val.nodes[0];
+ } else {
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "namespace-uri(node-set?)");
+ return LY_EVALID;
+ } else if (!set->used) {
+ set_fill_string(set, "", 0);
+ return LY_SUCCESS;
+ }
+
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(set));
+
+ item = &set->val.nodes[0];
+ }
+
+ switch (item->type) {
+ case LYXP_NODE_NONE:
+ LOGINT_RET(set->ctx);
+ case LYXP_NODE_ROOT:
+ case LYXP_NODE_ROOT_CONFIG:
+ case LYXP_NODE_TEXT:
+ set_fill_string(set, "", 0);
+ break;
+ case LYXP_NODE_ELEM:
+ case LYXP_NODE_META:
+ if (item->type == LYXP_NODE_ELEM) {
+ mod = item->node->schema->module;
+ } else { /* LYXP_NODE_META */
+ /* annotations */
+ mod = ((struct lyd_meta *)item->node)->annotation->module;
+ }
+
+ set_fill_string(set, mod->ns, strlen(mod->ns));
+ break;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath normalize-space(string?) function. Returns LYXP_SET_STRING
+ * with normalized value (no leading, trailing, double white spaces) of the node
+ * from the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_normalize_space(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ uint32_t i, new_used;
+ char *new;
+ ly_bool have_spaces = 0, space_before = 0;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (arg_count && (args[0]->type == LYXP_SET_SCNODE_SET) &&
+ (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ if (arg_count) {
+ set_fill_set(set, args[0]);
+ }
+ rc = lyxp_set_cast(set, LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ /* is there any normalization necessary? */
+ for (i = 0; set->val.str[i]; ++i) {
+ if (is_xmlws(set->val.str[i])) {
+ if ((i == 0) || space_before || (!set->val.str[i + 1])) {
+ have_spaces = 1;
+ break;
+ }
+ space_before = 1;
+ } else {
+ space_before = 0;
+ }
+ }
+
+ /* yep, there is */
+ if (have_spaces) {
+ /* it's enough, at least one character will go, makes space for ending '\0' */
+ new = malloc(strlen(set->val.str) * sizeof(char));
+ LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
+ new_used = 0;
+
+ space_before = 0;
+ for (i = 0; set->val.str[i]; ++i) {
+ if (is_xmlws(set->val.str[i])) {
+ if ((i == 0) || space_before) {
+ space_before = 1;
+ continue;
+ } else {
+ space_before = 1;
+ }
+ } else {
+ space_before = 0;
+ }
+
+ new[new_used] = (space_before ? ' ' : set->val.str[i]);
+ ++new_used;
+ }
+
+ /* at worst there is one trailing space now */
+ if (new_used && is_xmlws(new[new_used - 1])) {
+ --new_used;
+ }
+
+ new = ly_realloc(new, (new_used + 1) * sizeof(char));
+ LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
+ new[new_used] = '\0';
+
+ free(set->val.str);
+ set->val.str = new;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath not(boolean) function. Returns LYXP_SET_BOOLEAN
+ * with the argument converted to boolean and logically inverted.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_not(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
+ if (args[0]->val.bln) {
+ set_fill_boolean(set, 0);
+ } else {
+ set_fill_boolean(set, 1);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath number(object?) function. Returns LYXP_SET_NUMBER
+ * with the number representation of either the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_number(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return LY_SUCCESS;
+ }
+
+ if (arg_count) {
+ rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ set_fill_set(set, args[0]);
+ } else {
+ rc = lyxp_set_cast(set, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath position() function. Returns LYXP_SET_NUMBER
+ * with the context position.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_position(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "position()");
+ return LY_EVALID;
+ } else if (!set->used) {
+ set_fill_number(set, 0);
+ return LY_SUCCESS;
+ }
+
+ set_fill_number(set, set->ctx_pos);
+
+ /* UNUSED in 'Release' build type */
+ (void)options;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the YANG 1.1 re-match(string, string) function. Returns LYXP_SET_BOOLEAN
+ * depending on whether the second argument regex matches the first argument string. For details refer to
+ * YANG 1.1 RFC section 10.2.1.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_re_match(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lysc_pattern **patterns = NULL, **pattern;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_err_item *err;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ LY_ARRAY_NEW_RET(set->ctx, patterns, pattern, LY_EMEM);
+ *pattern = calloc(1, sizeof **pattern);
+ LOG_LOCSET(NULL, set->cur_node, NULL, NULL);
+ rc = lys_compile_type_pattern_check(set->ctx, args[1]->val.str, &(*pattern)->code);
+ if (set->cur_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ if (rc != LY_SUCCESS) {
+ LY_ARRAY_FREE(patterns);
+ return rc;
+ }
+
+ rc = lyplg_type_validate_patterns(patterns, args[0]->val.str, strlen(args[0]->val.str), &err);
+ pcre2_code_free((*pattern)->code);
+ free(*pattern);
+ LY_ARRAY_FREE(patterns);
+ if (rc && (rc != LY_EVALID)) {
+ ly_err_print(set->ctx, err);
+ ly_err_free(err);
+ return rc;
+ }
+
+ if (rc == LY_EVALID) {
+ ly_err_free(err);
+ set_fill_boolean(set, 0);
+ } else {
+ set_fill_boolean(set, 1);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath round(number) function. Returns LYXP_SET_NUMBER
+ * with the rounded first argument. For details refer to
+ * http://www.w3.org/TR/1999/REC-xpath-19991116/#function-round.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_round(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+
+ /* cover only the cases where floor can't be used */
+ if ((args[0]->val.num == -0.0f) || ((args[0]->val.num < 0) && (args[0]->val.num >= -0.5))) {
+ set_fill_number(set, -0.0f);
+ } else {
+ args[0]->val.num += 0.5;
+ rc = xpath_floor(args, 1, args[0], options);
+ LY_CHECK_RET(rc);
+ set_fill_number(set, args[0]->val.num);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath starts-with(string, string) function.
+ * Returns LYXP_SET_BOOLEAN whether the second argument is
+ * the prefix of the first or not.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_starts_with(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ if (strncmp(args[0]->val.str, args[1]->val.str, strlen(args[1]->val.str))) {
+ set_fill_boolean(set, 0);
+ } else {
+ set_fill_boolean(set, 1);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath string(object?) function. Returns LYXP_SET_STRING
+ * with the string representation of either the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_string(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return LY_SUCCESS;
+ }
+
+ if (arg_count) {
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ set_fill_set(set, args[0]);
+ } else {
+ rc = lyxp_set_cast(set, LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath string-length(string?) function. Returns LYXP_SET_NUMBER
+ * with the length of the string in either the argument or the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_string_length(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (arg_count && (args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ if (!arg_count && (set->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(set))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #0 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #0 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ if (arg_count) {
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ set_fill_number(set, strlen(args[0]->val.str));
+ } else {
+ rc = lyxp_set_cast(set, LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ set_fill_number(set, strlen(set->val.str));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath substring(string, number, number?) function.
+ * Returns LYXP_SET_STRING substring of the first argument starting
+ * on the second argument index ending on the third argument index,
+ * indexed from 1. For exact definition refer to
+ * http://www.w3.org/TR/1999/REC-xpath-19991116/#function-substring.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_substring(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
+{
+ int64_t start;
+ int32_t len;
+ uint32_t str_start, str_len, pos;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_numeric_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((arg_count == 3) && (args[2]->type == LYXP_SET_SCNODE_SET) &&
+ (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[2]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #3 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_numeric_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #3 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ /* start */
+ if (xpath_round(&args[1], 1, args[1], options)) {
+ return -1;
+ }
+ if (isfinite(args[1]->val.num)) {
+ start = args[1]->val.num - 1;
+ } else if (isinf(args[1]->val.num) && signbit(args[1]->val.num)) {
+ start = INT32_MIN;
+ } else {
+ start = INT32_MAX;
+ }
+
+ /* len */
+ if (arg_count == 3) {
+ rc = xpath_round(&args[2], 1, args[2], options);
+ LY_CHECK_RET(rc);
+ if (isnan(args[2]->val.num) || signbit(args[2]->val.num)) {
+ len = 0;
+ } else if (isfinite(args[2]->val.num)) {
+ len = args[2]->val.num;
+ } else {
+ len = INT32_MAX;
+ }
+ } else {
+ len = INT32_MAX;
+ }
+
+ /* find matching character positions */
+ str_start = 0;
+ str_len = 0;
+ for (pos = 0; args[0]->val.str[pos]; ++pos) {
+ if (pos < start) {
+ ++str_start;
+ } else if (pos < start + len) {
+ ++str_len;
+ } else {
+ break;
+ }
+ }
+
+ set_fill_string(set, args[0]->val.str + str_start, str_len);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath substring-after(string, string) function.
+ * Returns LYXP_SET_STRING with the string succeeding the occurance
+ * of the second argument in the first or an empty string.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_substring_after(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ char *ptr;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ ptr = strstr(args[0]->val.str, args[1]->val.str);
+ if (ptr) {
+ set_fill_string(set, ptr + strlen(args[1]->val.str), strlen(ptr + strlen(args[1]->val.str)));
+ } else {
+ set_fill_string(set, "", 0);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath substring-before(string, string) function.
+ * Returns LYXP_SET_STRING with the string preceding the occurance
+ * of the second argument in the first or an empty string.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_substring_before(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ char *ptr;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ ptr = strstr(args[0]->val.str, args[1]->val.str);
+ if (ptr) {
+ set_fill_string(set, args[0]->val.str, ptr - args[0]->val.str);
+ } else {
+ set_fill_string(set, "", 0);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath sum(node-set) function. Returns LYXP_SET_NUMBER
+ * with the sum of all the nodes in the context.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_sum(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ long double num;
+ char *str;
+ uint32_t i;
+ struct lyxp_set set_item;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type == LYXP_SET_SCNODE_SET) {
+ for (i = 0; i < args[0]->used; ++i) {
+ if (args[0]->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ sleaf = (struct lysc_node_leaf *)args[0]->val.scnodes[i].scnode;
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__,
+ lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_numeric_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of numeric type.", __func__, sleaf->name);
+ }
+ }
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ set_fill_number(set, 0);
+
+ if (args[0]->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "sum(node-set)");
+ return LY_EVALID;
+ } else if (!args[0]->used) {
+ return LY_SUCCESS;
+ }
+
+ set_init(&set_item, set);
+
+ set_item.type = LYXP_SET_NODE_SET;
+ set_item.val.nodes = calloc(1, sizeof *set_item.val.nodes);
+ LY_CHECK_ERR_RET(!set_item.val.nodes, LOGMEM(set->ctx), LY_EMEM);
+
+ set_item.used = 1;
+ set_item.size = 1;
+
+ for (i = 0; i < args[0]->used; ++i) {
+ set_item.val.nodes[0] = args[0]->val.nodes[i];
+
+ rc = cast_node_set_to_string(&set_item, &str);
+ LY_CHECK_RET(rc);
+ num = cast_string_to_number(str);
+ free(str);
+ set->val.num += num;
+ }
+
+ free(set_item.val.nodes);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath translate(string, string, string) function.
+ * Returns LYXP_SET_STRING with the first argument with the characters
+ * from the second argument replaced by those on the corresponding
+ * positions in the third argument.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_translate(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ uint32_t i, j, new_used;
+ char *new;
+ ly_bool have_removed;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if ((args[0]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[1]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[1]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #2 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #2 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+
+ if ((args[2]->type == LYXP_SET_SCNODE_SET) && (sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[2]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #3 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype), sleaf->name);
+ } else if (!warn_is_string_type(sleaf->type)) {
+ LOGWRN(set->ctx, "Argument #3 of %s is node \"%s\", not of string-type.", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
+
+ rc = lyxp_set_cast(args[0], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[1], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(args[2], LYXP_SET_STRING);
+ LY_CHECK_RET(rc);
+
+ new = malloc((strlen(args[0]->val.str) + 1) * sizeof(char));
+ LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
+ new_used = 0;
+
+ have_removed = 0;
+ for (i = 0; args[0]->val.str[i]; ++i) {
+ ly_bool found = 0;
+
+ for (j = 0; args[1]->val.str[j]; ++j) {
+ if (args[0]->val.str[i] == args[1]->val.str[j]) {
+ /* removing this char */
+ if (j >= strlen(args[2]->val.str)) {
+ have_removed = 1;
+ found = 1;
+ break;
+ }
+ /* replacing this char */
+ new[new_used] = args[2]->val.str[j];
+ ++new_used;
+ found = 1;
+ break;
+ }
+ }
+
+ /* copying this char */
+ if (!found) {
+ new[new_used] = args[0]->val.str[i];
+ ++new_used;
+ }
+ }
+
+ if (have_removed) {
+ new = ly_realloc(new, (new_used + 1) * sizeof(char));
+ LY_CHECK_ERR_RET(!new, LOGMEM(set->ctx), LY_EMEM);
+ }
+ new[new_used] = '\0';
+
+ lyxp_set_free_content(set);
+ set->type = LYXP_SET_STRING;
+ set->val.str = new;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath true() function. Returns LYXP_SET_BOOLEAN
+ * with true value.
+ *
+ * @param[in] args Array of arguments.
+ * @param[in] arg_count Count of elements in @p args.
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_true(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
+{
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ return LY_SUCCESS;
+ }
+
+ set_fill_boolean(set, 1);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute the XPath node() processing instruction (node type). Returns LYXP_SET_NODE_SET
+ * with only nodes from the context.
+ *
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] axis Axis to search on.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_pi_node(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
+{
+ if (options & LYXP_SCNODE_ALL) {
+ return moveto_scnode(set, NULL, NULL, axis, options);
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ lyxp_set_free_content(set);
+ return LY_SUCCESS;
+ }
+
+ /* just like moving to a node with no restrictions */
+ return moveto_node(set, NULL, NULL, axis, options);
+}
+
+/**
+ * @brief Execute the XPath text() processing instruction (node type). Returns LYXP_SET_NODE_SET
+ * with the text content of the nodes in the context.
+ *
+ * @param[in,out] set Context and result set at the same time.
+ * @param[in] axis Axis to search on.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+xpath_pi_text(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
+{
+ uint32_t i;
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INCTX, print_set_type(set), "text()");
+ return LY_EVALID;
+ }
+
+ if (axis != LYXP_AXIS_CHILD) {
+ /* even following and preceding axescan return text nodes, but whatever */
+ lyxp_set_free_content(set);
+ return LY_SUCCESS;
+ }
+
+ for (i = 0; i < set->used; ++i) {
+ switch (set->val.nodes[i].type) {
+ case LYXP_NODE_NONE:
+ LOGINT_RET(set->ctx);
+ case LYXP_NODE_ELEM:
+ if (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ set->val.nodes[i].type = LYXP_NODE_TEXT;
+ break;
+ }
+ /* fall through */
+ case LYXP_NODE_ROOT:
+ case LYXP_NODE_ROOT_CONFIG:
+ case LYXP_NODE_TEXT:
+ case LYXP_NODE_META:
+ set_remove_node_none(set, i);
+ break;
+ }
+ }
+ set_remove_nodes_none(set);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Skip prefix and return corresponding model if there is a prefix. Logs directly.
+ *
+ * XPath @p set is expected to be a (sc)node set!
+ *
+ * @param[in,out] qname Qualified node name. If includes prefix, it is skipped.
+ * @param[in,out] qname_len Length of @p qname, is updated accordingly.
+ * @param[in] set Set with general XPath context.
+ * @param[in] ctx_scnode Context node to inherit module for unprefixed node for ::LY_PREF_JSON.
+ * @param[out] moveto_mod Expected module of a matching node.
+ * @return LY_ERR
+ */
+static LY_ERR
+moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_set *set,
+ const struct lysc_node *ctx_scnode, const struct lys_module **moveto_mod)
+{
+ const struct lys_module *mod = NULL;
+ const char *ptr;
+ size_t pref_len;
+
+ assert((set->type == LYXP_SET_NODE_SET) || (set->type == LYXP_SET_SCNODE_SET));
+
+ if ((ptr = ly_strnchr(*qname, ':', *qname_len))) {
+ /* specific module */
+ pref_len = ptr - *qname;
+ mod = ly_resolve_prefix(set->ctx, *qname, pref_len, set->format, set->prefix_data);
+
+ /* check for errors and non-implemented modules, as they are not valid */
+ if (!mod || !mod->implemented) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INMOD, pref_len, *qname);
+ return LY_EVALID;
+ }
+
+ *qname += pref_len + 1;
+ *qname_len -= pref_len + 1;
+ } else if (((*qname)[0] == '*') && (*qname_len == 1)) {
+ /* all modules - special case */
+ mod = NULL;
+ } else {
+ switch (set->format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* current module */
+ mod = set->cur_mod;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* inherit parent (context node) module */
+ if (ctx_scnode) {
+ mod = ctx_scnode->module;
+ } else {
+ mod = NULL;
+ }
+ break;
+ case LY_VALUE_XML:
+ /* all nodes need to be prefixed */
+ LOGVAL(set->ctx, LYVE_DATA, "Non-prefixed node \"%.*s\" in XML xpath found.", *qname_len, *qname);
+ return LY_EVALID;
+ }
+ }
+
+ *moveto_mod = mod;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to the root. Handles absolute path.
+ * Result is LYXP_SET_NODE_SET.
+ *
+ * @param[in,out] set Set to use.
+ * @param[in] options Xpath options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+moveto_root(struct lyxp_set *set, uint32_t options)
+{
+ assert(!(options & LYXP_SKIP_EXPR));
+
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ } else {
+ set->type = LYXP_SET_NODE_SET;
+ set->used = 0;
+ set_insert_node(set, NULL, 0, set->root_type, 0);
+ set->non_child_axis = 0;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check @p node as a part of NameTest processing.
+ *
+ * @param[in] node Node to check.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] set Set to read general context from.
+ * @param[in] node_name Node name in the dictionary to move to, NULL for any node.
+ * @param[in] moveto_mod Expected module of the node, NULL for no prefix.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_ENOT if node does not match, LY_EINCOMPLETE on unresolved when,
+ * LY_EINVAL if neither node nor any children match)
+ */
+static LY_ERR
+moveto_node_check(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyxp_set *set,
+ const char *node_name, const struct lys_module *moveto_mod, uint32_t options)
+{
+ if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
+ assert(node_type == set->root_type);
+
+ if (node_name || moveto_mod) {
+ /* root will not match a specific node */
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+ } else if (node_type != LYXP_NODE_ELEM) {
+ /* other types will not match */
+ return LY_ENOT;
+ }
+
+ if (!node->schema) {
+ /* opaque node never matches */
+ return LY_ENOT;
+ }
+
+ /* module check */
+ if (moveto_mod) {
+ if ((set->ctx == LYD_CTX(node)) && (node->schema->module != moveto_mod)) {
+ return LY_ENOT;
+ } else if ((set->ctx != LYD_CTX(node)) && strcmp(node->schema->module->name, moveto_mod->name)) {
+ return LY_ENOT;
+ }
+ }
+
+ /* context check */
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
+ return LY_EINVAL;
+ } else if (set->context_op && (node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) &&
+ (node->schema != set->context_op)) {
+ return LY_EINVAL;
+ }
+
+ /* name check */
+ if (node_name) {
+ if ((set->ctx == LYD_CTX(node)) && (node->schema->name != node_name)) {
+ return LY_ENOT;
+ } else if ((set->ctx != LYD_CTX(node)) && strcmp(node->schema->name, node_name)) {
+ return LY_ENOT;
+ }
+ }
+
+ /* when check, accept the context node because it should only be the path ".", we have checked the when is valid before */
+ if (!(options & LYXP_IGNORE_WHEN) && lysc_has_when(node->schema) && !(node->flags & LYD_WHEN_TRUE) &&
+ (node != set->cur_node)) {
+ return LY_EINCOMPLETE;
+ }
+
+ /* match */
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get the next node in a forward DFS.
+ *
+ * @param[in] iter Last returned node.
+ * @param[in] stop Node to stop the search on and not return.
+ * @return Next node, NULL if there are no more.
+ */
+static const struct lyd_node *
+moveto_axis_node_next_dfs_forward(const struct lyd_node *iter, const struct lyd_node *stop)
+{
+ const struct lyd_node *next = NULL;
+
+ /* 1) child */
+ next = lyd_child(iter);
+ if (!next) {
+ if (iter == stop) {
+ /* reached stop, no more descendants */
+ return NULL;
+ }
+ /* 2) child next sibling */
+ next = iter->next;
+ }
+ while (!next) {
+ iter = lyd_parent(iter);
+ if ((!stop && !iter) || (stop && (lyd_parent(iter) == lyd_parent(stop)))) {
+ return NULL;
+ }
+ next = iter->next;
+ }
+
+ return next;
+}
+
+/**
+ * @brief Get the next node in a backward DFS.
+ *
+ * @param[in] iter Last returned node.
+ * @param[in] stop Node to stop the search on and not return.
+ * @return Next node, NULL if there are no more.
+ */
+static const struct lyd_node *
+moveto_axis_node_next_dfs_backward(const struct lyd_node *iter, const struct lyd_node *stop)
+{
+ const struct lyd_node *next = NULL;
+
+ /* 1) previous sibling innermost last child */
+ next = iter->prev->next ? iter->prev : NULL;
+ while (next && lyd_child(next)) {
+ next = lyd_child(next);
+ next = next->prev;
+ }
+
+ if (!next) {
+ /* 2) parent */
+ iter = lyd_parent(iter);
+ if ((!stop && !iter) || (stop && (lyd_parent(iter) == lyd_parent(stop)))) {
+ return NULL;
+ }
+ next = iter;
+ }
+
+ return next;
+}
+
+/**
+ * @brief Get the first node on an axis for a context node.
+ *
+ * @param[in,out] iter NULL, updated to the next node.
+ * @param[in,out] iter_type Node type 0 of @p iter, updated to the node type of the next node.
+ * @param[in] node Context node.
+ * @param[in] node_type Type of @p node.
+ * @param[in] axis Axis to use.
+ * @param[in] set XPath set with the general context.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOTFOUND if no next node found.
+ */
+static LY_ERR
+moveto_axis_node_next_first(const struct lyd_node **iter, enum lyxp_node_type *iter_type, const struct lyd_node *node,
+ enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set)
+{
+ const struct lyd_node *next = NULL;
+ enum lyxp_node_type next_type = 0;
+
+ assert(!*iter);
+ assert(!*iter_type);
+
+ switch (axis) {
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ case LYXP_AXIS_SELF:
+ /* return the context node */
+ next = node;
+ next_type = node_type;
+ break;
+
+ case LYXP_AXIS_ANCESTOR:
+ case LYXP_AXIS_PARENT:
+ if (node_type == LYXP_NODE_ELEM) {
+ next = lyd_parent(node);
+ next_type = next ? LYXP_NODE_ELEM : set->root_type;
+ } else if (node_type == LYXP_NODE_TEXT) {
+ next = node;
+ next_type = LYXP_NODE_ELEM;
+ } else if (node_type == LYXP_NODE_META) {
+ next = ((struct lyd_meta *)node)->parent;
+ next_type = LYXP_NODE_ELEM;
+ } /* else root does not have a parent */
+ break;
+
+ case LYXP_AXIS_CHILD:
+ if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
+ assert(!node);
+
+ /* search in all the trees */
+ next = set->tree;
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ } else {
+ /* search in children */
+ next = lyd_child(node);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ }
+ break;
+
+ case LYXP_AXIS_DESCENDANT:
+ if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
+ /* top-level nodes */
+ next = set->tree;
+ next_type = LYXP_NODE_ELEM;
+ } else if (node_type == LYXP_NODE_ELEM) {
+ /* start from the context node */
+ next = moveto_axis_node_next_dfs_forward(node, node);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ } /* else no children */
+ break;
+
+ case LYXP_AXIS_FOLLOWING:
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ if (node_type == LYXP_NODE_ELEM) {
+ /* first next sibling */
+ next = node->next;
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ } /* else no sibling */
+ break;
+
+ case LYXP_AXIS_PRECEDING:
+ if ((node_type == LYXP_NODE_ELEM) && node->prev->next) {
+ /* skip ancestors */
+ next = moveto_axis_node_next_dfs_backward(node, NULL);
+ assert(next);
+ next_type = LYXP_NODE_ELEM;
+ } /* else no sibling */
+ break;
+
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ if (node_type == LYXP_NODE_ELEM) {
+ /* first previous sibling */
+ next = node->prev->next ? node->prev : NULL;
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ } /* else no sibling */
+ break;
+
+ case LYXP_AXIS_ATTRIBUTE:
+ /* handled specially */
+ assert(0);
+ LOGINT(set->ctx);
+ break;
+ }
+
+ *iter = next;
+ *iter_type = next_type;
+ return next_type ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+/**
+ * @brief Iterate over all nodes on an axis for a context node.
+ *
+ * @param[in,out] iter Last returned node, start with NULL, updated to the next node.
+ * @param[in,out] iter_type Node type of @p iter, start with 0, updated to the node type of the next node.
+ * @param[in] node Context node.
+ * @param[in] node_type Type of @p node.
+ * @param[in] axis Axis to use.
+ * @param[in] set XPath set with the general context.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOTFOUND if no next node found.
+ */
+static LY_ERR
+moveto_axis_node_next(const struct lyd_node **iter, enum lyxp_node_type *iter_type, const struct lyd_node *node,
+ enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set)
+{
+ const struct lyd_node *next = NULL;
+ enum lyxp_node_type next_type = 0;
+
+ if (!*iter_type) {
+ /* first returned node */
+ return moveto_axis_node_next_first(iter, iter_type, node, node_type, axis, set);
+ }
+
+ switch (axis) {
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ if ((*iter == node) && (*iter_type == node_type)) {
+ /* fake first ancestor, we returned self before */
+ *iter = NULL;
+ *iter_type = 0;
+ return moveto_axis_node_next_first(iter, iter_type, node, node_type, LYXP_AXIS_ANCESTOR, set);
+ } /* else continue ancestor */
+
+ /* fallthrough */
+ case LYXP_AXIS_ANCESTOR:
+ if (*iter_type == LYXP_NODE_ELEM) {
+ /* iter parent */
+ next = lyd_parent(*iter);
+ next_type = next ? LYXP_NODE_ELEM : set->root_type;
+ } /* else root, no ancestors */
+ break;
+
+ case LYXP_AXIS_CHILD:
+ assert(*iter_type == LYXP_NODE_ELEM);
+
+ /* next sibling (child) */
+ next = (*iter)->next;
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ if ((*iter == node) && (*iter_type == node_type)) {
+ /* fake first descendant, we returned self before */
+ *iter = NULL;
+ *iter_type = 0;
+ return moveto_axis_node_next_first(iter, iter_type, node, node_type, LYXP_AXIS_DESCENDANT, set);
+ } /* else continue descendant */
+
+ /* fallthrough */
+ case LYXP_AXIS_DESCENDANT:
+ assert(*iter_type == LYXP_NODE_ELEM);
+ next = moveto_axis_node_next_dfs_forward(*iter, node);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_FOLLOWING:
+ assert(*iter_type == LYXP_NODE_ELEM);
+ next = moveto_axis_node_next_dfs_forward(*iter, NULL);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ assert(*iter_type == LYXP_NODE_ELEM);
+
+ /* next sibling */
+ next = (*iter)->next;
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_PARENT:
+ case LYXP_AXIS_SELF:
+ /* parent/self was returned before */
+ break;
+
+ case LYXP_AXIS_PRECEDING:
+ assert(*iter_type == LYXP_NODE_ELEM);
+ next = moveto_axis_node_next_dfs_backward(*iter, NULL);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ assert(*iter_type == LYXP_NODE_ELEM);
+
+ /* previous sibling */
+ next = (*iter)->prev->next ? (*iter)->prev : NULL;
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_ATTRIBUTE:
+ /* handled specially */
+ assert(0);
+ LOGINT(set->ctx);
+ break;
+ }
+
+ *iter = next;
+ *iter_type = next_type;
+ return next_type ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+/**
+ * @brief Move context @p set to a node. Result is LYXP_SET_NODE_SET. Context position aware.
+ *
+ * @param[in,out] set Set to use.
+ * @param[in] moveto_mod Matching node module, NULL for no prefix.
+ * @param[in] ncname Matching node name in the dictionary, NULL for any.
+ * @param[in] axis Axis to search on.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+moveto_node(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, enum lyxp_axis axis,
+ uint32_t options)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ const struct lyd_node *iter;
+ enum lyxp_node_type iter_type;
+ struct lyxp_set result;
+ uint32_t i;
+
+ if (options & LYXP_SKIP_EXPR) {
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ return LY_EVALID;
+ }
+
+ /* init result set */
+ set_init(&result, set);
+
+ for (i = 0; i < set->used; ++i) {
+ /* iterate over all the nodes on the axis of the node */
+ iter = NULL;
+ iter_type = 0;
+ while (!moveto_axis_node_next(&iter, &iter_type, set->val.nodes[i].node, set->val.nodes[i].type, axis, set)) {
+ r = moveto_node_check(iter, iter_type, set, ncname, moveto_mod, options);
+ if (r == LY_EINCOMPLETE) {
+ rc = r;
+ goto cleanup;
+ } else if (r) {
+ continue;
+ }
+
+ /* check for duplicates if they are possible */
+ switch (axis) {
+ case LYXP_AXIS_ANCESTOR:
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ case LYXP_AXIS_DESCENDANT:
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ case LYXP_AXIS_FOLLOWING:
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ case LYXP_AXIS_PARENT:
+ case LYXP_AXIS_PRECEDING:
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ result.non_child_axis = 1;
+ if (set_dup_node_check(&result, iter, iter_type, -1)) {
+ continue;
+ }
+ break;
+ case LYXP_AXIS_CHILD:
+ case LYXP_AXIS_SELF:
+ break;
+ case LYXP_AXIS_ATTRIBUTE:
+ /* handled specially */
+ assert(0);
+ LOGINT(set->ctx);
+ break;
+ }
+
+ /* matching node */
+ set_insert_node(&result, iter, 0, iter_type, result.used);
+ }
+ }
+
+ /* move result to the set */
+ lyxp_set_free_content(set);
+ *set = result;
+ result.type = LYXP_SET_NUMBER;
+
+ /* sort the final set if the document order could have been broken */
+ if (set->non_child_axis) {
+ set_sort(set);
+ } else {
+ assert(!set_sort(set));
+ }
+
+cleanup:
+ lyxp_set_free_content(&result);
+ return rc;
+}
+
+/**
+ * @brief Move context @p set to child nodes using hashes. Result is LYXP_SET_NODE_SET. Context position aware.
+ *
+ * @param[in,out] set Set to use.
+ * @param[in] scnode Matching node schema.
+ * @param[in] predicates If @p scnode is ::LYS_LIST or ::LYS_LEAFLIST, the predicates specifying a single instance.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+moveto_node_hash_child(struct lyxp_set *set, const struct lysc_node *scnode, const struct ly_path_predicate *predicates,
+ uint32_t options)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ uint32_t i;
+ const struct lyd_node *siblings;
+ struct lyxp_set result;
+ struct lyd_node *sub, *inst = NULL;
+
+ assert(scnode && (!(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) || predicates));
+
+ /* init result set */
+ set_init(&result, set);
+
+ if (options & LYXP_SKIP_EXPR) {
+ goto cleanup;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* context check for all the nodes since we have the schema node */
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (scnode->flags & LYS_CONFIG_R)) {
+ lyxp_set_free_content(set);
+ goto cleanup;
+ } else if (set->context_op && (scnode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) &&
+ (scnode != set->context_op)) {
+ lyxp_set_free_content(set);
+ goto cleanup;
+ }
+
+ /* create specific data instance if needed */
+ if (scnode->nodetype == LYS_LIST) {
+ LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, &inst), cleanup);
+ } else if (scnode->nodetype == LYS_LEAFLIST) {
+ LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
+ }
+
+ for (i = 0; i < set->used; ++i) {
+ siblings = NULL;
+
+ if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
+ assert(!set->val.nodes[i].node);
+
+ /* search in all the trees */
+ siblings = set->tree;
+ } else if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
+ /* search in children */
+ siblings = lyd_child(set->val.nodes[i].node);
+ }
+
+ /* find the node using hashes */
+ if (inst) {
+ r = lyd_find_sibling_first(siblings, inst, &sub);
+ } else {
+ r = lyd_find_sibling_val(siblings, scnode, NULL, 0, &sub);
+ }
+ LY_CHECK_ERR_GOTO(r && (r != LY_ENOTFOUND), ret = r, cleanup);
+
+ /* when check */
+ if (!(options & LYXP_IGNORE_WHEN) && sub && lysc_has_when(sub->schema) && !(sub->flags & LYD_WHEN_TRUE)) {
+ ret = LY_EINCOMPLETE;
+ goto cleanup;
+ }
+
+ if (sub) {
+ /* pos filled later */
+ set_insert_node(&result, sub, 0, LYXP_NODE_ELEM, result.used);
+ }
+ }
+
+ /* move result to the set */
+ lyxp_set_free_content(set);
+ *set = result;
+ result.type = LYXP_SET_NUMBER;
+ assert(!set_sort(set));
+
+cleanup:
+ lyxp_set_free_content(&result);
+ lyd_free_tree(inst);
+ return ret;
+}
+
+/**
+ * @brief Check @p node as a part of schema NameTest processing.
+ *
+ * @param[in] node Schema node to check.
+ * @param[in] ctx_scnode Context node.
+ * @param[in] set Set to read general context from.
+ * @param[in] node_name Node name in the dictionary to move to, NULL for any nodes.
+ * @param[in] moveto_mod Expected module of the node, NULL for no prefix.
+ * @return LY_ERR (LY_ENOT if node does not match, LY_EINVAL if neither node nor any children match)
+ */
+static LY_ERR
+moveto_scnode_check(const struct lysc_node *node, const struct lysc_node *ctx_scnode, const struct lyxp_set *set,
+ const char *node_name, const struct lys_module *moveto_mod)
+{
+ if (!moveto_mod && node_name) {
+ switch (set->format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* use current module */
+ moveto_mod = set->cur_mod;
+ break;
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* inherit module of the context node, if any */
+ if (ctx_scnode) {
+ moveto_mod = ctx_scnode->module;
+ }
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_XML:
+ /* not defined */
+ LOGINT(set->ctx);
+ return LY_EINVAL;
+ }
+ }
+
+ if (!node) {
+ /* root will not match a specific node */
+ if (node_name || moveto_mod) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+ }
+
+ /* module check */
+ if (moveto_mod && (node->module != moveto_mod)) {
+ return LY_ENOT;
+ }
+
+ /* context check */
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (node->flags & LYS_CONFIG_R)) {
+ return LY_EINVAL;
+ } else if (set->context_op && (node->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node != set->context_op)) {
+ return LY_EINVAL;
+ }
+
+ /* name check */
+ if (node_name && (node->name != node_name)) {
+ return LY_ENOT;
+ }
+
+ /* match */
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get the next node in a forward schema node DFS.
+ *
+ * @param[in] iter Last returned node.
+ * @param[in] stop Node to stop the search on and not return.
+ * @param[in] getnext_opts Options for ::lys_getnext().
+ * @return Next node, NULL if there are no more.
+ */
+static const struct lysc_node *
+moveto_axis_scnode_next_dfs_forward(const struct lysc_node *iter, const struct lysc_node *stop, uint32_t getnext_opts)
+{
+ const struct lysc_node *next = NULL;
+
+ next = lysc_node_child(iter);
+ if (!next) {
+ /* no children, try siblings */
+ if ((iter == stop) || !lysc_data_parent(iter)) {
+ /* we are done, no next element to process */
+ return NULL;
+ }
+
+ next = lys_getnext(iter, lysc_data_parent(iter), NULL, getnext_opts);
+ }
+ while (!next && iter) {
+ /* parent is already processed, go to its sibling */
+ iter = iter->parent;
+ if ((iter == stop) || !lysc_data_parent(iter)) {
+ /* we are done, no next element to process */
+ return NULL;
+ }
+ next = lys_getnext(iter, lysc_data_parent(iter), NULL, getnext_opts);
+ }
+
+ return next;
+}
+
+/**
+ * @brief Consider schema node based on its in_ctx enum value.
+ *
+ * @param[in,out] in_ctx In_ctx enum of the schema node, may be updated.
+ * @param[in] axis Axis to use.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the node should not be returned.
+ */
+static LY_ERR
+moveto_axis_scnode_next_in_ctx(int32_t *in_ctx, enum lyxp_axis axis)
+{
+ switch (axis) {
+ case LYXP_AXIS_SELF:
+ if ((*in_ctx == LYXP_SET_SCNODE_START) || (*in_ctx == LYXP_SET_SCNODE_ATOM_CTX)) {
+ /* additionally put the start node into context */
+ *in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ return LY_SUCCESS;
+ }
+ break;
+ case LYXP_AXIS_PARENT:
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ case LYXP_AXIS_ANCESTOR:
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ case LYXP_AXIS_DESCENDANT:
+ case LYXP_AXIS_FOLLOWING:
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ case LYXP_AXIS_PRECEDING:
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ case LYXP_AXIS_CHILD:
+ if (*in_ctx == LYXP_SET_SCNODE_START) {
+ /* remember that context node was used */
+ *in_ctx = LYXP_SET_SCNODE_START_USED;
+ return LY_SUCCESS;
+ } else if (*in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ /* traversed */
+ *in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ return LY_SUCCESS;
+ }
+ break;
+ case LYXP_AXIS_ATTRIBUTE:
+ /* unreachable */
+ assert(0);
+ LOGINT(NULL);
+ break;
+ }
+
+ return LY_ENOT;
+}
+
+/**
+ * @brief Get previous sibling for a schema node.
+ *
+ * @param[in] scnode Schema node.
+ * @param[in] getnext_opts Options for ::lys_getnext().
+ * @return Previous sibling, NULL if none.
+ */
+static const struct lysc_node *
+moveto_axis_scnode_preceding_sibling(const struct lysc_node *scnode, uint32_t getnext_opts)
+{
+ const struct lysc_node *next = NULL, *prev = NULL;
+
+ while ((next = lys_getnext(next, lysc_data_parent(scnode), scnode->module->compiled, getnext_opts))) {
+ if (next == scnode) {
+ break;
+ }
+
+ prev = next;
+ }
+
+ return prev;
+}
+
+/**
+ * @brief Get the first schema node on an axis for a context node.
+ *
+ * @param[in,out] iter Last returned node, start with NULL, updated to the next node.
+ * @param[in,out] iter_type Node type of @p iter, start with 0, updated to the node type of the next node.
+ * @param[in,out] iter_mod Internal module iterator, do not change.
+ * @param[in,out] iter_mod_idx Internal module index iterator, do not change.
+ * @param[in] scnode Context node.
+ * @param[in] node_type Type of @p scnode.
+ * @param[in] in_ctx In_ctx enum of @p scnode.
+ * @param[in] axis Axis to use.
+ * @param[in] set XPath set with the general context.
+ * @param[in] getnext_opts Options for ::lys_getnext().
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOTFOUND if no next node found.
+ */
+static LY_ERR
+moveto_axis_scnode_next_first(const struct lysc_node **iter, enum lyxp_node_type *iter_type, const struct lys_module **iter_mod,
+ uint32_t *iter_mod_idx, const struct lysc_node *scnode, enum lyxp_node_type node_type, enum lyxp_axis axis,
+ struct lyxp_set *set, uint32_t getnext_opts)
+{
+ const struct lysc_node *next = NULL;
+ enum lyxp_node_type next_type = 0;
+
+ assert(!*iter);
+ assert(!*iter_type);
+
+ *iter_mod = NULL;
+ *iter_mod_idx = 0;
+
+ switch (axis) {
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ case LYXP_AXIS_SELF:
+ if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT) || (node_type == LYXP_NODE_ELEM)) {
+ /* just return the node */
+ next = scnode;
+ next_type = node_type;
+ }
+ break;
+
+ case LYXP_AXIS_ANCESTOR:
+ case LYXP_AXIS_PARENT:
+ if (node_type == LYXP_NODE_ELEM) {
+ next = lysc_data_parent(scnode);
+ next_type = next ? LYXP_NODE_ELEM : set->root_type;
+ } /* else no parent */
+ break;
+
+ case LYXP_AXIS_DESCENDANT:
+ case LYXP_AXIS_CHILD:
+ if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
+ /* it can actually be in any module, it's all <running>, and even if it's moveto_mod (if set),
+ * it can be in a top-level augment */
+ while ((*iter_mod = ly_ctx_get_module_iter(set->ctx, iter_mod_idx))) {
+ /* module may not be implemented or not compiled yet */
+ if (!(*iter_mod)->compiled) {
+ continue;
+ }
+
+ /* get next node */
+ if ((next = lys_getnext(NULL, NULL, (*iter_mod)->compiled, getnext_opts))) {
+ next_type = LYXP_NODE_ELEM;
+ break;
+ }
+ }
+ } else if (node_type == LYXP_NODE_ELEM) {
+ /* get next node */
+ next = lys_getnext(NULL, scnode, NULL, getnext_opts);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ }
+ break;
+
+ case LYXP_AXIS_FOLLOWING:
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ if (node_type == LYXP_NODE_ELEM) {
+ /* first next sibling */
+ next = lys_getnext(scnode, lysc_data_parent(scnode), scnode->module->compiled, getnext_opts);
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ } /* else no sibling */
+ break;
+
+ case LYXP_AXIS_PRECEDING:
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ if (node_type == LYXP_NODE_ELEM) {
+ /* first parent sibling */
+ next = lys_getnext(NULL, lysc_data_parent(scnode), scnode->module->compiled, getnext_opts);
+ if (next == scnode) {
+ /* no preceding sibling */
+ next = NULL;
+ }
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ } /* else no sibling */
+ break;
+
+ case LYXP_AXIS_ATTRIBUTE:
+ /* unreachable */
+ assert(0);
+ LOGINT(set->ctx);
+ break;
+ }
+
+ *iter = next;
+ *iter_type = next_type;
+ return next_type ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+/**
+ * @brief Iterate over all schema nodes on an axis for a context node.
+ *
+ * @param[in,out] iter Last returned node, start with NULL, updated to the next node.
+ * @param[in,out] iter_type Node type of @p iter, start with 0, updated to the node type of the next node.
+ * @param[in,out] iter_mod Internal module iterator, do not change.
+ * @param[in,out] iter_mod_idx Internal module index iterator, do not change.
+ * @param[in] scnode Context node.
+ * @param[in] node_type Type of @p scnode.
+ * @param[in] axis Axis to use.
+ * @param[in] set XPath set with the general context.
+ * @param[in] getnext_opts Options for ::lys_getnext().
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOTFOUND if no next node found.
+ */
+static LY_ERR
+moveto_axis_scnode_next(const struct lysc_node **iter, enum lyxp_node_type *iter_type, const struct lys_module **iter_mod,
+ uint32_t *iter_mod_idx, const struct lysc_node *scnode, enum lyxp_node_type node_type, enum lyxp_axis axis,
+ struct lyxp_set *set, uint32_t getnext_opts)
+{
+ const struct lysc_node *next = NULL, *dfs_stop;
+ enum lyxp_node_type next_type = 0;
+
+ if (!*iter_type) {
+ /* first returned node */
+ return moveto_axis_scnode_next_first(iter, iter_type, iter_mod, iter_mod_idx, scnode, node_type, axis, set,
+ getnext_opts);
+ }
+
+ switch (axis) {
+ case LYXP_AXIS_PARENT:
+ case LYXP_AXIS_SELF:
+ /* parent/self was returned before */
+ break;
+
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ if ((*iter == scnode) && (*iter_type == node_type)) {
+ /* fake first ancestor, we returned self before */
+ *iter = NULL;
+ *iter_type = 0;
+ return moveto_axis_scnode_next_first(iter, iter_type, iter_mod, iter_mod_idx, scnode, node_type,
+ LYXP_AXIS_ANCESTOR, set, getnext_opts);
+ } /* else continue ancestor */
+
+ /* fallthrough */
+ case LYXP_AXIS_ANCESTOR:
+ if (*iter_type == LYXP_NODE_ELEM) {
+ next = lysc_data_parent(*iter);
+ next_type = next ? LYXP_NODE_ELEM : set->root_type;
+ } /* else no ancestor */
+ break;
+
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ if ((*iter == scnode) && (*iter_type == node_type)) {
+ /* fake first descendant, we returned self before */
+ *iter = NULL;
+ *iter_type = 0;
+ return moveto_axis_scnode_next_first(iter, iter_type, iter_mod, iter_mod_idx, scnode, node_type,
+ LYXP_AXIS_DESCENDANT, set, getnext_opts);
+ } /* else DFS until context node */
+ dfs_stop = scnode;
+
+ /* fallthrough */
+ case LYXP_AXIS_DESCENDANT:
+ if (axis == LYXP_AXIS_DESCENDANT) {
+ /* DFS until the context node */
+ dfs_stop = scnode;
+ }
+
+ /* fallthrough */
+ case LYXP_AXIS_PRECEDING:
+ if (axis == LYXP_AXIS_PRECEDING) {
+ /* DFS until the previous sibling */
+ dfs_stop = moveto_axis_scnode_preceding_sibling(scnode, getnext_opts);
+ assert(dfs_stop);
+
+ if (*iter == dfs_stop) {
+ /* we are done */
+ break;
+ }
+ }
+
+ /* fallthrough */
+ case LYXP_AXIS_FOLLOWING:
+ if (axis == LYXP_AXIS_FOLLOWING) {
+ /* DFS through the whole module */
+ dfs_stop = NULL;
+ }
+
+ /* nested nodes */
+ assert(*iter);
+ next = moveto_axis_scnode_next_dfs_forward(*iter, dfs_stop, getnext_opts);
+ if (next) {
+ next_type = LYXP_NODE_ELEM;
+ break;
+ } /* else get next top-level node just like a child */
+
+ /* fallthrough */
+ case LYXP_AXIS_CHILD:
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ if (!*iter_mod) {
+ /* nodes from a single module */
+ if ((next = lys_getnext(*iter, lysc_data_parent(*iter), (*iter)->module->compiled, getnext_opts))) {
+ next_type = LYXP_NODE_ELEM;
+ break;
+ }
+
+ assert(scnode);
+ if ((axis != LYXP_AXIS_CHILD) && !lysc_data_parent(scnode)) {
+ /* iterating over top-level nodes, find next */
+ while (lysc_data_parent(*iter)) {
+ *iter = lysc_data_parent(*iter);
+ }
+ if ((next = lys_getnext(*iter, NULL, (*iter)->module->compiled, getnext_opts))) {
+ next_type = LYXP_NODE_ELEM;
+ break;
+ }
+ }
+ }
+
+ while (*iter_mod) {
+ /* module top-level nodes */
+ if ((next = lys_getnext(*iter, NULL, (*iter_mod)->compiled, getnext_opts))) {
+ next_type = LYXP_NODE_ELEM;
+ break;
+ }
+
+ /* get next module */
+ while ((*iter_mod = ly_ctx_get_module_iter(set->ctx, iter_mod_idx))) {
+ /* module may not be implemented or not compiled yet */
+ if ((*iter_mod)->compiled) {
+ break;
+ }
+ }
+
+ /* new module, start over */
+ *iter = NULL;
+ }
+ break;
+
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ assert(*iter);
+
+ /* next parent sibling until scnode */
+ next = lys_getnext(*iter, lysc_data_parent(*iter), (*iter)->module->compiled, getnext_opts);
+ if (next == scnode) {
+ /* no previous sibling */
+ next = NULL;
+ }
+ next_type = next ? LYXP_NODE_ELEM : 0;
+ break;
+
+ case LYXP_AXIS_ATTRIBUTE:
+ /* unreachable */
+ assert(0);
+ LOGINT(set->ctx);
+ break;
+ }
+
+ *iter = next;
+ *iter_type = next_type;
+ return next_type ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+/**
+ * @brief Move context @p set to a schema node. Result is LYXP_SET_SCNODE_SET (or LYXP_SET_EMPTY).
+ *
+ * @param[in,out] set Set to use.
+ * @param[in] moveto_mod Matching node module, NULL for no prefix.
+ * @param[in] ncname Matching node name in the dictionary, NULL for any.
+ * @param[in] axis Axis to search on.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, enum lyxp_axis axis,
+ uint32_t options)
+{
+ ly_bool temp_ctx = 0;
+ uint32_t getnext_opts, orig_used, i, mod_idx, idx;
+ const struct lys_module *mod;
+ const struct lysc_node *iter;
+ enum lyxp_node_type iter_type;
+
+ if (options & LYXP_SKIP_EXPR) {
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_SCNODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ return LY_EVALID;
+ }
+
+ /* getnext opts */
+ getnext_opts = 0;
+ if (options & LYXP_SCNODE_OUTPUT) {
+ getnext_opts |= LYS_GETNEXT_OUTPUT;
+ }
+
+ orig_used = set->used;
+ for (i = 0; i < orig_used; ++i) {
+ /* update in_ctx first */
+ if (moveto_axis_scnode_next_in_ctx(&set->val.scnodes[i].in_ctx, axis)) {
+ /* not usable, skip */
+ continue;
+ }
+
+ iter = NULL;
+ iter_type = 0;
+ while (!moveto_axis_scnode_next(&iter, &iter_type, &mod, &mod_idx, set->val.scnodes[i].scnode,
+ set->val.scnodes[i].type, axis, set, getnext_opts)) {
+ if (moveto_scnode_check(iter, NULL, set, ncname, moveto_mod)) {
+ continue;
+ }
+
+ /* insert */
+ LY_CHECK_RET(set_scnode_insert_node(set, iter, iter_type, axis, &idx));
+
+ /* we need to prevent these nodes from being considered in this moveto */
+ if ((idx < orig_used) && (idx > i)) {
+ set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_NEW_CTX;
+ temp_ctx = 1;
+ }
+ }
+
+ if (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 */
+ LY_CHECK_RET(set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
+ if ((idx < orig_used) && (idx > i)) {
+ set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_NEW_CTX;
+ temp_ctx = 1;
+ }
+ }
+ }
+
+ /* correct temporary in_ctx values */
+ if (temp_ctx) {
+ for (i = 0; i < orig_used; ++i) {
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_NEW_CTX) {
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to a child node and all its descendants. Result is LYXP_SET_NODE_SET.
+ * Context position aware.
+ *
+ * @param[in] set Set to use.
+ * @param[in] moveto_mod Matching node module, NULL for no prefix.
+ * @param[in] ncname Matching node name in the dictionary, NULL for any.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+moveto_node_alldesc_child(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
+{
+ uint32_t i;
+ const struct lyd_node *next, *elem, *start;
+ struct lyxp_set ret_set;
+ LY_ERR rc;
+
+ if (options & LYXP_SKIP_EXPR) {
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ return LY_EVALID;
+ }
+
+ /* replace the original nodes (and throws away all text and meta nodes, root is replaced by a child) */
+ rc = xpath_pi_node(set, LYXP_AXIS_CHILD, options);
+ LY_CHECK_RET(rc);
+
+ /* this loop traverses all the nodes in the set and adds/keeps only those that match qname */
+ set_init(&ret_set, set);
+ for (i = 0; i < set->used; ++i) {
+
+ /* TREE DFS */
+ start = set->val.nodes[i].node;
+ for (elem = next = start; elem; elem = next) {
+ rc = moveto_node_check(elem, LYXP_NODE_ELEM, set, ncname, moveto_mod, options);
+ if (!rc) {
+ /* add matching node into result set */
+ set_insert_node(&ret_set, elem, 0, LYXP_NODE_ELEM, ret_set.used);
+ if (set_dup_node_check(set, elem, LYXP_NODE_ELEM, i)) {
+ /* the node is a duplicate, we'll process it later in the set */
+ goto skip_children;
+ }
+ } else if (rc == LY_EINCOMPLETE) {
+ return rc;
+ } else if (rc == LY_EINVAL) {
+ goto skip_children;
+ }
+
+ /* TREE DFS NEXT ELEM */
+ /* select element for the next run - children first */
+ next = lyd_child(elem);
+ if (!next) {
+skip_children:
+ /* no children, so try siblings, but only if it's not the start,
+ * that is considered to be the root and it's siblings are not traversed */
+ if (elem != start) {
+ next = elem->next;
+ } else {
+ break;
+ }
+ }
+ while (!next) {
+ /* no siblings, go back through the parents */
+ if (lyd_parent(elem) == start) {
+ /* we are done, no next element to process */
+ break;
+ }
+ /* parent is already processed, go to its sibling */
+ elem = lyd_parent(elem);
+ next = elem->next;
+ }
+ }
+ }
+
+ /* make the temporary set the current one */
+ ret_set.ctx_pos = set->ctx_pos;
+ ret_set.ctx_size = set->ctx_size;
+ lyxp_set_free_content(set);
+ memcpy(set, &ret_set, sizeof *set);
+ assert(!set_sort(set));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to a child schema node and all its descendants starting from a node.
+ * Result is LYXP_SET_NODE_SET.
+ *
+ * @param[in] set Set to use.
+ * @param[in] start Start node whose subtree to add.
+ * @param[in] start_idx Index of @p start in @p set.
+ * @param[in] moveto_mod Matching node module, NULL for no prefix.
+ * @param[in] ncname Matching node name in the dictionary, NULL for any.
+ * @param[in] options XPath options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+moveto_scnode_dfs(struct lyxp_set *set, const struct lysc_node *start, uint32_t start_idx,
+ const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
+{
+ const struct lysc_node *next, *elem;
+ uint32_t idx;
+ LY_ERR rc;
+
+ /* TREE DFS */
+ for (elem = next = start; elem; elem = next) {
+ if ((elem == start) || (elem->nodetype & (LYS_CHOICE | LYS_CASE))) {
+ /* schema-only nodes, skip root */
+ goto next_iter;
+ }
+
+ rc = moveto_scnode_check(elem, start, set, ncname, moveto_mod);
+ if (!rc) {
+ if (lyxp_set_scnode_contains(set, elem, LYXP_NODE_ELEM, start_idx, &idx)) {
+ set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ if (idx > start_idx) {
+ /* we will process it later in the set */
+ goto skip_children;
+ }
+ } else {
+ LY_CHECK_RET(set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
+ }
+ } else if (rc == LY_EINVAL) {
+ goto skip_children;
+ }
+
+next_iter:
+ /* TREE DFS NEXT ELEM */
+ /* select element for the next run - children first */
+ next = lysc_node_child(elem);
+ if (next && (next->nodetype == LYS_INPUT) && (options & LYXP_SCNODE_OUTPUT)) {
+ next = next->next;
+ } else if (next && (next->nodetype == LYS_OUTPUT) && !(options & LYXP_SCNODE_OUTPUT)) {
+ next = next->next;
+ }
+ if (!next) {
+skip_children:
+ /* no children, so try siblings, but only if it's not the start,
+ * that is considered to be the root and it's siblings are not traversed */
+ if (elem != start) {
+ next = elem->next;
+ } else {
+ break;
+ }
+ }
+ while (!next) {
+ /* no siblings, go back through the parents */
+ if (elem->parent == start) {
+ /* we are done, no next element to process */
+ break;
+ }
+ /* parent is already processed, go to its sibling */
+ elem = elem->parent;
+ next = elem->next;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to a child schema node and all its descendants. Result is LYXP_SET_NODE_SET.
+ *
+ * @param[in] set Set to use.
+ * @param[in] moveto_mod Matching node module, NULL for no prefix.
+ * @param[in] ncname Matching node name in the dictionary, NULL for any.
+ * @param[in] options XPath options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+moveto_scnode_alldesc_child(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
+{
+ uint32_t i, orig_used, mod_idx;
+ const struct lys_module *mod;
+ const struct lysc_node *root;
+
+ if (options & LYXP_SKIP_EXPR) {
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_SCNODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ return LY_EVALID;
+ }
+
+ orig_used = set->used;
+ for (i = 0; i < orig_used; ++i) {
+ if (set->val.scnodes[i].in_ctx != LYXP_SET_SCNODE_ATOM_CTX) {
+ if (set->val.scnodes[i].in_ctx != LYXP_SET_SCNODE_START) {
+ continue;
+ }
+
+ /* remember context node */
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_START_USED;
+ } else {
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ }
+
+ if ((set->val.scnodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.scnodes[i].type == LYXP_NODE_ROOT)) {
+ /* traverse all top-level nodes in all the modules */
+ mod_idx = 0;
+ while ((mod = ly_ctx_get_module_iter(set->ctx, &mod_idx))) {
+ /* module may not be implemented or not compiled yet */
+ if (!mod->compiled) {
+ continue;
+ }
+
+ root = NULL;
+ /* no getnext opts needed */
+ while ((root = lys_getnext(root, NULL, mod->compiled, 0))) {
+ LY_CHECK_RET(moveto_scnode_dfs(set, root, i, moveto_mod, ncname, options));
+ }
+ }
+
+ } else if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
+ /* add all the descendants recursively */
+ LY_CHECK_RET(moveto_scnode_dfs(set, set->val.scnodes[i].scnode, i, moveto_mod, ncname, options));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to an attribute. Result is LYXP_SET_NODE_SET.
+ * Indirectly context position aware.
+ *
+ * @param[in,out] set Set to use.
+ * @param[in] mod Matching metadata module, NULL for any.
+ * @param[in] ncname Matching metadata name in the dictionary, NULL for any.
+ * @param[in] options XPath options.
+ * @return LY_ERR
+ */
+static LY_ERR
+moveto_attr(struct lyxp_set *set, const struct lys_module *mod, const char *ncname, uint32_t options)
+{
+ struct lyd_meta *sub;
+
+ if (options & LYXP_SKIP_EXPR) {
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ return LY_EVALID;
+ }
+
+ for (uint32_t i = 0; i < set->used; ) {
+ ly_bool replaced = 0;
+
+ /* only attributes of an elem (not dummy) can be in the result, skip all the rest;
+ * our attributes are always qualified */
+ if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
+ for (sub = set->val.nodes[i].node->meta; sub; sub = sub->next) {
+
+ /* check "namespace" */
+ if (mod && (sub->annotation->module != mod)) {
+ continue;
+ }
+
+ if (!ncname || (sub->name == ncname)) {
+ /* match */
+ if (!replaced) {
+ set->val.meta[i].meta = sub;
+ set->val.meta[i].type = LYXP_NODE_META;
+ /* pos does not change */
+ replaced = 1;
+ } else {
+ set_insert_node(set, (struct lyd_node *)sub, set->val.nodes[i].pos, LYXP_NODE_META, i + 1);
+ }
+ ++i;
+ }
+ }
+ }
+
+ if (!replaced) {
+ /* no match */
+ set_remove_node(set, i);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set1 to union with @p set2. @p set2 is emptied afterwards.
+ * Result is LYXP_SET_NODE_SET. Context position aware.
+ *
+ * @param[in,out] set1 Set to use for the result.
+ * @param[in] set2 Set that is copied to @p set1.
+ * @return LY_ERR
+ */
+static LY_ERR
+moveto_union(struct lyxp_set *set1, struct lyxp_set *set2)
+{
+ LY_ERR rc;
+
+ if ((set1->type != LYXP_SET_NODE_SET) || (set2->type != LYXP_SET_NODE_SET)) {
+ LOGVAL(set1->ctx, LY_VCODE_XP_INOP_2, "union", print_set_type(set1), print_set_type(set2));
+ return LY_EVALID;
+ }
+
+ /* set2 is empty or both set1 and set2 */
+ if (!set2->used) {
+ return LY_SUCCESS;
+ }
+
+ if (!set1->used) {
+ /* release hidden allocated data (lyxp_set.size) */
+ lyxp_set_free_content(set1);
+ /* direct copying of the entire structure */
+ memcpy(set1, set2, sizeof *set1);
+ /* dynamic memory belongs to set1 now, do not free */
+ memset(set2, 0, sizeof *set2);
+ return LY_SUCCESS;
+ }
+
+ /* we assume sets are sorted */
+ assert(!set_sort(set1) && !set_sort(set2));
+
+ /* sort, remove duplicates */
+ rc = set_sorted_merge(set1, set2);
+ LY_CHECK_RET(rc);
+
+ /* final set must be sorted */
+ assert(!set_sort(set1));
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to an attribute in any of the descendants. Result is LYXP_SET_NODE_SET.
+ * Context position aware.
+ *
+ * @param[in,out] set Set to use.
+ * @param[in] mod Matching metadata module, NULL for any.
+ * @param[in] ncname Matching metadata name in the dictionary, NULL for any.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static int
+moveto_attr_alldesc(struct lyxp_set *set, const struct lys_module *mod, const char *ncname, uint32_t options)
+{
+ struct lyd_meta *sub;
+ struct lyxp_set *set_all_desc = NULL;
+ LY_ERR rc;
+
+ if (options & LYXP_SKIP_EXPR) {
+ return LY_SUCCESS;
+ }
+
+ if (set->type != LYXP_SET_NODE_SET) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ return LY_EVALID;
+ }
+
+ /* can be optimized similarly to moveto_node_alldesc() and save considerable amount of memory,
+ * but it likely won't be used much, so it's a waste of time */
+ /* copy the context */
+ set_all_desc = set_copy(set);
+ /* get all descendant nodes (the original context nodes are removed) */
+ rc = moveto_node_alldesc_child(set_all_desc, NULL, NULL, options);
+ if (rc != LY_SUCCESS) {
+ lyxp_set_free(set_all_desc);
+ return rc;
+ }
+ /* prepend the original context nodes */
+ rc = moveto_union(set, set_all_desc);
+ if (rc != LY_SUCCESS) {
+ lyxp_set_free(set_all_desc);
+ return rc;
+ }
+ lyxp_set_free(set_all_desc);
+
+ for (uint32_t i = 0; i < set->used; ) {
+ ly_bool replaced = 0;
+
+ /* only attributes of an elem can be in the result, skip all the rest,
+ * we have all attributes qualified in lyd tree */
+ if (set->val.nodes[i].type == LYXP_NODE_ELEM) {
+ for (sub = set->val.nodes[i].node->meta; sub; sub = sub->next) {
+ /* check "namespace" */
+ if (mod && (sub->annotation->module != mod)) {
+ continue;
+ }
+
+ if (!ncname || (sub->name == ncname)) {
+ /* match */
+ if (!replaced) {
+ set->val.meta[i].meta = sub;
+ set->val.meta[i].type = LYXP_NODE_META;
+ /* pos does not change */
+ replaced = 1;
+ } else {
+ set_insert_node(set, (struct lyd_node *)sub, set->val.meta[i].pos, LYXP_NODE_META, i + 1);
+ }
+ ++i;
+ }
+ }
+ }
+
+ if (!replaced) {
+ /* no match */
+ set_remove_node(set, i);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set1 single item to the result of a comparison.
+ *
+ * @param[in] set1 First set with the item to compare.
+ * @param[in] idx1 Index of the item in @p set1.
+ * @param[in] set2 Second set.
+ * @param[in] op Comparison operator to process.
+ * @param[in] switch_operands Whether to switch sets as operands; whether it is `set1 op set2` or `set2 op set1`.
+ * @param[out] result Result of the comparison.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+moveto_op_comp_item(const struct lyxp_set *set1, uint32_t idx1, struct lyxp_set *set2, const char *op,
+ ly_bool switch_operands, ly_bool *result)
+{
+ struct lyxp_set tmp1 = {0};
+ LY_ERR rc = LY_SUCCESS;
+
+ assert(set1->type == LYXP_SET_NODE_SET);
+
+ /* cast set1 */
+ switch (set2->type) {
+ case LYXP_SET_NUMBER:
+ rc = set_comp_cast(&tmp1, set1, LYXP_SET_NUMBER, idx1);
+ break;
+ case LYXP_SET_BOOLEAN:
+ rc = set_comp_cast(&tmp1, set1, LYXP_SET_BOOLEAN, idx1);
+ break;
+ default:
+ rc = set_comp_cast(&tmp1, set1, LYXP_SET_STRING, idx1);
+ break;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* canonize set2 */
+ LY_CHECK_GOTO(rc = set_comp_canonize(set2, &set1->val.nodes[idx1]), cleanup);
+
+ /* compare recursively and store the result */
+ if (switch_operands) {
+ LY_CHECK_GOTO(rc = moveto_op_comp(set2, &tmp1, op, result), cleanup);
+ } else {
+ LY_CHECK_GOTO(rc = moveto_op_comp(&tmp1, set2, op, result), cleanup);
+ }
+
+cleanup:
+ lyxp_set_free_content(&tmp1);
+ return rc;
+}
+
+/**
+ * @brief Move context @p set1 to the result of a comparison. Handles '=', '!=', '<=', '<', '>=', or '>'.
+ * Result is LYXP_SET_BOOLEAN. Indirectly context position aware.
+ *
+ * @param[in] set1 Set acting as the first operand for @p op.
+ * @param[in] set2 Set acting as the second operand for @p op.
+ * @param[in] op Comparison operator to process.
+ * @param[out] result Result of the comparison.
+ * @return LY_ERR
+ */
+static LY_ERR
+moveto_op_comp(struct lyxp_set *set1, struct lyxp_set *set2, const char *op, ly_bool *result)
+{
+ /*
+ * NODE SET + NODE SET = NODE SET + STRING /(1 NODE SET) 2 STRING
+ * NODE SET + STRING = STRING + STRING /1 STRING (2 STRING)
+ * NODE SET + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
+ * NODE SET + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
+ * STRING + NODE SET = STRING + STRING /(1 STRING) 2 STRING
+ * NUMBER + NODE SET = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
+ * BOOLEAN + NODE SET = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
+ *
+ * '=' or '!='
+ * BOOLEAN + BOOLEAN
+ * BOOLEAN + STRING = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
+ * BOOLEAN + NUMBER = BOOLEAN + BOOLEAN /(1 BOOLEAN) 2 BOOLEAN
+ * STRING + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
+ * NUMBER + BOOLEAN = BOOLEAN + BOOLEAN /1 BOOLEAN (2 BOOLEAN)
+ * NUMBER + NUMBER
+ * NUMBER + STRING = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
+ * STRING + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
+ * STRING + STRING
+ *
+ * '<=', '<', '>=', '>'
+ * NUMBER + NUMBER
+ * BOOLEAN + BOOLEAN = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
+ * BOOLEAN + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
+ * BOOLEAN + STRING = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
+ * NUMBER + STRING = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
+ * STRING + STRING = NUMBER + NUMBER /1 NUMBER, 2 NUMBER
+ * STRING + NUMBER = NUMBER + NUMBER /1 NUMBER (2 NUMBER)
+ * NUMBER + BOOLEAN = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
+ * STRING + BOOLEAN = NUMBER + NUMBER /(1 NUMBER) 2 NUMBER
+ */
+ uint32_t i;
+ LY_ERR rc;
+
+ /* iterative evaluation with node-sets */
+ if ((set1->type == LYXP_SET_NODE_SET) || (set2->type == LYXP_SET_NODE_SET)) {
+ if (set1->type == LYXP_SET_NODE_SET) {
+ for (i = 0; i < set1->used; ++i) {
+ /* evaluate for the single item */
+ LY_CHECK_RET(moveto_op_comp_item(set1, i, set2, op, 0, result));
+
+ /* lazy evaluation until true */
+ if (*result) {
+ return LY_SUCCESS;
+ }
+ }
+ } else {
+ for (i = 0; i < set2->used; ++i) {
+ /* evaluate for the single item */
+ LY_CHECK_RET(moveto_op_comp_item(set2, i, set1, op, 1, result));
+
+ /* lazy evaluation until true */
+ if (*result) {
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ /* false for all the nodes */
+ *result = 0;
+ return LY_SUCCESS;
+ }
+
+ /* first convert properly */
+ if ((op[0] == '=') || (op[0] == '!')) {
+ if ((set1->type == LYXP_SET_BOOLEAN) || (set2->type == LYXP_SET_BOOLEAN)) {
+ lyxp_set_cast(set1, LYXP_SET_BOOLEAN);
+ lyxp_set_cast(set2, LYXP_SET_BOOLEAN);
+ } else if ((set1->type == LYXP_SET_NUMBER) || (set2->type == LYXP_SET_NUMBER)) {
+ rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ } /* else we have 2 strings */
+ } else {
+ rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ }
+
+ assert(set1->type == set2->type);
+
+ /* compute result */
+ if (op[0] == '=') {
+ if (set1->type == LYXP_SET_BOOLEAN) {
+ *result = (set1->val.bln == set2->val.bln);
+ } else if (set1->type == LYXP_SET_NUMBER) {
+ *result = (set1->val.num == set2->val.num);
+ } else {
+ assert(set1->type == LYXP_SET_STRING);
+ *result = strcmp(set1->val.str, set2->val.str) ? 0 : 1;
+ }
+ } else if (op[0] == '!') {
+ if (set1->type == LYXP_SET_BOOLEAN) {
+ *result = (set1->val.bln != set2->val.bln);
+ } else if (set1->type == LYXP_SET_NUMBER) {
+ *result = (set1->val.num != set2->val.num);
+ } else {
+ assert(set1->type == LYXP_SET_STRING);
+ *result = strcmp(set1->val.str, set2->val.str) ? 1 : 0;
+ }
+ } else {
+ assert(set1->type == LYXP_SET_NUMBER);
+ if (op[0] == '<') {
+ if (op[1] == '=') {
+ *result = (set1->val.num <= set2->val.num);
+ } else {
+ *result = (set1->val.num < set2->val.num);
+ }
+ } else {
+ if (op[1] == '=') {
+ *result = (set1->val.num >= set2->val.num);
+ } else {
+ *result = (set1->val.num > set2->val.num);
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move context @p set to the result of a basic operation. Handles '+', '-', unary '-', '*', 'div',
+ * or 'mod'. Result is LYXP_SET_NUMBER. Indirectly context position aware.
+ *
+ * @param[in,out] set1 Set to use for the result.
+ * @param[in] set2 Set acting as the second operand for @p op.
+ * @param[in] op Operator to process.
+ * @return LY_ERR
+ */
+static LY_ERR
+moveto_op_math(struct lyxp_set *set1, struct lyxp_set *set2, const char *op)
+{
+ LY_ERR rc;
+
+ /* unary '-' */
+ if (!set2 && (op[0] == '-')) {
+ rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ set1->val.num *= -1;
+ lyxp_set_free(set2);
+ return LY_SUCCESS;
+ }
+
+ assert(set1 && set2);
+
+ rc = lyxp_set_cast(set1, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+ rc = lyxp_set_cast(set2, LYXP_SET_NUMBER);
+ LY_CHECK_RET(rc);
+
+ switch (op[0]) {
+ /* '+' */
+ case '+':
+ set1->val.num += set2->val.num;
+ break;
+
+ /* '-' */
+ case '-':
+ set1->val.num -= set2->val.num;
+ break;
+
+ /* '*' */
+ case '*':
+ set1->val.num *= set2->val.num;
+ break;
+
+ /* 'div' */
+ case 'd':
+ set1->val.num /= set2->val.num;
+ break;
+
+ /* 'mod' */
+ case 'm':
+ set1->val.num = ((long long)set1->val.num) % ((long long)set2->val.num);
+ break;
+
+ default:
+ LOGINT_RET(set1->ctx);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate Predicate. Logs directly on error.
+ *
+ * [9] Predicate ::= '[' Expr ']'
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @param[in] axis Axis to search on.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options, enum lyxp_axis axis)
+{
+ LY_ERR rc;
+ uint32_t i, orig_exp, orig_pos, orig_size;
+ int32_t pred_in_ctx;
+ ly_bool reverse_axis = 0;
+ struct lyxp_set set2 = {0};
+
+ /* '[' */
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+only_parse:
+ rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ LY_CHECK_RET(rc);
+ } else if (set->type == LYXP_SET_NODE_SET) {
+ /* we (possibly) need the set sorted, it can affect the result (if the predicate result is a number) */
+ assert(!set_sort(set));
+
+ /* empty set, nothing to evaluate */
+ if (!set->used) {
+ goto only_parse;
+ }
+
+ /* decide forward or reverse axis */
+ switch (axis) {
+ case LYXP_AXIS_ANCESTOR:
+ case LYXP_AXIS_ANCESTOR_OR_SELF:
+ case LYXP_AXIS_PRECEDING:
+ case LYXP_AXIS_PRECEDING_SIBLING:
+ reverse_axis = 1;
+ break;
+ case LYXP_AXIS_DESCENDANT:
+ case LYXP_AXIS_DESCENDANT_OR_SELF:
+ case LYXP_AXIS_FOLLOWING:
+ case LYXP_AXIS_FOLLOWING_SIBLING:
+ case LYXP_AXIS_PARENT:
+ case LYXP_AXIS_CHILD:
+ case LYXP_AXIS_SELF:
+ case LYXP_AXIS_ATTRIBUTE:
+ reverse_axis = 0;
+ break;
+ }
+
+ orig_exp = *tok_idx;
+ orig_pos = reverse_axis ? set->used + 1 : 0;
+ orig_size = set->used;
+ for (i = 0; i < set->used; ++i) {
+ set_init(&set2, set);
+ set_insert_node(&set2, set->val.nodes[i].node, set->val.nodes[i].pos, set->val.nodes[i].type, 0);
+
+ /* remember the node context position for position() and context size for last() */
+ orig_pos += reverse_axis ? -1 : 1;
+
+ set2.ctx_pos = orig_pos;
+ set2.ctx_size = orig_size;
+ *tok_idx = orig_exp;
+
+ rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
+ if (rc != LY_SUCCESS) {
+ lyxp_set_free_content(&set2);
+ return rc;
+ }
+
+ /* number is a proximity position */
+ if (set2.type == LYXP_SET_NUMBER) {
+ if ((long long)set2.val.num == orig_pos) {
+ set2.val.num = 1;
+ } else {
+ set2.val.num = 0;
+ }
+ }
+ lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
+
+ /* predicate satisfied or not? */
+ if (!set2.val.bln) {
+ set_remove_node_none(set, i);
+ }
+ }
+ set_remove_nodes_none(set);
+
+ } else if (set->type == LYXP_SET_SCNODE_SET) {
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ /* there is a currently-valid node */
+ break;
+ }
+ }
+ /* empty set, nothing to evaluate */
+ if (i == set->used) {
+ goto only_parse;
+ }
+
+ orig_exp = *tok_idx;
+
+ /* set special in_ctx to all the valid snodes */
+ pred_in_ctx = set_scnode_new_in_ctx(set);
+
+ /* use the valid snodes one-by-one */
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx != pred_in_ctx) {
+ continue;
+ }
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+
+ *tok_idx = orig_exp;
+
+ rc = eval_expr_select(exp, tok_idx, 0, set, options);
+ LY_CHECK_RET(rc);
+
+ set->val.scnodes[i].in_ctx = pred_in_ctx;
+ }
+
+ /* restore the state as it was before the predicate */
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ } else if (set->val.scnodes[i].in_ctx == pred_in_ctx) {
+ set->val.scnodes[i].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ }
+ }
+
+ } else {
+ set2.type = LYXP_SET_NODE_SET;
+ set_fill_set(&set2, set);
+
+ rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
+ if (rc != LY_SUCCESS) {
+ lyxp_set_free_content(&set2);
+ return rc;
+ }
+
+ lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
+ if (!set2.val.bln) {
+ lyxp_set_free_content(set);
+ }
+ lyxp_set_free_content(&set2);
+ }
+
+ /* ']' */
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate Literal. Logs directly on error.
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in,out] set Context and result set. On NULL the rule is only parsed.
+ */
+static void
+eval_literal(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set)
+{
+ if (set) {
+ if (exp->tok_len[*tok_idx] == 2) {
+ set_fill_string(set, "", 0);
+ } else {
+ set_fill_string(set, &exp->expr[exp->tok_pos[*tok_idx] + 1], exp->tok_len[*tok_idx] - 2);
+ }
+ }
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+}
+
+/**
+ * @brief Check that a nametest in a predicate matches a key node.
+ *
+ * @param[in] nametest Nametest to check.
+ * @param[in] len Length of @p nametest.
+ * @param[in] ctx_scnode Found schema node as the context for the predicate.
+ * @param[in] set Context set.
+ * @param[in] key Expected key node.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if a predicate could not be compiled.
+ * @return LY_ERR on any error.
+ */
+static LY_ERR
+eval_name_test_try_compile_predicate_key(const char *nametest, uint32_t len, const struct lysc_node *ctx_scnode,
+ const struct lyxp_set *set, const struct lysc_node *key)
+{
+ const struct lys_module *mod;
+
+ /* prefix (module) */
+ LY_CHECK_RET(moveto_resolve_model(&nametest, &len, set, ctx_scnode, &mod));
+ if (mod != key->module) {
+ return LY_ENOT;
+ }
+
+ /* node name */
+ if (ly_strncmp(key->name, nametest, len)) {
+ return LY_ENOT;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Append a simple predicate for the node.
+ *
+ * @param[in] exp Full parsed XPath expression.
+ * @param[in] tok_idx Predicate start index in @p exp.
+ * @param[in] end_tok_idx Predicate end index in @p exp.
+ * @param[in] ctx_scnode Found schema node as the context for the predicate.
+ * @param[in] set Context set.
+ * @param[in] pred_node Node with the value referenced in the predicate.
+ * @param[in,out] pred Predicate to append to.
+ * @param[in,out] pred_len Length of @p pred, is updated.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if a predicate could not be compiled.
+ * @return LY_ERR on any error.
+ */
+static LY_ERR
+eval_name_test_try_compile_predicate_append(const struct lyxp_expr *exp, uint32_t tok_idx, uint32_t end_tok_idx,
+ const struct lysc_node *ctx_scnode, const struct lyxp_set *set, const struct lysc_node *pred_node, char **pred,
+ uint32_t *pred_len)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint32_t i;
+ const struct lyd_node *siblings;
+ struct lyd_node *ctx_node;
+ const struct lysc_node *sparent, *cur_scnode;
+ struct lyxp_expr *val_exp = NULL;
+ struct lyxp_set set2 = {0};
+ char quot;
+
+ /* duplicate the value expression */
+ LY_CHECK_GOTO(rc = lyxp_expr_dup(set->ctx, exp, tok_idx, end_tok_idx, &val_exp), cleanup);
+
+ /* get its atoms */
+ cur_scnode = set->cur_node ? set->cur_node->schema : NULL;
+ LY_CHECK_GOTO(rc = lyxp_atomize(set->ctx, val_exp, set->cur_mod, set->format, set->prefix_data, cur_scnode,
+ ctx_scnode, &set2, LYXP_SCNODE), cleanup);
+
+ /* check whether we can compile a single predicate (evaluation result value is always the same) */
+ for (i = 0; i < set2.used; ++i) {
+ if ((set2.val.scnodes[i].type != LYXP_NODE_ELEM) || (set2.val.scnodes[i].in_ctx < LYXP_SET_SCNODE_ATOM_NODE)) {
+ /* skip root and context node */
+ continue;
+ }
+
+ /* 1) context node descendants are traversed - do best-effort detection of the value dependency on the
+ * context node instance */
+ if ((set2.val.scnodes[i].axis == LYXP_AXIS_CHILD) && (set2.val.scnodes[i].scnode->parent == ctx_scnode)) {
+ /* 1.1) context node child was accessed on the child axis, certain dependency */
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ if ((set2.val.scnodes[i].axis == LYXP_AXIS_DESCENDANT) || (set2.val.scnodes[i].axis == LYXP_AXIS_DESCENDANT_OR_SELF)) {
+ for (sparent = set2.val.scnodes[i].scnode->parent; sparent && (sparent != ctx_scnode); sparent = sparent->parent) {}
+ if (sparent) {
+ /* 1.2) context node descendant was accessed on the descendant axis, probable dependency */
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ }
+
+ /* 2) multi-instance nodes (list or leaf-list) are traversed - all the instances need to be considered,
+ * but the current node can be safely ignored, it is always the same data instance */
+ if ((set2.val.scnodes[i].scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (cur_scnode != set2.val.scnodes[i].scnode)) {
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ }
+
+ /* get any data instance of the context node, we checked it makes no difference */
+ siblings = set->val.nodes[0].node ? lyd_child(set->val.nodes[0].node) : set->tree;
+ LY_CHECK_GOTO(rc = lyd_find_sibling_schema(siblings, ctx_scnode, &ctx_node), cleanup);
+
+ /* evaluate the value subexpression with the root context node */
+ lyxp_set_free_content(&set2);
+ LY_CHECK_GOTO(rc = lyxp_eval(set->ctx, val_exp, set->cur_mod, set->format, set->prefix_data, set->cur_node,
+ ctx_node, set->tree, NULL, &set2, 0), cleanup);
+
+ /* cast it into a string */
+ LY_CHECK_GOTO(rc = lyxp_set_cast(&set2, LYXP_SET_STRING), cleanup);
+
+ /* append the JSON predicate */
+ *pred = ly_realloc(*pred, *pred_len + 1 + strlen(pred_node->name) + 2 + strlen(set2.val.str) + 3);
+ LY_CHECK_ERR_GOTO(!*pred, LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
+ quot = strchr(set2.val.str, '\'') ? '\"' : '\'';
+ *pred_len += sprintf(*pred + *pred_len, "[%s=%c%s%c]", pred_node->name, quot, set2.val.str, quot);
+
+cleanup:
+ lyxp_expr_free(set->ctx, val_exp);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Try to compile list or leaf-list predicate in the known format to be used for hash-based instance search.
+ *
+ * @param[in] exp Full parsed XPath expression.
+ * @param[in,out] tok_idx Index in @p exp at the beginning of the predicate, is updated on success.
+ * @param[in] ctx_scnode Found schema node as the context for the predicate.
+ * @param[in] set Context set.
+ * @param[out] predicates Parsed predicates.
+ * @param[out] pred_type Type of @p predicates.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if a predicate could not be compiled.
+ * @return LY_ERR on any error.
+ */
+static LY_ERR
+eval_name_test_try_compile_predicates(const struct lyxp_expr *exp, uint32_t *tok_idx, const struct lysc_node *ctx_scnode,
+ const struct lyxp_set *set, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint32_t e_idx, val_start_idx, pred_idx = 0, temp_lo = 0, pred_len = 0, nested_pred;
+ const struct lysc_node *key;
+ char *pred = NULL;
+ struct lyxp_expr *exp2 = NULL;
+
+ assert(ctx_scnode->nodetype & (LYS_LIST | LYS_LEAFLIST));
+
+ /* turn logging off */
+ ly_temp_log_options(&temp_lo);
+
+ if (ctx_scnode->nodetype == LYS_LIST) {
+ /* check for predicates "[key1=...][key2=...]..." */
+
+ /* get key count */
+ if (ctx_scnode->flags & LYS_KEYLESS) {
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+
+ /* learn where the predicates end */
+ e_idx = *tok_idx;
+ for (key = lysc_node_child(ctx_scnode); key && (key->flags & LYS_KEY); key = key->next) {
+ /* '[' */
+ if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ ++e_idx;
+
+ if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_NAMETEST)) {
+ /* not a key */
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+
+ /* check key */
+ LY_CHECK_GOTO(rc = eval_name_test_try_compile_predicate_key(exp->expr + exp->tok_pos[e_idx],
+ exp->tok_len[e_idx], ctx_scnode, set, key), cleanup);
+
+ ++e_idx;
+
+ if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_EQUAL)) {
+ /* not '=' */
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ ++e_idx;
+
+ /* value start */
+ val_start_idx = e_idx;
+
+ /* ']' */
+ nested_pred = 1;
+ do {
+ ++e_idx;
+
+ if ((nested_pred == 1) && !lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_LOG)) {
+ /* higher priority than '=' */
+ rc = LY_ENOT;
+ goto cleanup;
+ } else if (!lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
+ /* nested predicate */
+ ++nested_pred;
+ } else if (!lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
+ /* predicate end */
+ --nested_pred;
+ }
+ } while (nested_pred);
+
+ /* try to evaluate the value */
+ LY_CHECK_GOTO(rc = eval_name_test_try_compile_predicate_append(exp, val_start_idx, e_idx - 1, ctx_scnode,
+ set, key, &pred, &pred_len), cleanup);
+
+ ++e_idx;
+ }
+ } else {
+ /* check for predicate "[.=...]" */
+
+ /* learn just where this single predicate ends */
+ e_idx = *tok_idx;
+
+ /* '[' */
+ if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ ++e_idx;
+
+ if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_DOT)) {
+ /* not the node value */
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ ++e_idx;
+
+ if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_EQUAL)) {
+ /* not '=' */
+ rc = LY_ENOT;
+ goto cleanup;
+ }
+ ++e_idx;
+
+ /* value start */
+ val_start_idx = e_idx;
+
+ /* ']' */
+ nested_pred = 1;
+ do {
+ ++e_idx;
+
+ if ((nested_pred == 1) && !lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_OPER_LOG)) {
+ /* higher priority than '=' */
+ rc = LY_ENOT;
+ goto cleanup;
+ } else if (!lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
+ /* nested predicate */
+ ++nested_pred;
+ } else if (!lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
+ /* predicate end */
+ --nested_pred;
+ }
+ } while (nested_pred);
+
+ /* try to evaluate the value */
+ LY_CHECK_GOTO(rc = eval_name_test_try_compile_predicate_append(exp, val_start_idx, e_idx - 1, ctx_scnode, set,
+ ctx_scnode, &pred, &pred_len), cleanup);
+
+ ++e_idx;
+ }
+
+ /* parse the predicate(s) */
+ LY_CHECK_GOTO(rc = ly_path_parse_predicate(set->ctx, ctx_scnode, pred, pred_len, LY_PATH_PREFIX_OPTIONAL,
+ LY_PATH_PRED_SIMPLE, &exp2), cleanup);
+
+ /* compile */
+ rc = ly_path_compile_predicate(set->ctx, set->cur_node ? set->cur_node->schema : NULL, set->cur_mod, ctx_scnode, exp2,
+ &pred_idx, LY_VALUE_JSON, NULL, predicates, pred_type);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* success, the predicate must include all the needed information for hash-based search */
+ *tok_idx = e_idx;
+
+cleanup:
+ ly_temp_log_options(NULL);
+ lyxp_expr_free(set->ctx, exp2);
+ free(pred);
+ return rc;
+}
+
+/**
+ * @brief Search for/check the next schema node that could be the only matching schema node meaning the
+ * data node(s) could be found using a single hash-based search.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] node Next context node to check.
+ * @param[in] name Expected node name.
+ * @param[in] name_len Length of @p name.
+ * @param[in] moveto_mod Expected node module, can be NULL for JSON format with no prefix.
+ * @param[in] root_type XPath root type.
+ * @param[in] format Prefix format.
+ * @param[in,out] found Previously found node, is updated.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if the whole check failed and hashes cannot be used.
+ */
+static LY_ERR
+eval_name_test_with_predicate_get_scnode(const struct ly_ctx *ctx, const struct lyd_node *node, const char *name,
+ uint32_t name_len, const struct lys_module *moveto_mod, enum lyxp_node_type root_type, LY_VALUE_FORMAT format,
+ const struct lysc_node **found)
+{
+ const struct lysc_node *scnode;
+ const struct lys_module *mod;
+ uint32_t idx = 0;
+
+ assert((format == LY_VALUE_JSON) || moveto_mod);
+
+continue_search:
+ scnode = NULL;
+ if (!node) {
+ if ((format == LY_VALUE_JSON) && !moveto_mod) {
+ /* search all modules for a single match */
+ while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ scnode = lys_find_child(NULL, mod, name, name_len, 0, 0);
+ if (scnode) {
+ /* we have found a match */
+ break;
+ }
+ }
+
+ if (!scnode) {
+ /* all modules searched */
+ idx = 0;
+ }
+ } else {
+ /* search in top-level */
+ scnode = lys_find_child(NULL, moveto_mod, name, name_len, 0, 0);
+ }
+ } else if (!*found || (lysc_data_parent(*found) != node->schema)) {
+ if ((format == LY_VALUE_JSON) && !moveto_mod) {
+ /* we must adjust the module to inherit the one from the context node */
+ moveto_mod = node->schema->module;
+ }
+
+ /* search in children, do not repeat the same search */
+ scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ } /* else skip redundant search */
+
+ /* additional context check */
+ if (scnode && (root_type == LYXP_NODE_ROOT_CONFIG) && (scnode->flags & LYS_CONFIG_R)) {
+ scnode = NULL;
+ }
+
+ if (scnode) {
+ if (*found) {
+ /* we found a schema node with the same name but at different level, give up, too complicated
+ * (more hash-based searches would be required, not supported) */
+ return LY_ENOT;
+ } else {
+ /* remember the found schema node and continue to make sure it can be used */
+ *found = scnode;
+ }
+ }
+
+ if (idx) {
+ /* continue searching all the following models */
+ goto continue_search;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Generate message when no matching schema nodes were found for a path segment.
+ *
+ * @param[in] set XPath set.
+ * @param[in] scparent Previous schema parent in the context, if only one.
+ * @param[in] ncname XPath NCName being evaluated.
+ * @param[in] ncname_len Length of @p ncname.
+ * @param[in] expr Whole XPath expression.
+ * @param[in] options XPath options.
+ */
+static void
+eval_name_test_scnode_no_match_msg(struct lyxp_set *set, const struct lyxp_set_scnode *scparent, const char *ncname,
+ uint32_t ncname_len, const char *expr, uint32_t options)
+{
+ const char *format;
+ char *path = NULL, *ppath = NULL;
+
+ path = lysc_path(set->cur_scnode, LYSC_PATH_LOG, NULL, 0);
+ if (scparent) {
+ /* generate path for the parent */
+ if (scparent->type == LYXP_NODE_ELEM) {
+ ppath = lysc_path(scparent->scnode, LYSC_PATH_LOG, NULL, 0);
+ } else if (scparent->type == LYXP_NODE_ROOT) {
+ ppath = strdup("<root>");
+ } else if (scparent->type == LYXP_NODE_ROOT_CONFIG) {
+ ppath = strdup("<config-root>");
+ }
+ }
+ if (ppath) {
+ format = "Schema node \"%.*s\" for parent \"%s\" not found; in expr \"%.*s\" with context node \"%s\".";
+ if (options & LYXP_SCNODE_ERROR) {
+ LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
+ } else {
+ LOGWRN(set->ctx, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
+ }
+ } else {
+ format = "Schema node \"%.*s\" not found; in expr \"%.*s\" with context node \"%s\".";
+ if (options & LYXP_SCNODE_ERROR) {
+ LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
+ } else {
+ LOGWRN(set->ctx, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
+ }
+ }
+ free(path);
+ free(ppath);
+}
+
+/**
+ * @brief Evaluate NameTest and any following Predicates. Logs directly on error.
+ *
+ * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ * [7] NameTest ::= '*' | NCName ':' '*' | QName
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] axis What axis to search on.
+ * @param[in] all_desc Whether to search all the descendants or children only.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when, LY_ENOT for not found schema node)
+ */
+static LY_ERR
+eval_name_test_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_axis axis, ly_bool all_desc,
+ struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ const char *ncname, *ncname_dict = NULL;
+ uint32_t i, ncname_len;
+ const struct lys_module *moveto_mod = NULL;
+ const struct lysc_node *scnode = NULL;
+ struct ly_path_predicate *predicates = NULL;
+ enum ly_path_pred_type pred_type = 0;
+ int scnode_skip_pred = 0;
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+ goto moveto;
+ }
+
+ ncname = &exp->expr[exp->tok_pos[*tok_idx - 1]];
+ ncname_len = exp->tok_len[*tok_idx - 1];
+
+ if ((ncname[0] == '*') && (ncname_len == 1)) {
+ /* all nodes will match */
+ goto moveto;
+ }
+
+ /* parse (and skip) module name */
+ rc = moveto_resolve_model(&ncname, &ncname_len, set, NULL, &moveto_mod);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if ((ncname[0] == '*') && (ncname_len == 1)) {
+ /* all nodes from the module will match */
+ goto moveto;
+ }
+
+ if (((set->format == LY_VALUE_JSON) || moveto_mod) && (axis == LYXP_AXIS_CHILD) && !all_desc &&
+ (set->type == LYXP_SET_NODE_SET)) {
+ /* find the matching schema node in some parent in the context */
+ for (i = 0; i < set->used; ++i) {
+ if (eval_name_test_with_predicate_get_scnode(set->ctx, set->val.nodes[i].node, ncname, ncname_len,
+ moveto_mod, set->root_type, set->format, &scnode)) {
+ /* check failed */
+ scnode = NULL;
+ break;
+ }
+ }
+
+ if (scnode && (scnode->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
+ /* try to create the predicates */
+ if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set, &predicates, &pred_type)) {
+ /* hashes cannot be used */
+ scnode = NULL;
+ }
+ }
+ }
+
+ if (!scnode) {
+ /* we are not able to match based on a schema node and not all the modules match ("*"),
+ * use dictionary for efficient comparison */
+ LY_CHECK_GOTO(rc = lydict_insert(set->ctx, ncname, ncname_len, &ncname_dict), cleanup);
+ }
+
+moveto:
+ /* move to the attribute(s), data node(s), or schema node(s) */
+ if (axis == LYXP_AXIS_ATTRIBUTE) {
+ if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ if (all_desc) {
+ rc = moveto_attr_alldesc(set, moveto_mod, ncname_dict, options);
+ } else {
+ rc = moveto_attr(set, moveto_mod, ncname_dict, options);
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ } else {
+ if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
+ const struct lyxp_set_scnode *scparent = NULL;
+ ly_bool found = 0;
+
+ /* remember parent if there is only one, to print in the warning */
+ for (i = 0; i < set->used; ++i) {
+ if (set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX) {
+ if (!scparent) {
+ /* remember the context node */
+ scparent = &set->val.scnodes[i];
+ } else {
+ /* several context nodes, no reasonable error possible */
+ scparent = NULL;
+ break;
+ }
+ }
+ }
+
+ if (all_desc && (axis == LYXP_AXIS_CHILD)) {
+ /* efficient evaluation that does not add all the descendants into the set */
+ rc = moveto_scnode_alldesc_child(set, moveto_mod, ncname_dict, options);
+ } else {
+ if (all_desc) {
+ /* "//" == "/descendant-or-self::node()/" */
+ rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ rc = moveto_scnode(set, moveto_mod, ncname_dict, axis, options);
+ }
+ 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 (!found) {
+ /* generate message */
+ eval_name_test_scnode_no_match_msg(set, scparent, ncname, ncname_len, exp->expr, options);
+
+ if (options & LYXP_SCNODE_ERROR) {
+ /* error */
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* skip the predicates and the rest of this path to not generate invalid warnings */
+ rc = LY_ENOT;
+ scnode_skip_pred = 1;
+ }
+ } else {
+ if (all_desc && (axis == LYXP_AXIS_CHILD)) {
+ /* efficient evaluation */
+ rc = moveto_node_alldesc_child(set, moveto_mod, ncname_dict, options);
+ } else if (scnode && (axis == LYXP_AXIS_CHILD)) {
+ /* we can find the child nodes using hashes */
+ rc = moveto_node_hash_child(set, scnode, predicates, options);
+ } else {
+ if (all_desc) {
+ /* "//" == "/descendant-or-self::node()/" */
+ rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ rc = moveto_node(set, moveto_mod, ncname_dict, axis, options);
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+ if (scnode_skip_pred) {
+ /* skip predicates */
+ options |= LYXP_SKIP_EXPR;
+ }
+
+ /* Predicate* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+ r = eval_predicate(exp, tok_idx, set, options, axis);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+cleanup:
+ if (scnode_skip_pred) {
+ /* restore options */
+ options &= ~LYXP_SKIP_EXPR;
+ }
+ if (!(options & LYXP_SKIP_EXPR)) {
+ lydict_remove(set->ctx, ncname_dict);
+ ly_path_predicates_free(set->ctx, pred_type, predicates);
+ }
+ return rc;
+}
+
+/**
+ * @brief Evaluate NodeType and any following Predicates. Logs directly on error.
+ *
+ * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ * [8] NodeType ::= 'text' | 'node'
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] axis Axis to search on.
+ * @param[in] all_desc Whether to search all the descendants or axis only.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_node_type_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_axis axis, ly_bool all_desc,
+ struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+
+ (void)all_desc;
+
+ if (!(options & LYXP_SKIP_EXPR)) {
+ assert(exp->tok_len[*tok_idx] == 4);
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "node", 4)) {
+ rc = xpath_pi_node(set, axis, options);
+ } else {
+ assert(!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "text", 4));
+ rc = xpath_pi_text(set, axis, options);
+ }
+ LY_CHECK_RET(rc);
+ }
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* '(' */
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* ')' */
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* Predicate* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+ rc = eval_predicate(exp, tok_idx, set, options, axis);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate RelativeLocationPath. Logs directly on error.
+ *
+ * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+ * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] all_desc Whether to search all the descendants or children only.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (YL_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_relative_location_path(const struct lyxp_expr *exp, uint32_t *tok_idx, ly_bool all_desc, struct lyxp_set *set,
+ uint32_t options)
+{
+ LY_ERR rc = LY_SUCCESS;
+ enum lyxp_axis axis;
+ int scnode_skip_path = 0;
+
+ goto step;
+ do {
+ /* evaluate '/' or '//' */
+ if (exp->tok_len[*tok_idx] == 1) {
+ all_desc = 0;
+ } else {
+ assert(exp->tok_len[*tok_idx] == 2);
+ all_desc = 1;
+ }
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+step:
+ /* AxisSpecifier */
+ if (exp->tokens[*tok_idx] == LYXP_TOKEN_AXISNAME) {
+ axis = str2axis(exp->expr + exp->tok_pos[*tok_idx], exp->tok_len[*tok_idx]);
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_DCOLON);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+ } else if (exp->tokens[*tok_idx] == LYXP_TOKEN_AT) {
+ axis = LYXP_AXIS_ATTRIBUTE;
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+ } else {
+ /* default */
+ axis = LYXP_AXIS_CHILD;
+ }
+
+ /* NodeTest Predicate* */
+ switch (exp->tokens[*tok_idx]) {
+ case LYXP_TOKEN_DOT:
+ /* evaluate '.' */
+ if (!(options & LYXP_SKIP_EXPR)) {
+ if (((options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_SCNODE_SET)) ||
+ (!(options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_NODE_SET))) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (all_desc) {
+ rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ rc = xpath_pi_node(set, LYXP_AXIS_SELF, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+ break;
+
+ case LYXP_TOKEN_DDOT:
+ /* evaluate '..' */
+ if (!(options & LYXP_SKIP_EXPR)) {
+ if (((options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_SCNODE_SET)) ||
+ (!(options & LYXP_SCNODE_ALL) && (set->type != LYXP_SET_NODE_SET))) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (all_desc) {
+ rc = xpath_pi_node(set, LYXP_AXIS_DESCENDANT_OR_SELF, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ rc = xpath_pi_node(set, LYXP_AXIS_PARENT, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+ break;
+
+ case LYXP_TOKEN_NAMETEST:
+ /* evaluate NameTest Predicate* */
+ rc = eval_name_test_with_predicate(exp, tok_idx, axis, all_desc, set, options);
+ if (rc == LY_ENOT) {
+ assert(options & LYXP_SCNODE_ALL);
+ /* skip the rest of this path */
+ rc = LY_SUCCESS;
+ scnode_skip_path = 1;
+ options |= LYXP_SKIP_EXPR;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+ break;
+
+ case LYXP_TOKEN_NODETYPE:
+ /* evaluate NodeType Predicate* */
+ rc = eval_node_type_with_predicate(exp, tok_idx, axis, all_desc, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ break;
+
+ default:
+ LOGINT(set->ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+ } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
+
+cleanup:
+ if (scnode_skip_path) {
+ options &= ~LYXP_SKIP_EXPR;
+ }
+ return rc;
+}
+
+/**
+ * @brief Evaluate AbsoluteLocationPath. Logs directly on error.
+ *
+ * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_absolute_location_path(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
+{
+ ly_bool all_desc;
+
+ if (!(options & LYXP_SKIP_EXPR)) {
+ /* no matter what tokens follow, we need to be at the root */
+ LY_CHECK_RET(moveto_root(set, options));
+ }
+
+ /* '/' RelativeLocationPath? */
+ if (exp->tok_len[*tok_idx] == 1) {
+ /* evaluate '/' - deferred */
+ all_desc = 0;
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
+ return LY_SUCCESS;
+ }
+ switch (exp->tokens[*tok_idx]) {
+ case LYXP_TOKEN_DOT:
+ case LYXP_TOKEN_DDOT:
+ case LYXP_TOKEN_AXISNAME:
+ case LYXP_TOKEN_AT:
+ case LYXP_TOKEN_NAMETEST:
+ case LYXP_TOKEN_NODETYPE:
+ LY_CHECK_RET(eval_relative_location_path(exp, tok_idx, all_desc, set, options));
+ break;
+ default:
+ break;
+ }
+
+ } else {
+ /* '//' RelativeLocationPath */
+ /* evaluate '//' - deferred so as not to waste memory by remembering all the nodes */
+ all_desc = 1;
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ LY_CHECK_RET(eval_relative_location_path(exp, tok_idx, all_desc, set, options));
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate FunctionCall. Logs directly on error.
+ *
+ * [11] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_function_call(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+
+ LY_ERR (*xpath_func)(struct lyxp_set **, uint32_t, struct lyxp_set *, uint32_t) = NULL;
+ uint32_t arg_count = 0, i;
+ struct lyxp_set **args = NULL, **args_aux;
+
+ if (!(options & LYXP_SKIP_EXPR)) {
+ /* FunctionName */
+ switch (exp->tok_len[*tok_idx]) {
+ case 3:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
+ xpath_func = &xpath_not;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
+ xpath_func = &xpath_sum;
+ }
+ break;
+ case 4:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
+ xpath_func = &xpath_lang;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
+ xpath_func = &xpath_last;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
+ xpath_func = &xpath_name;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
+ xpath_func = &xpath_true;
+ }
+ break;
+ case 5:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
+ xpath_func = &xpath_count;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
+ xpath_func = &xpath_false;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
+ xpath_func = &xpath_floor;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
+ xpath_func = &xpath_round;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
+ xpath_func = &xpath_deref;
+ }
+ break;
+ case 6:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
+ xpath_func = &xpath_concat;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
+ xpath_func = &xpath_number;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
+ xpath_func = &xpath_string;
+ }
+ break;
+ case 7:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
+ xpath_func = &xpath_boolean;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
+ xpath_func = &xpath_ceiling;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
+ xpath_func = &xpath_current;
+ }
+ break;
+ case 8:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
+ xpath_func = &xpath_contains;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
+ xpath_func = &xpath_position;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
+ xpath_func = &xpath_re_match;
+ }
+ break;
+ case 9:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
+ xpath_func = &xpath_substring;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
+ xpath_func = &xpath_translate;
+ }
+ break;
+ case 10:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
+ xpath_func = &xpath_local_name;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
+ xpath_func = &xpath_enum_value;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
+ xpath_func = &xpath_bit_is_set;
+ }
+ break;
+ case 11:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
+ xpath_func = &xpath_starts_with;
+ }
+ break;
+ case 12:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
+ xpath_func = &xpath_derived_from;
+ }
+ break;
+ case 13:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
+ xpath_func = &xpath_namespace_uri;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
+ xpath_func = &xpath_string_length;
+ }
+ break;
+ case 15:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
+ xpath_func = &xpath_normalize_space;
+ } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
+ xpath_func = &xpath_substring_after;
+ }
+ break;
+ case 16:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
+ xpath_func = &xpath_substring_before;
+ }
+ break;
+ case 20:
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
+ xpath_func = &xpath_derived_from_or_self;
+ }
+ break;
+ }
+
+ if (!xpath_func) {
+ LOGVAL(set->ctx, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EVALID;
+ }
+ }
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* '(' */
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* ( Expr ( ',' Expr )* )? */
+ if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
+ if (!(options & LYXP_SKIP_EXPR)) {
+ args = malloc(sizeof *args);
+ LY_CHECK_ERR_GOTO(!args, LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
+ arg_count = 1;
+ args[0] = set_copy(set);
+ if (!args[0]) {
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+
+ rc = eval_expr_select(exp, tok_idx, 0, args[0], options);
+ LY_CHECK_GOTO(rc, cleanup);
+ } else {
+ rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (!(options & LYXP_SKIP_EXPR)) {
+ ++arg_count;
+ args_aux = realloc(args, arg_count * sizeof *args);
+ LY_CHECK_ERR_GOTO(!args_aux, arg_count--; LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
+ args = args_aux;
+ args[arg_count - 1] = set_copy(set);
+ if (!args[arg_count - 1]) {
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+
+ rc = eval_expr_select(exp, tok_idx, 0, args[arg_count - 1], options);
+ LY_CHECK_GOTO(rc, cleanup);
+ } else {
+ rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+ /* ')' */
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (!(options & LYXP_SKIP_EXPR)) {
+ /* evaluate function */
+ rc = xpath_func(args, arg_count, set, options);
+
+ if (options & LYXP_SCNODE_ALL) {
+ /* merge all nodes from arg evaluations */
+ for (i = 0; i < arg_count; ++i) {
+ set_scnode_clear_ctx(args[i], LYXP_SET_SCNODE_ATOM_NODE);
+ lyxp_set_scnode_merge(set, args[i]);
+ }
+ }
+ } else {
+ rc = LY_SUCCESS;
+ }
+
+cleanup:
+ for (i = 0; i < arg_count; ++i) {
+ lyxp_set_free(args[i]);
+ }
+ free(args);
+ return rc;
+}
+
+/**
+ * @brief Evaluate Number. Logs directly on error.
+ *
+ * @param[in] ctx Context for errors.
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in,out] set Context and result set. On NULL the rule is only parsed.
+ * @return LY_ERR
+ */
+static LY_ERR
+eval_number(struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set)
+{
+ long double num;
+ char *endptr;
+
+ if (set) {
+ errno = 0;
+ num = strtold(&exp->expr[exp->tok_pos[*tok_idx]], &endptr);
+ if (errno) {
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
+ LOGVAL(ctx, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double (%s).",
+ exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]], strerror(errno));
+ return LY_EVALID;
+ } else if (endptr - &exp->expr[exp->tok_pos[*tok_idx]] != exp->tok_len[*tok_idx]) {
+ LOGVAL(ctx, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
+ LOGVAL(ctx, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double.",
+ exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EVALID;
+ }
+
+ set_fill_number(set, num);
+ }
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var)
+{
+ LY_ERR ret = LY_ENOTFOUND;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(vars && name);
+
+ name_len = name_len ? name_len : strlen(name);
+
+ LY_ARRAY_FOR(vars, u) {
+ if (!strncmp(vars[u].name, name, name_len)) {
+ ret = LY_SUCCESS;
+ break;
+ }
+ }
+
+ if (var && !ret) {
+ *var = &vars[u];
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Evaluate VariableReference.
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+eval_variable_reference(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR ret;
+ const char *name;
+ struct lyxp_var *var;
+ const struct lyxp_var *vars;
+ struct lyxp_expr *tokens = NULL;
+ uint32_t token_index, name_len;
+
+ vars = set->vars;
+
+ /* find out the name and value of the variable */
+ name = &exp->expr[exp->tok_pos[*tok_idx]];
+ name_len = exp->tok_len[*tok_idx];
+ ret = lyxp_vars_find((struct lyxp_var *)vars, name, name_len, &var);
+ LY_CHECK_ERR_RET(ret, LOGERR(set->ctx, ret, "XPath variable \"%.*s\" not defined.", (int)name_len, name), ret);
+
+ /* parse value */
+ ret = lyxp_expr_parse(set->ctx, var->value, 0, 1, &tokens);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate value */
+ token_index = 0;
+ ret = eval_expr_select(tokens, &token_index, 0, set, options);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ lyxp_expr_free(set->ctx, tokens);
+
+ return ret;
+}
+
+/**
+ * @brief Evaluate PathExpr. Logs directly on error.
+ *
+ * [12] PathExpr ::= LocationPath | PrimaryExpr Predicate*
+ * | PrimaryExpr Predicate* '/' RelativeLocationPath
+ * | PrimaryExpr Predicate* '//' RelativeLocationPath
+ * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+ * [10] PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_path_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_set *set, uint32_t options)
+{
+ ly_bool all_desc;
+ LY_ERR rc;
+
+ switch (exp->tokens[*tok_idx]) {
+ case LYXP_TOKEN_PAR1:
+ /* '(' Expr ')' */
+
+ /* '(' */
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* Expr */
+ rc = eval_expr_select(exp, tok_idx, 0, set, options);
+ LY_CHECK_RET(rc);
+
+ /* ')' */
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ goto predicate;
+
+ case LYXP_TOKEN_DOT:
+ case LYXP_TOKEN_DDOT:
+ case LYXP_TOKEN_AXISNAME:
+ case LYXP_TOKEN_AT:
+ case LYXP_TOKEN_NAMETEST:
+ case LYXP_TOKEN_NODETYPE:
+ /* RelativeLocationPath */
+ rc = eval_relative_location_path(exp, tok_idx, 0, set, options);
+ LY_CHECK_RET(rc);
+ break;
+
+ case LYXP_TOKEN_VARREF:
+ /* VariableReference */
+ rc = eval_variable_reference(exp, tok_idx, set, options);
+ LY_CHECK_RET(rc);
+ ++(*tok_idx);
+
+ goto predicate;
+
+ case LYXP_TOKEN_FUNCNAME:
+ /* FunctionCall */
+ rc = eval_function_call(exp, tok_idx, set, options);
+ LY_CHECK_RET(rc);
+
+ goto predicate;
+
+ case LYXP_TOKEN_OPER_PATH:
+ case LYXP_TOKEN_OPER_RPATH:
+ /* AbsoluteLocationPath */
+ rc = eval_absolute_location_path(exp, tok_idx, set, options);
+ LY_CHECK_RET(rc);
+ break;
+
+ case LYXP_TOKEN_LITERAL:
+ /* Literal */
+ if ((options & LYXP_SKIP_EXPR) || (options & LYXP_SCNODE_ALL)) {
+ if (!(options & LYXP_SKIP_EXPR)) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ }
+ eval_literal(exp, tok_idx, NULL);
+ } else {
+ eval_literal(exp, tok_idx, set);
+ }
+
+ goto predicate;
+
+ case LYXP_TOKEN_NUMBER:
+ /* Number */
+ if ((options & LYXP_SKIP_EXPR) || (options & LYXP_SCNODE_ALL)) {
+ if (!(options & LYXP_SKIP_EXPR)) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ }
+ rc = eval_number(NULL, exp, tok_idx, NULL);
+ } else {
+ rc = eval_number(set->ctx, exp, tok_idx, set);
+ }
+ LY_CHECK_RET(rc);
+
+ goto predicate;
+
+ default:
+ LOGVAL(set->ctx, LY_VCODE_XP_INTOK, lyxp_token2str(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+
+predicate:
+ /* Predicate* */
+ while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+ rc = eval_predicate(exp, tok_idx, set, options, LYXP_AXIS_CHILD);
+ LY_CHECK_RET(rc);
+ }
+
+ /* ('/' or '//') RelativeLocationPath */
+ if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
+
+ /* evaluate '/' or '//' */
+ if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
+ all_desc = 0;
+ } else {
+ all_desc = 1;
+ }
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
+ LY_CHECK_RET(rc);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate UnionExpr. Logs directly on error.
+ *
+ * [20] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxp_set orig_set, set2;
+ uint32_t i;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* ('|' PathExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_UNI);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval */
+ if (options & LYXP_SCNODE_ALL) {
+ lyxp_set_scnode_merge(set, &set2);
+ } else {
+ rc = moveto_union(set, &set2);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Evaluate UnaryExpr. Logs directly on error.
+ *
+ * [19] UnaryExpr ::= UnionExpr | '-' UnaryExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_unary_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+ uint32_t this_op, i;
+
+ assert(repeat);
+
+ /* ('-')+ */
+ this_op = *tok_idx;
+ for (i = 0; i < repeat; ++i) {
+ assert(!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) && (exp->expr[exp->tok_pos[*tok_idx]] == '-'));
+
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+ }
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNARY, set, options);
+ LY_CHECK_RET(rc);
+
+ if (!(options & LYXP_SKIP_EXPR) && (repeat % 2)) {
+ if (options & LYXP_SCNODE_ALL) {
+ warn_operands(set->ctx, set, NULL, 1, exp->expr, exp->tok_pos[this_op]);
+ } else {
+ rc = moveto_op_math(set, NULL, &exp->expr[exp->tok_pos[this_op]]);
+ LY_CHECK_RET(rc);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate MultiplicativeExpr. Logs directly on error.
+ *
+ * [18] MultiplicativeExpr ::= UnaryExpr
+ * | MultiplicativeExpr '*' UnaryExpr
+ * | MultiplicativeExpr 'div' UnaryExpr
+ * | MultiplicativeExpr 'mod' UnaryExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_multiplicative_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set,
+ uint32_t options)
+{
+ LY_ERR rc;
+ uint32_t i, this_op;
+ struct lyxp_set orig_set, set2;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* ('*' / 'div' / 'mod' UnaryExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ this_op = *tok_idx;
+
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval */
+ if (options & LYXP_SCNODE_ALL) {
+ warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
+ lyxp_set_scnode_merge(set, &set2);
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ } else {
+ rc = moveto_op_math(set, &set2, &exp->expr[exp->tok_pos[this_op]]);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Evaluate AdditiveExpr. Logs directly on error.
+ *
+ * [17] AdditiveExpr ::= MultiplicativeExpr
+ * | AdditiveExpr '+' MultiplicativeExpr
+ * | AdditiveExpr '-' MultiplicativeExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_additive_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+ uint32_t i, this_op;
+ struct lyxp_set orig_set, set2;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* ('+' / '-' MultiplicativeExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ this_op = *tok_idx;
+
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval */
+ if (options & LYXP_SCNODE_ALL) {
+ warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
+ lyxp_set_scnode_merge(set, &set2);
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ } else {
+ rc = moveto_op_math(set, &set2, &exp->expr[exp->tok_pos[this_op]]);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Evaluate RelationalExpr. Logs directly on error.
+ *
+ * [16] RelationalExpr ::= AdditiveExpr
+ * | RelationalExpr '<' AdditiveExpr
+ * | RelationalExpr '>' AdditiveExpr
+ * | RelationalExpr '<=' AdditiveExpr
+ * | RelationalExpr '>=' AdditiveExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_relational_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+ uint32_t i, this_op;
+ struct lyxp_set orig_set, set2;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ this_op = *tok_idx;
+
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_COMP);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval */
+ if (options & LYXP_SCNODE_ALL) {
+ warn_operands(set->ctx, set, &set2, 1, exp->expr, exp->tok_pos[this_op - 1]);
+ lyxp_set_scnode_merge(set, &set2);
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ } else {
+ ly_bool result;
+
+ rc = moveto_op_comp(set, &set2, &exp->expr[exp->tok_pos[this_op]], &result);
+ LY_CHECK_GOTO(rc, cleanup);
+ set_fill_boolean(set, result);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Evaluate EqualityExpr. Logs directly on error.
+ *
+ * [15] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
+ * | EqualityExpr '!=' RelationalExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_equality_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+ uint32_t i, this_op;
+ struct lyxp_set orig_set, set2;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* ('=' / '!=' RelationalExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ this_op = *tok_idx;
+
+ assert((exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL) || (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_NEQUAL));
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ if (options & LYXP_SKIP_EXPR) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval */
+ if (options & LYXP_SCNODE_ALL) {
+ warn_operands(set->ctx, set, &set2, 0, exp->expr, exp->tok_pos[this_op - 1]);
+ warn_equality_value(exp, set, *tok_idx - 1, this_op - 1, *tok_idx - 1);
+ warn_equality_value(exp, &set2, this_op - 1, this_op - 1, *tok_idx - 1);
+ lyxp_set_scnode_merge(set, &set2);
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ } else {
+ ly_bool result;
+
+ rc = moveto_op_comp(set, &set2, &exp->expr[exp->tok_pos[this_op]], &result);
+ LY_CHECK_GOTO(rc, cleanup);
+ set_fill_boolean(set, result);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Evaluate AndExpr. Logs directly on error.
+ *
+ * [14] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+ struct lyxp_set orig_set, set2;
+ uint32_t i;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* cast to boolean, we know that will be the final result */
+ if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ }
+
+ /* ('and' EqualityExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, ((options & LYXP_SKIP_EXPR) || !set->val.bln ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* lazy evaluation */
+ if ((options & LYXP_SKIP_EXPR) || ((set->type == LYXP_SET_BOOLEAN) && !set->val.bln)) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options | LYXP_SKIP_EXPR);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval - just get boolean value actually */
+ if (set->type == LYXP_SET_SCNODE_SET) {
+ set_scnode_clear_ctx(&set2, LYXP_SET_SCNODE_ATOM_NODE);
+ lyxp_set_scnode_merge(set, &set2);
+ } else {
+ lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
+ set_fill_set(set, &set2);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Evaluate OrExpr. Logs directly on error.
+ *
+ * [13] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] repeat How many times this expression is repeated.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR rc;
+ struct lyxp_set orig_set, set2;
+ uint32_t i;
+
+ assert(repeat);
+
+ set_init(&orig_set, set);
+ set_init(&set2, set);
+
+ set_fill_set(&orig_set, set);
+
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* cast to boolean, we know that will be the final result */
+ if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ }
+
+ /* ('or' AndExpr)* */
+ for (i = 0; i < repeat; ++i) {
+ assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
+ LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, ((options & LYXP_SKIP_EXPR) || set->val.bln ? "skipped" : "parsed"),
+ lyxp_token2str(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+ ++(*tok_idx);
+
+ /* lazy evaluation */
+ if ((options & LYXP_SKIP_EXPR) || ((set->type == LYXP_SET_BOOLEAN) && set->val.bln)) {
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options | LYXP_SKIP_EXPR);
+ LY_CHECK_GOTO(rc, cleanup);
+ continue;
+ }
+
+ set_fill_set(&set2, &orig_set);
+ /* expr_type cound have been LYXP_EXPR_NONE in all these later calls (except for the first one),
+ * but it does not matter */
+ rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, &set2, options);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* eval - just get boolean value actually */
+ if (set->type == LYXP_SET_SCNODE_SET) {
+ set_scnode_clear_ctx(&set2, LYXP_SET_SCNODE_ATOM_NODE);
+ lyxp_set_scnode_merge(set, &set2);
+ } else {
+ lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
+ set_fill_set(set, &set2);
+ }
+ }
+
+cleanup:
+ lyxp_set_free_content(&orig_set);
+ lyxp_set_free_content(&set2);
+ return rc;
+}
+
+/**
+ * @brief Decide what expression is at the pointer @p tok_idx and evaluate it accordingly.
+ *
+ * @param[in] exp Parsed XPath expression.
+ * @param[in] tok_idx Position in the expression @p exp.
+ * @param[in] etype Expression type to evaluate.
+ * @param[in,out] set Context and result set.
+ * @param[in] options XPath options.
+ * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
+ */
+static LY_ERR
+eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype, struct lyxp_set *set,
+ uint32_t options)
+{
+ uint32_t i, count;
+ enum lyxp_expr_type next_etype;
+ LY_ERR rc;
+
+ /* process operator repeats */
+ if (!exp->repeat[*tok_idx]) {
+ next_etype = LYXP_EXPR_NONE;
+ } else {
+ /* find etype repeat */
+ for (i = 0; exp->repeat[*tok_idx][i] > etype; ++i) {}
+
+ /* select one-priority lower because etype expression called us */
+ if (i) {
+ next_etype = exp->repeat[*tok_idx][i - 1];
+ /* count repeats for that expression */
+ for (count = 0; i && exp->repeat[*tok_idx][i - 1] == next_etype; ++count, --i) {}
+ } else {
+ next_etype = LYXP_EXPR_NONE;
+ }
+ }
+
+ /* decide what expression are we parsing based on the repeat */
+ switch (next_etype) {
+ case LYXP_EXPR_OR:
+ rc = eval_or_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_AND:
+ rc = eval_and_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_EQUALITY:
+ rc = eval_equality_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_RELATIONAL:
+ rc = eval_relational_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_ADDITIVE:
+ rc = eval_additive_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_MULTIPLICATIVE:
+ rc = eval_multiplicative_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_UNARY:
+ rc = eval_unary_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_UNION:
+ rc = eval_union_expr(exp, tok_idx, count, set, options);
+ break;
+ case LYXP_EXPR_NONE:
+ rc = eval_path_expr(exp, tok_idx, set, options);
+ break;
+ default:
+ LOGINT_RET(set->ctx);
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Get root type.
+ *
+ * @param[in] ctx_node Context node.
+ * @param[in] ctx_scnode Schema context node.
+ * @param[in] options XPath options.
+ * @return Root type.
+ */
+static enum lyxp_node_type
+lyxp_get_root_type(const struct lyd_node *ctx_node, const struct lysc_node *ctx_scnode, uint32_t options)
+{
+ const struct lysc_node *op;
+
+ /* explicit */
+ if (options & LYXP_ACCESS_TREE_ALL) {
+ return LYXP_NODE_ROOT;
+ } else if (options & LYXP_ACCESS_TREE_CONFIG) {
+ return LYXP_NODE_ROOT_CONFIG;
+ }
+
+ if (options & LYXP_SCNODE_ALL) {
+ /* schema */
+ for (op = ctx_scnode; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
+
+ if (op || (options & LYXP_SCNODE)) {
+ /* general root that can access everything */
+ return LYXP_NODE_ROOT;
+ } else if (!ctx_scnode || (ctx_scnode->flags & LYS_CONFIG_W)) {
+ /* root context node can access only config data (because we said so, it is unspecified) */
+ return LYXP_NODE_ROOT_CONFIG;
+ }
+ return LYXP_NODE_ROOT;
+ }
+
+ /* data */
+ op = ctx_node ? ctx_node->schema : NULL;
+ for ( ; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
+
+ if (op || !(options & LYXP_SCHEMA)) {
+ /* general root that can access everything */
+ return LYXP_NODE_ROOT;
+ } else if (!ctx_node || !ctx_node->schema || (ctx_node->schema->flags & LYS_CONFIG_W)) {
+ /* root context node can access only config data (because we said so, it is unspecified) */
+ return LYXP_NODE_ROOT_CONFIG;
+ }
+ return LYXP_NODE_ROOT;
+}
+
+LY_ERR
+lyxp_eval(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct lys_module *cur_mod,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lyd_node *cur_node, const struct lyd_node *ctx_node,
+ const struct lyd_node *tree, const struct lyxp_var *vars, struct lyxp_set *set, uint32_t options)
+{
+ uint32_t tok_idx = 0;
+ LY_ERR rc;
+
+ LY_CHECK_ARG_RET(ctx, ctx, exp, set, LY_EINVAL);
+ if (!cur_mod && ((format == LY_VALUE_SCHEMA) || (format == LY_VALUE_SCHEMA_RESOLVED))) {
+ LOGERR(ctx, LY_EINVAL, "Current module must be set if schema format is used.");
+ return LY_EINVAL;
+ }
+
+ if (tree) {
+ /* adjust the pointer to be the first top-level sibling */
+ while (tree->parent) {
+ tree = lyd_parent(tree);
+ }
+ tree = lyd_first_sibling(tree);
+
+ if (lysc_data_parent(tree->schema)) {
+ /* unable to evaluate absolute paths */
+ LOGERR(ctx, LY_EINVAL, "Data node \"%s\" has no parent but is not instance of a top-level schema node.",
+ LYD_NAME(tree));
+ return LY_EINVAL;
+ }
+ }
+
+ /* prepare set for evaluation */
+ memset(set, 0, sizeof *set);
+ set->type = LYXP_SET_NODE_SET;
+ set->root_type = lyxp_get_root_type(ctx_node, NULL, options);
+ set_insert_node(set, (struct lyd_node *)ctx_node, 0, ctx_node ? LYXP_NODE_ELEM : set->root_type, 0);
+
+ set->ctx = (struct ly_ctx *)ctx;
+ set->cur_node = cur_node;
+ for (set->context_op = cur_node ? cur_node->schema : NULL;
+ set->context_op && !(set->context_op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF));
+ set->context_op = set->context_op->parent) {}
+ set->tree = tree;
+ set->cur_mod = cur_mod;
+ set->format = format;
+ set->prefix_data = prefix_data;
+ set->vars = vars;
+
+ LOG_LOCSET(NULL, set->cur_node, NULL, NULL);
+
+ /* evaluate */
+ rc = eval_expr_select(exp, &tok_idx, 0, set, options);
+ if (rc != LY_SUCCESS) {
+ lyxp_set_free_content(set);
+ }
+
+ if (set->cur_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ return rc;
+}
+
+#if 0
+
+/* full xml printing of set elements, not used currently */
+
+void
+lyxp_set_print_xml(FILE *f, struct lyxp_set *set)
+{
+ uint32_t i;
+ char *str_num;
+ struct lyout out;
+
+ memset(&out, 0, sizeof out);
+
+ out.type = LYOUT_STREAM;
+ out.method.f = f;
+
+ switch (set->type) {
+ case LYXP_SET_EMPTY:
+ ly_print_(&out, "Empty XPath set\n\n");
+ break;
+ case LYXP_SET_BOOLEAN:
+ ly_print_(&out, "Boolean XPath set:\n");
+ ly_print_(&out, "%s\n\n", set->value.bool ? "true" : "false");
+ break;
+ case LYXP_SET_STRING:
+ ly_print_(&out, "String XPath set:\n");
+ ly_print_(&out, "\"%s\"\n\n", set->value.str);
+ break;
+ case LYXP_SET_NUMBER:
+ ly_print_(&out, "Number XPath set:\n");
+
+ if (isnan(set->value.num)) {
+ str_num = strdup("NaN");
+ } else if ((set->value.num == 0) || (set->value.num == -0.0f)) {
+ str_num = strdup("0");
+ } else if (isinf(set->value.num) && !signbit(set->value.num)) {
+ str_num = strdup("Infinity");
+ } else if (isinf(set->value.num) && signbit(set->value.num)) {
+ str_num = strdup("-Infinity");
+ } else if ((long long)set->value.num == set->value.num) {
+ if (asprintf(&str_num, "%lld", (long long)set->value.num) == -1) {
+ str_num = NULL;
+ }
+ } else {
+ if (asprintf(&str_num, "%03.1Lf", set->value.num) == -1) {
+ str_num = NULL;
+ }
+ }
+ if (!str_num) {
+ LOGMEM;
+ return;
+ }
+ ly_print_(&out, "%s\n\n", str_num);
+ free(str_num);
+ break;
+ case LYXP_SET_NODE_SET:
+ ly_print_(&out, "Node XPath set:\n");
+
+ for (i = 0; i < set->used; ++i) {
+ ly_print_(&out, "%d. ", i + 1);
+ switch (set->node_type[i]) {
+ case LYXP_NODE_ROOT_ALL:
+ ly_print_(&out, "ROOT all\n\n");
+ break;
+ case LYXP_NODE_ROOT_CONFIG:
+ ly_print_(&out, "ROOT config\n\n");
+ break;
+ case LYXP_NODE_ROOT_STATE:
+ ly_print_(&out, "ROOT state\n\n");
+ break;
+ case LYXP_NODE_ROOT_NOTIF:
+ ly_print_(&out, "ROOT notification \"%s\"\n\n", set->value.nodes[i]->schema->name);
+ break;
+ case LYXP_NODE_ROOT_RPC:
+ ly_print_(&out, "ROOT rpc \"%s\"\n\n", set->value.nodes[i]->schema->name);
+ break;
+ case LYXP_NODE_ROOT_OUTPUT:
+ ly_print_(&out, "ROOT output \"%s\"\n\n", set->value.nodes[i]->schema->name);
+ break;
+ case LYXP_NODE_ELEM:
+ ly_print_(&out, "ELEM \"%s\"\n", set->value.nodes[i]->schema->name);
+ xml_print_node(&out, 1, set->value.nodes[i], 1, LYP_FORMAT);
+ ly_print_(&out, "\n");
+ break;
+ case LYXP_NODE_TEXT:
+ ly_print_(&out, "TEXT \"%s\"\n\n", ((struct lyd_node_leaf_list *)set->value.nodes[i])->value_str);
+ break;
+ case LYXP_NODE_ATTR:
+ ly_print_(&out, "ATTR \"%s\" = \"%s\"\n\n", set->value.attrs[i]->name, set->value.attrs[i]->value);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+#endif
+
+LY_ERR
+lyxp_set_cast(struct lyxp_set *set, enum lyxp_set_type target)
+{
+ long double num;
+ char *str;
+ LY_ERR rc;
+
+ if (!set || (set->type == target)) {
+ return LY_SUCCESS;
+ }
+
+ /* it's not possible to convert anything into a node set */
+ assert(target != LYXP_SET_NODE_SET);
+
+ if (set->type == LYXP_SET_SCNODE_SET) {
+ lyxp_set_free_content(set);
+ return LY_EINVAL;
+ }
+
+ /* to STRING */
+ if ((target == LYXP_SET_STRING) || ((target == LYXP_SET_NUMBER) && (set->type == LYXP_SET_NODE_SET))) {
+ switch (set->type) {
+ case LYXP_SET_NUMBER:
+ if (isnan(set->val.num)) {
+ set->val.str = strdup("NaN");
+ LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
+ } else if ((set->val.num == 0) || (set->val.num == -0.0f)) {
+ set->val.str = strdup("0");
+ LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
+ } else if (isinf(set->val.num) && !signbit(set->val.num)) {
+ set->val.str = strdup("Infinity");
+ LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
+ } else if (isinf(set->val.num) && signbit(set->val.num)) {
+ set->val.str = strdup("-Infinity");
+ LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), -1);
+ } else if ((long long)set->val.num == set->val.num) {
+ if (asprintf(&str, "%lld", (long long)set->val.num) == -1) {
+ LOGMEM_RET(set->ctx);
+ }
+ set->val.str = str;
+ } else {
+ if (asprintf(&str, "%03.1Lf", set->val.num) == -1) {
+ LOGMEM_RET(set->ctx);
+ }
+ set->val.str = str;
+ }
+ break;
+ case LYXP_SET_BOOLEAN:
+ if (set->val.bln) {
+ set->val.str = strdup("true");
+ } else {
+ set->val.str = strdup("false");
+ }
+ LY_CHECK_ERR_RET(!set->val.str, LOGMEM(set->ctx), LY_EMEM);
+ break;
+ case LYXP_SET_NODE_SET:
+ /* we need the set sorted, it affects the result */
+ assert(!set_sort(set));
+
+ rc = cast_node_set_to_string(set, &str);
+ LY_CHECK_RET(rc);
+ lyxp_set_free_content(set);
+ set->val.str = str;
+ break;
+ default:
+ LOGINT_RET(set->ctx);
+ }
+ set->type = LYXP_SET_STRING;
+ }
+
+ /* to NUMBER */
+ if (target == LYXP_SET_NUMBER) {
+ switch (set->type) {
+ case LYXP_SET_STRING:
+ num = cast_string_to_number(set->val.str);
+ lyxp_set_free_content(set);
+ set->val.num = num;
+ break;
+ case LYXP_SET_BOOLEAN:
+ if (set->val.bln) {
+ set->val.num = 1;
+ } else {
+ set->val.num = 0;
+ }
+ break;
+ default:
+ LOGINT_RET(set->ctx);
+ }
+ set->type = LYXP_SET_NUMBER;
+ }
+
+ /* to BOOLEAN */
+ if (target == LYXP_SET_BOOLEAN) {
+ switch (set->type) {
+ case LYXP_SET_NUMBER:
+ if ((set->val.num == 0) || (set->val.num == -0.0f) || isnan(set->val.num)) {
+ set->val.bln = 0;
+ } else {
+ set->val.bln = 1;
+ }
+ break;
+ case LYXP_SET_STRING:
+ if (set->val.str[0]) {
+ lyxp_set_free_content(set);
+ set->val.bln = 1;
+ } else {
+ lyxp_set_free_content(set);
+ set->val.bln = 0;
+ }
+ break;
+ case LYXP_SET_NODE_SET:
+ if (set->used) {
+ lyxp_set_free_content(set);
+ set->val.bln = 1;
+ } else {
+ lyxp_set_free_content(set);
+ set->val.bln = 0;
+ }
+ break;
+ default:
+ LOGINT_RET(set->ctx);
+ }
+ set->type = LYXP_SET_BOOLEAN;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct lys_module *cur_mod,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *cur_scnode,
+ const struct lysc_node *ctx_scnode, struct lyxp_set *set, uint32_t options)
+{
+ LY_ERR ret;
+ uint32_t tok_idx = 0;
+
+ LY_CHECK_ARG_RET(ctx, ctx, exp, set, LY_EINVAL);
+ if (!cur_mod && ((format == LY_VALUE_SCHEMA) || (format == LY_VALUE_SCHEMA_RESOLVED))) {
+ LOGARG(NULL, "Current module must be set if schema format is used.");
+ return LY_EINVAL;
+ }
+
+ /* prepare set for evaluation */
+ memset(set, 0, sizeof *set);
+ set->type = LYXP_SET_SCNODE_SET;
+ set->root_type = lyxp_get_root_type(NULL, ctx_scnode, options);
+ LY_CHECK_RET(set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
+ set->val.scnodes[0].in_ctx = LYXP_SET_SCNODE_START;
+
+ set->ctx = (struct ly_ctx *)ctx;
+ set->cur_scnode = cur_scnode;
+ for (set->context_op = cur_scnode;
+ set->context_op && !(set->context_op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF));
+ set->context_op = set->context_op->parent) {}
+ set->cur_mod = cur_mod;
+ set->format = format;
+ set->prefix_data = prefix_data;
+
+ LOG_LOCSET(set->cur_scnode, NULL, NULL, NULL);
+
+ /* evaluate */
+ ret = eval_expr_select(exp, &tok_idx, 0, set, options);
+
+ LOG_LOCBACK(set->cur_scnode ? 1 : 0, 0, 0, 0);
+ return ret;
+}
+
+LIBYANG_API_DEF const char *
+lyxp_get_expr(const struct lyxp_expr *path)
+{
+ if (!path) {
+ return NULL;
+ }
+
+ return path->expr;
+}
diff --git a/src/xpath.h b/src/xpath.h
new file mode 100644
index 0000000..3e61bb0
--- /dev/null
+++ b/src/xpath.h
@@ -0,0 +1,517 @@
+/**
+ * @file xpath.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG XPath evaluation functions header
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_XPATH_H
+#define LY_XPATH_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "compat.h"
+#include "log.h"
+#include "tree.h"
+#include "tree_schema.h"
+
+struct ly_ctx;
+struct lyd_node;
+
+/**
+ * @internal
+ * @page internals
+ * @section internalsXpath XPath Implementation
+ *
+ * XPath evaluator fully compliant with http://www.w3.org/TR/1999/REC-xpath-19991116/
+ * except the following restrictions in the grammar.
+ *
+ * @subsection internalsXpathGrammar Parsed Grammar
+ *
+ * Full axes are not supported, abbreviated forms must be used,
+ * "id()" function is not supported, and processing instruction and comment nodes are not supported,
+ * which is also reflected in the grammar. Undefined rules and constants are tokens.
+ *
+ * Modified full grammar:
+ * @code
+ * [1] Expr ::= OrExpr // just an alias
+ *
+ * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+ * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+ * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+ * [5] Step ::= (AxisName '::' | '@')? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ * [7] NameTest ::= '*' | NCName ':' '*' | QName
+ * [8] NodeType ::= 'text' | 'node'
+ * [9] Predicate ::= '[' Expr ']'
+ * [10] PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
+ * [11] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
+ * [12] PathExpr ::= LocationPath | PrimaryExpr Predicate*
+ * | PrimaryExpr Predicate* '/' RelativeLocationPath
+ * | PrimaryExpr Predicate* '//' RelativeLocationPath
+ * [13] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+ * [14] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+ * [15] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
+ * | EqualityExpr '!=' RelationalExpr
+ * [16] RelationalExpr ::= AdditiveExpr
+ * | RelationalExpr '<' AdditiveExpr
+ * | RelationalExpr '>' AdditiveExpr
+ * | RelationalExpr '<=' AdditiveExpr
+ * | RelationalExpr '>=' AdditiveExpr
+ * [17] AdditiveExpr ::= MultiplicativeExpr
+ * | AdditiveExpr '+' MultiplicativeExpr
+ * | AdditiveExpr '-' MultiplicativeExpr
+ * [18] MultiplicativeExpr ::= UnaryExpr
+ * | MultiplicativeExpr '*' UnaryExpr
+ * | MultiplicativeExpr 'div' UnaryExpr
+ * | MultiplicativeExpr 'mod' UnaryExpr
+ * [19] UnaryExpr ::= UnionExpr | '-' UnaryExpr
+ * [20] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+ * @endcode
+ */
+
+/* expression tokens allocation */
+#define LYXP_EXPR_SIZE_START 10
+#define LYXP_EXPR_SIZE_STEP 5
+
+/* XPath matches allocation */
+#define LYXP_SET_SIZE_START 4
+#define LYXP_SET_SIZE_MUL_STEP 2
+
+/* building string when casting */
+#define LYXP_STRING_CAST_SIZE_START 64
+#define LYXP_STRING_CAST_SIZE_STEP 16
+
+/* Maximum number of nested expressions. */
+#define LYXP_MAX_BLOCK_DEPTH 100
+
+/**
+ * @brief Tokens that can be in an XPath expression.
+ */
+enum lyxp_token {
+ LYXP_TOKEN_NONE = 0,
+ LYXP_TOKEN_PAR1, /* '(' */
+ LYXP_TOKEN_PAR2, /* ')' */
+ LYXP_TOKEN_BRACK1, /* '[' */
+ LYXP_TOKEN_BRACK2, /* ']' */
+ LYXP_TOKEN_DOT, /* '.' */
+ LYXP_TOKEN_DDOT, /* '..' */
+ LYXP_TOKEN_AT, /* '@' */
+ LYXP_TOKEN_COMMA, /* ',' */
+ LYXP_TOKEN_DCOLON, /* '::' */
+ LYXP_TOKEN_NAMETEST, /* NameTest */
+ LYXP_TOKEN_NODETYPE, /* NodeType */
+ LYXP_TOKEN_VARREF, /* VariableReference */
+ LYXP_TOKEN_FUNCNAME, /* FunctionName */
+ LYXP_TOKEN_OPER_LOG, /* Operator 'and', 'or' */
+ LYXP_TOKEN_OPER_EQUAL, /* Operator '=' */
+ LYXP_TOKEN_OPER_NEQUAL, /* Operator '!=' */
+ LYXP_TOKEN_OPER_COMP, /* Operator '<', '<=', '>', '>=' */
+ LYXP_TOKEN_OPER_MATH, /* Operator '+', '-', '*', 'div', 'mod', '-' (unary) */
+ LYXP_TOKEN_OPER_UNI, /* Operator '|' */
+ LYXP_TOKEN_OPER_PATH, /* Operator '/' */
+ LYXP_TOKEN_OPER_RPATH, /* Operator '//' (recursive path) */
+ LYXP_TOKEN_AXISNAME, /* AxisName */
+ LYXP_TOKEN_LITERAL, /* Literal - with either single or double quote */
+ LYXP_TOKEN_NUMBER /* Number */
+};
+
+/**
+ * @brief XPath Axes types.
+ */
+enum lyxp_axis {
+ LYXP_AXIS_ANCESTOR,
+ LYXP_AXIS_ANCESTOR_OR_SELF,
+ LYXP_AXIS_ATTRIBUTE,
+ LYXP_AXIS_CHILD,
+ LYXP_AXIS_DESCENDANT,
+ LYXP_AXIS_DESCENDANT_OR_SELF,
+ LYXP_AXIS_FOLLOWING,
+ LYXP_AXIS_FOLLOWING_SIBLING,
+ // LYXP_AXIS_NAMESPACE, /* not supported */
+ LYXP_AXIS_PARENT,
+ LYXP_AXIS_PRECEDING,
+ LYXP_AXIS_PRECEDING_SIBLING,
+ LYXP_AXIS_SELF
+};
+
+/**
+ * @brief XPath (sub)expressions that can be repeated.
+ */
+enum lyxp_expr_type {
+ LYXP_EXPR_NONE = 0,
+ LYXP_EXPR_OR,
+ LYXP_EXPR_AND,
+ LYXP_EXPR_EQUALITY,
+ LYXP_EXPR_RELATIONAL,
+ LYXP_EXPR_ADDITIVE,
+ LYXP_EXPR_MULTIPLICATIVE,
+ LYXP_EXPR_UNARY,
+ LYXP_EXPR_UNION
+};
+
+/**
+ * @brief Types of context nodes, #LYXP_NODE_ROOT_CONFIG used only in when or must conditions.
+ */
+enum lyxp_node_type {
+ LYXP_NODE_NONE, /* invalid node type */
+
+ /* XML document roots */
+ LYXP_NODE_ROOT, /* access to all the data (node value first top-level node) */
+ LYXP_NODE_ROOT_CONFIG, /* <running> data context, no state data (node value first top-level node) */
+
+ /* XML elements */
+ LYXP_NODE_ELEM, /* YANG data element (most common) */
+ LYXP_NODE_TEXT, /* YANG data text element (extremely specific use, unlikely to be ever needed) */
+ LYXP_NODE_META /* YANG metadata (do not use for the context node) */
+};
+
+/**
+ * @brief Structure holding a parsed XPath expression.
+ */
+struct lyxp_expr {
+ enum lyxp_token *tokens; /**< Array of tokens. */
+ uint32_t *tok_pos; /**< Array of the token offsets in expr. */
+ uint32_t *tok_len; /**< Array of token lengths in expr. */
+ enum lyxp_expr_type **repeat; /**< Array of expression types that this token begins and is repeated ended with 0,
+ more in the comment after this declaration. */
+ uint32_t used; /**< Used array items. */
+ uint32_t size; /**< Allocated array items. */
+
+ const char *expr; /**< The original XPath expression. */
+};
+
+/*
+ * lyxp_expr repeat
+ *
+ * This value is NULL for all the tokens that do not begin an
+ * expression which can be repeated. Otherwise it is an array
+ * of expression types that this token begins. These values
+ * are used during evaluation to know whether we need to
+ * duplicate the current context or not and to decide what
+ * the current expression is (for example, if we are only
+ * starting the parsing and the first token has no repeat,
+ * we do not parse it as an OrExpr but directly as PathExpr).
+ * Examples:
+ *
+ * Expr: "/ *[key1 and key2 or key1 < key2]"
+ * Tokens: '/' '*' '[' NameTest 'and' NameTest 'or' NameTest '<' NameTest ']'
+ * Repeat: NULL NULL NULL _ NULL NULL NULL _ NULL NULL NULL
+ * | v
+ * v RelationalExpr 0
+ * AndExpr OrExpr 0
+ *
+ * Expr: "//node[key and node2]/key | /cont"
+ * Tokens: '//' NameTest '[' NameTest 'and' NameTest ']' '/' NameTest '|' '/' NameTest
+ * Repeat: _ NULL NULL _ NULL NULL NULL NULL NULL NULL NULL NULL
+ * | v
+ * v AndExpr 0
+ * UnionExpr 0
+ *
+ * Operators between expressions which this concerns:
+ * 'or', 'and', '=', '!=', '<', '>', '<=', '>=', '+', '-', '*', 'div', 'mod', '|'
+ */
+
+/**
+ * @brief Supported types of (partial) XPath results.
+ */
+enum lyxp_set_type {
+ LYXP_SET_NODE_SET = 0,
+ LYXP_SET_SCNODE_SET,
+ LYXP_SET_BOOLEAN,
+ LYXP_SET_NUMBER,
+ LYXP_SET_STRING
+};
+
+/**
+ * @brief Item stored in an XPath set hash table.
+ */
+struct lyxp_set_hash_node {
+ struct lyd_node *node;
+ enum lyxp_node_type type;
+} _PACKED;
+
+/**
+ * @brief XPath variable bindings.
+ */
+struct lyxp_var {
+ char *name; /**< Variable name. In the XPath expression, the name is preceded by a '$' character. */
+ char *value; /**< The value of a variable is an object, which can be of any of the type that are possible
+ for the value of an expression. */
+};
+
+/**
+ * @brief XPath set - (partial) result.
+ */
+struct lyxp_set {
+ enum lyxp_set_type type; /**< Type of the object (value). */
+
+ union {
+ struct lyxp_set_node {
+ struct lyd_node *node; /**< Data node. */
+ enum lyxp_node_type type; /**< Type of the node. */
+ uint32_t pos; /**< Unique node position in the data. */
+ } *nodes; /**< Set of data nodes. */
+ struct lyxp_set_scnode {
+ struct lysc_node *scnode; /**< Compiled YANG node. */
+ enum lyxp_node_type type; /**< Type of the node. */
+
+/* _START and _ATOM values should have grouped values */
+#define LYXP_SET_SCNODE_START -2 /**< scnode not traversed, currently (the only node) in context */
+#define LYXP_SET_SCNODE_START_USED -1 /**< scnode not traversed except for the eval start, not currently in the context */
+#define LYXP_SET_SCNODE_ATOM_NODE 0 /**< scnode was traversed, but not currently in the context */
+#define LYXP_SET_SCNODE_ATOM_VAL 1 /**< scnode was traversed and its value used, but not currently in the context */
+#define LYXP_SET_SCNODE_ATOM_CTX 2 /**< scnode currently in context */
+#define LYXP_SET_SCNODE_ATOM_NEW_CTX 3 /**< scnode in context and just added, so skip it for the current operation */
+#define LYXP_SET_SCNODE_ATOM_PRED_CTX 4 /**< includes any higher value - scnode is not in context because we are in
+ a predicate and this scnode was used/will be used later */
+ int32_t in_ctx; /**< Flag specifies the state of the node in context. Values are defined
+ as LYXP_SET_SCNODE_* */
+ enum lyxp_axis axis; /**< Axis defines on what axis was this schema node reached. */
+ } *scnodes; /**< Set of compiled YANG data nodes. */
+ struct lyxp_set_meta {
+ struct lyd_meta *meta; /**< Node that provides information about metadata of a data element. */
+ enum lyxp_node_type type; /**< Type of the node. */
+ uint32_t pos; /**< Unique node position in the data. if node_type is LYXP_SET_NODE_META,
+ it is the parent node position */
+ } *meta; /**< Set of YANG metadata objects. */
+ char *str; /**< String object. */
+ long double num; /**< Object of the floating-point number. */
+ ly_bool bln; /**< Boolean object. */
+ } val; /**< Evaluated object (value). */
+
+ /* this is valid only for type LYXP_SET_NODE_SET and LYXP_SET_SCNODE_SET */
+ uint32_t used; /**< Number of nodes in the set. */
+ uint32_t size; /**< Allocated size for the set. */
+ struct hash_table *ht; /**< Hash table for quick determination of whether a node is in the set. */
+
+ /* XPath context information, this is valid only for type LYXP_SET_NODE_SET */
+ uint32_t ctx_pos; /**< Position of the current examined node in the set. */
+ uint32_t ctx_size; /**< Position of the last node at the time the node was examined. */
+ ly_bool non_child_axis; /**< Whether any node change was performed on a non-child axis. */
+
+ /* general context */
+ struct ly_ctx *ctx; /**< General context for logging. */
+
+ union {
+ const struct lyd_node *cur_node; /**< Current (original context) node. */
+ const struct lysc_node *cur_scnode; /**< Current (original context) compiled node. */
+ };
+ enum lyxp_node_type root_type; /**< Type of root node. */
+ const struct lysc_node *context_op; /**< Schema of the current node. */
+ const struct lyd_node *tree; /**< Data tree on which to perform the evaluation. */
+ const struct lys_module *cur_mod; /**< Current module for the expression (where it was "instantiated"). */
+ LY_VALUE_FORMAT format; /**< Format of the XPath expression. */
+ void *prefix_data; /**< Format-specific prefix data (see ::ly_resolve_prefix). */
+ const struct lyxp_var *vars; /**< XPath variables. [Sized array](@ref sizedarrays).
+ Set of variable bindings. */
+};
+
+/**
+ * @brief Get string format of an XPath token.
+ *
+ * @param[in] tok Token to transform.
+ * @return Token type string.
+ */
+const char *lyxp_token2str(enum lyxp_token tok);
+
+/**
+ * @brief Evaluate an XPath expression on data. Be careful when using this function, the result can often
+ * be confusing without thorough understanding of XPath evaluation rules defined in RFC 7950.
+ *
+ * @param[in] ctx libyang context to use.
+ * @param[in] exp Parsed XPath expression to be evaluated.
+ * @param[in] cur_mod Current module for the expression (where it was "instantiated").
+ * @param[in] format Format of the XPath expression (more specifically, of any used prefixes).
+ * @param[in] prefix_data Format-specific prefix data (see ::ly_resolve_prefix).
+ * @param[in] cur_node Current data node, NULL in case of the root node. Equal to @p ctx_node unless a
+ * subexpression is being evaluated.
+ * @param[in] ctx_node Starting context data node, NULL in case of the root node. Equal to @p cur_node unless a
+ * subexpression is being evaluated.
+ * @param[in] tree Data tree on which to perform the evaluation, it must include all the available data (including
+ * the tree of @p ctx_node). Can be any node of the tree, it is adjusted.
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] set Result set.
+ * @param[in] options Whether to apply some evaluation restrictions.
+ * @return LY_EVALID for invalid argument types/count,
+ * @return LY_EINCOMPLETE for unresolved when,
+ * @return LY_EINVAL, LY_EMEM, LY_EINT for other errors.
+ */
+LY_ERR lyxp_eval(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct lys_module *cur_mod,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lyd_node *cur_node, const struct lyd_node *ctx_node,
+ const struct lyd_node *tree, const struct lyxp_var *vars, struct lyxp_set *set, uint32_t options);
+
+/**
+ * @brief Get all the partial XPath nodes (atoms) that are required for @p exp to be evaluated.
+ *
+ * @param[in] ctx libyang context to use.
+ * @param[in] exp Parsed XPath expression to be evaluated.
+ * @param[in] cur_mod Current module for the expression (where it was "instantiated").
+ * @param[in] format Format of the XPath expression (more specifically, of any used prefixes).
+ * @param[in] prefix_data Format-specific prefix data (see ::ly_resolve_prefix).
+ * @param[in] cur_scnode Current schema node, NULL in case of the root node. Equal to @p ctx_scnode unless a
+ * subexpression is being atomized.
+ * @param[in] ctx_scnode Starting context schema node, NULL in case of the root node. Equal to @p cur_scnode unless a
+ * subexpression is being atomized.
+ * @param[out] set Result set.
+ * @param[in] options Whether to apply some evaluation restrictions, one flag must always be used.
+ * @return LY_ERR (same as ::lyxp_eval()).
+ */
+LY_ERR lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct lys_module *cur_mod,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *cur_scnode,
+ const struct lysc_node *ctx_scnode, struct lyxp_set *set, uint32_t options);
+
+/** used only internally, maps with @ref findxpathoptions */
+#define LYXP_IGNORE_WHEN 0x01 /**< Ignore unevaluated when in data nodes and do not return ::LY_EINCOMPLETE. */
+#define LYXP_SCHEMA 0x02 /**< Apply data node access restrictions defined for 'when' and 'must' evaluation. */
+#define LYXP_SCNODE 0x04 /**< No special tree access modifiers. */
+#define LYXP_SCNODE_SCHEMA LYS_FIND_XP_SCHEMA /**< Apply node access restrictions defined for 'when' and 'must' evaluation. */
+#define LYXP_SCNODE_OUTPUT LYS_FIND_XP_OUTPUT /**< Search RPC/action output nodes instead of input ones. */
+#define LYXP_SCNODE_ALL 0x1C /**< mask for all the LYXP_* values */
+#define LYXP_SKIP_EXPR 0x20 /**< The rest of the expression will not be evaluated (lazy evaluation) */
+#define LYXP_SCNODE_ERROR LYS_FIND_NO_MATCH_ERROR /**< Return error if a path segment matches no nodes, otherwise only
+ warning is printed. */
+#define LYXP_ACCESS_TREE_ALL 0x80 /**< Explicit accessible tree of all the nodes. */
+#define LYXP_ACCESS_TREE_CONFIG 0x0100 /**< Explicit accessible tree of only configuration data. */
+
+/**
+ * @brief Cast XPath set to another type.
+ * Indirectly context position aware.
+ *
+ * @param[in] set Set to cast.
+ * @param[in] target Target type to cast \p set into.
+ * @return LY_ERR
+ */
+LY_ERR lyxp_set_cast(struct lyxp_set *set, enum lyxp_set_type target);
+
+/**
+ * @brief Free dynamic content of a set.
+ *
+ * @param[in] set Set to modify.
+ */
+void lyxp_set_free_content(struct lyxp_set *set);
+
+/**
+ * @brief Check for duplicates in a schema node set.
+ *
+ * @param[in] set Set to check.
+ * @param[in] node Node to look for in @p set.
+ * @param[in] node_type Type of @p node.
+ * @param[in] skip_idx Index from @p set to skip.
+ * @param[out] index_p Optional pointer to store index if the node is found.
+ * @return Boolean value whether the @p node found or not.
+ */
+ly_bool lyxp_set_scnode_contains(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+ int skip_idx, uint32_t *index_p);
+
+/**
+ * @brief Merge 2 schema node sets.
+ *
+ * @param[in] set1 Set to merge into.
+ * @param[in] set2 Set to merge. Its content is freed.
+ */
+void lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2);
+
+/**
+ * @brief Parse an XPath expression into a structure of tokens.
+ * Logs directly.
+ *
+ * https://www.w3.org/TR/1999/REC-xpath-19991116/#exprlex
+ *
+ * @param[in] ctx Context for errors.
+ * @param[in] expr_str XPath expression to parse. It is duplicated.
+ * @param[in] expr_len Length of @p expr, can be 0 if @p expr is 0-terminated.
+ * @param[in] reparse Whether to re-parse the expression to finalize full XPath parsing and fill
+ * information about expressions and their operators (fill repeat).
+ * @param[out] expr_p Pointer to return the filled expression structure.
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EMEM in case of memory allocation failure.
+ * @return LY_EVALID in case of invalid XPath expression in @p expr_str.
+ */
+LY_ERR lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len, ly_bool reparse,
+ struct lyxp_expr **expr_p);
+
+/**
+ * @brief Duplicate parsed XPath expression.
+ *
+ * If @p start_idx and @p end_idx are both 0, the whole expression is duplicated.
+ *
+ * @param[in] ctx Context with a dictionary.
+ * @param[in] exp Parsed expression.
+ * @param[in] start_idx Starting @p exp index to duplicate.
+ * @param[in] end_idx Last @p exp index to duplicate.
+ * @param[out] dup Duplicated structure.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxp_expr_dup(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t start_idx, uint32_t end_idx,
+ struct lyxp_expr **dup);
+
+/**
+ * @brief Look at the next token and check its kind.
+ *
+ * @param[in] ctx Context for logging, not logged if NULL.
+ * @param[in] exp Expression to use.
+ * @param[in] tok_idx Token index in the expression \p exp.
+ * @param[in] want_tok Expected token.
+ * @return LY_EINCOMPLETE on EOF,
+ * @return LY_ENOT on non-matching token,
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR lyxp_check_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_token want_tok);
+
+/**
+ * @brief Look at the next token and skip it if it matches the expected one.
+ *
+ * @param[in] ctx Context for logging, not logged if NULL.
+ * @param[in] exp Expression to use.
+ * @param[in,out] tok_idx Token index in the expression \p exp, is updated.
+ * @param[in] want_tok Expected token.
+ * @return LY_EINCOMPLETE on EOF,
+ * @return LY_ENOT on non-matching token,
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR lyxp_next_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_token want_tok);
+
+/**
+ * @brief Look at the next token and skip it if it matches either of the 2 expected ones.
+ *
+ * @param[in] ctx Context for logging, not logged if NULL.
+ * @param[in] exp Expression to use.
+ * @param[in,out] tok_idx Token index in the expression \p exp, is updated.
+ * @param[in] want_tok1 Expected token 1.
+ * @param[in] want_tok2 Expected token 2.
+ * @return LY_EINCOMPLETE on EOF,
+ * @return LY_ENOT on non-matching token,
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx,
+ enum lyxp_token want_tok1, enum lyxp_token want_tok2);
+
+/**
+ * @brief Find variable named @name in @p vars.
+ *
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[in] name Name of the variable being searched.
+ * @param[in] name_len Name length can be set to 0 if @p name is terminated by null byte.
+ * @param[out] var Variable that was found. The parameter is optional.
+ * @return LY_SUCCESS if the variable was found, otherwise LY_ENOTFOUND.
+ */
+LY_ERR lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var);
+
+/**
+ * @brief Frees a parsed XPath expression. @p expr should not be used afterwards.
+ *
+ * @param[in] ctx libyang context of the expression.
+ * @param[in] expr Expression to free.
+ */
+void lyxp_expr_free(const struct ly_ctx *ctx, struct lyxp_expr *expr);
+
+#endif /* LY_XPATH_H */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..6f36f31
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Correct RPATH usage on OS X
+set(CMAKE_MACOSX_RPATH TRUE)
+
+configure_file("${PROJECT_SOURCE_DIR}/tests/tests_config.h.in" "${PROJECT_BINARY_DIR}/tests/tests_config.h" ESCAPE_QUOTES @ONLY)
+include_directories(SYSTEM ${CMOCKA_INCLUDE_DIR})
+include_directories(${PROJECT_BINARY_DIR}/tests/)
+
+function(ly_add_utest)
+ cmake_parse_arguments(ADDTEST "" "NAME;WRAP" "SOURCES" ${ARGN})
+ set(TEST_NAME utest_${ADDTEST_NAME})
+
+ foreach(TEST_SOURCE ${ADDTEST_SOURCES})
+ list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SOURCE})
+ endforeach()
+
+ add_executable(${TEST_NAME} ${TEST_SOURCES} $<TARGET_OBJECTS:yangobj>)
+ target_compile_definitions(${TEST_NAME} PRIVATE LIBYANG_BUILD)
+
+ # Set common attributes of all tests
+ set_target_properties(${TEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
+ target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} ${CMAKE_DL_LIBS})
+ if (NOT WIN32)
+ target_link_libraries(${TEST_NAME} m)
+ else()
+ target_link_libraries(${TEST_NAME} ${COMPAT_WIN_LIBRARIES})
+ endif()
+ if (NOT APPLE)
+ if (ADDTEST_WRAP)
+ set_target_properties(${TEST_NAME} PROPERTIES LINK_FLAGS "${ADDTEST_WRAP}")
+ endif()
+ endif()
+
+ add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "MALLOC_CHECK_=3")
+
+ if(ENABLE_VALGRIND_TESTS)
+ add_test(${TEST_NAME}_valgrind valgrind --leak-check=full --show-leak-kinds=all --suppressions=${PROJECT_SOURCE_DIR}/tests/ld.supp --error-exitcode=1 ${CMAKE_BINARY_DIR}/tests/${TEST_NAME})
+ endif()
+endfunction()
+
+if(ENABLE_TESTS)
+ add_subdirectory(plugins)
+ add_subdirectory(utests)
+ if(NOT WIN32)
+ add_subdirectory(style)
+ add_subdirectory(fuzz)
+ endif()
+endif()
+if(ENABLE_PERF_TESTS)
+ add_subdirectory(perf)
+endif()
+
+set(format_sources ${format_sources} PARENT_SCOPE)
diff --git a/tests/cstr.sh b/tests/cstr.sh
new file mode 100755
index 0000000..e672bc1
--- /dev/null
+++ b/tests/cstr.sh
@@ -0,0 +1,503 @@
+#!/bin/bash
+
+# @file cstr.sh
+# @author Adam Piecek <piecek@cesnet.cz>
+# @brief Helper script for creating tests that converts yang and c strings.
+#
+# Copyright (c) 2020 CESNET, z.s.p.o.
+#
+# This source code is licensed under BSD 3-Clause License (the "License").
+# You may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://opensource.org/licenses/BSD-3-Clause
+
+#---------- Global variables ----------
+
+script_name="$(basename $0)"
+
+# -i <FILE>
+target_file=""
+target_text=""
+
+# -l NUM
+linenum_flag=""
+linenum_default=1
+linenum=$linenum_default
+
+# -v
+verbose=""
+
+# -y <EXE>
+yanglint_flag=""
+yanglint_exe="yanglint -f yang"
+yanglint_run=""
+
+# <FILE>
+input_text=""
+input_file=""
+input_ext=""
+
+tmpfile=""
+
+#---------- Basic functions ----------
+
+function usage(){
+echo "\
+Usage:
+$script_name [<INPUT>] [OPTION]...
+
+Examples:
+From the \"test.c\" file, c-string is converted from line 20 to a yang schema.
+$script_name test.c -l 20
+The yang scheme is converted from the \"a.yang\" file to c-string and inserted into the \"test.c\" file on line 30.
+$script_name a.yang -i test.c -l 30
+
+Note:
+If <INPUT> missing, read standard input. Press ctrl-d to end the entry.
+The c-string is statement starting with character (\") and ending with string (\";).
+This script creates a temporary file in the \"/tmp\" directory with the file name <yang_module_name>.yang,
+which it eventually deletes when it finishes its run.
+"
+echo "\
+OPTION:
+-c, --combinations # Print a help table with all important parameter combinations and a usage comment.
+#
+-i, --insert=FILE # Insert c-string to the FILE.
+ # Option is valid only if <INPUT> is in .yang format and option \"-l\" is set.
+ # Warning: make sure you have a file backed up so you can undo the change.
+ # Don't forget to reload the file in your editor to see the changes.
+#
+-l, --line=NUM # If <INPUT> is in .c format: find c-string on the NUM line.
+ # If <INPUT> is in .yang format: insert c-string on the NUM-th line.
+ # If <INPUT> is from standard input, then the parameter is always $linenum_default
+ # regardless of the value of the --line parameter.
+#
+-v, --verbose # Print debug messages.
+#
+-y, --yanglint=EXEC # Run yang schema formatting by \"EXEC\".
+ # Default value is \"./$yanglint_exe </tmp/TEMPORARY_FILE>\",
+ # but if the local directory does not contain yanglint,
+ # then yanglint is taken from PATH.
+ # Note that parameters must also be entered, eg \"pyang -f tree\".
+" | column -t -s "#"
+}
+
+function combinations(){
+echo "\
+Abbreviations: c -> .c format file, y -> .yang format file
+All important combinations of parameters are: ( -y, -v parameters are ommitted)
+"
+echo "\
+<c> -i -l # Not allowed.
+<c> -i # Not allowed.
+<c> -l # Find c-string on line -l in file/stdin <c>, convert it to .yang and print the result.
+<c> # Get c-string on line $linenum_default in file/stdin <c>, convert it to .yang and print the result.
+<y> -i -l # Get yang schema from file/stdin <y>, convert it to .c format and insert result to the -i file on line -l.
+<y> -i # Get yang schema from file/stdin <y>, convert it to .c format and insert result to the -i file on line $linenum_default.
+<y> -l # Not allowed.
+<y> # Get yang schema from file/stdin <y>, convert it to .c format and print the result.
+" | column -t -s "#"
+}
+
+function print_verbose()
+{
+ if [ -n "$verbose" ] && [ -n "$1" ]; then echo "Verbose: $1" ; fi
+}
+
+function exit_if_error()
+{
+ if [ $? != 0 ]; then
+ if [ -n "$1" ]; then
+ echo "$1" >&2
+ fi
+ exit 1
+ fi
+}
+
+function print_error_then_exit()
+{
+ echo "$1" >&2
+ exit 1
+}
+
+function fill_input() {
+ # check if argument is empty
+ if [ -z "$1" ]; then
+ # read from stdin
+ while IFS= read -r line; do
+ if [ -z $input_text ] ; then
+ input_text="$line"
+ else
+ input_text="$input_text\n$line"
+ fi
+ done
+ # substitute string \n with newline
+ input_text="$(echo -e "$input_text")"
+ print_verbose "Text is loaded from stdin"
+ else
+ # check if file exists and is a regular file
+ if [ -f "$1" ]; then
+ # <INPUT> is readable file
+ input_file="$1"
+ if [ -r $input_file ]; then
+ # read from file is possible
+ input_text=$(cat "$input_file")
+ print_verbose "Text is loaded from \"$input_file\" file"
+ else
+ print_error_then_exit "Error: cannot read \"$input_file\""
+ fi
+ else
+ print_error_then_exit "Error: file \"$1\" cannot open"
+ fi
+ fi
+ # set input_text
+ # set input_file if file name was entered
+}
+
+
+#---------- Getopt ----------
+
+# options may be followed by one colon to indicate they have a required argument
+options=$(getopt -o ci:hl:vy: -l combinations,insert,help,line:,verbose,yanglint: --name "$0" -- "$@")
+exit_if_error "Failed to parse options...exiting."
+
+eval set -- "$options"
+
+# extract options and their arguments into variables.
+while true ; do
+ case "$1" in
+ -c | --combinations )
+ combinations
+ exit 0
+ ;;
+ -i | --insert )
+ target_file="$2"
+ shift 2
+ ;;
+ -h | --help )
+ usage
+ exit 0
+ ;;
+ -l | --line )
+ linenum_flag="true"
+ linenum="$2"
+ shift 2
+ ;;
+ -v | --verbose )
+ verbose="true"
+ shift
+ ;;
+ -y | --yanglint)
+ yanglint_flag="true"
+ yanglint_exe="$2"
+ shift 2
+ ;;
+ -- )
+ # set input_text
+ # set input_file if file name was entered
+ fill_input $2
+ break
+ ;;
+ -* )
+ usage
+ print_error_then_exit "$0: error - unrecognized option $1"
+ ;;
+ *)
+ print_error_then_exit "Internal error!"
+ ;;
+ esac
+done
+
+#---------- Functions for checking parameters ----------
+
+function get_one_line()
+{
+ local text_with_more_lines="$1"
+ local linenum="$2"
+ echo "$(echo "$text_with_more_lines" | sed "${linenum}q;d")"
+}
+
+function recognize_format()
+{
+ local text="$1"
+ local linenum="$2"
+ local line=$(get_one_line "$text" "$linenum")
+ local matched_chars=$(expr match "$line" "^\s*\"")
+ if [ "$matched_chars" == "0" ]; then
+ echo "yang"
+ else
+ echo "c"
+ fi
+}
+
+#---------- Check parameters ----------
+
+# check -y
+exe_name=$(echo "$yanglint_exe" | awk '{print $1;}')
+if [ -n "$yanglint_flag" ]; then
+ # try user's exe
+ command -v "$exe_name" > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ command -v "./$exe_name" > /dev/null 2>&1
+ exit_if_error "Error: cannot find exe \"$exe_name\""
+ yanglint_run="./$yanglint_exe"
+ else
+ yanglint_run="$yanglint_exe"
+ fi
+ print_verbose "Using user's EXE \"$exe_name\""
+else
+ # try in exe current directory
+ command -v "./$exe_name" > /dev/null 2>&1
+ if [ $? == 0 ] ; then
+ print_verbose "Using default \"$exe_name\" in current directory"
+ yanglint_run="./$yanglint_exe"
+ else
+ # try PATH's exe
+ command -v "$exe_name" > /dev/null 2>&1
+ exit_if_error "Error: \"$exe_name\" wasn't found in the current directory nor is installed"
+ print_verbose "Using default \"$exe_name\" from PATH"
+ yanglint_run="$yanglint_exe"
+ fi
+fi
+# yanglint_run must be set
+yanglint_run="$yanglint_run"" " # add space due to input filename
+
+# check <INPUT>
+# expected that input_text has string
+if [ -n "$input_file" ]; then
+ # get suffix of the <INPUT> file
+ input_ext=${input_file##*.}
+else
+ # <INPUT> is from stdin
+ input_ext=$(recognize_format "$input_text" "1")
+fi
+print_verbose "<INPUT> is in \"$input_ext\" format"
+# input_ext must be set
+
+# check -i
+if [ -n "$target_file" ]; then
+ # if target_file is writeable
+ if [ -w $target_file ]; then
+ print_verbose "target_file $target_file is writeable"
+ else
+ print_error_then_exit "Error: cannot insert text to file \"$target_file\""
+ fi
+ # if <INPUT> is yang then -l must be set
+ if [ "$input_ext" == "yang" ] && [ -n "$linenum_flag" ]; then
+ print_verbose "-i option is valid"
+ else
+ print_error_then_exit "Error: Option -i is valid only if <INPUT> is in .yang format and option \"-l\" is set."
+ fi
+ target_text=$(cat "$target_file")
+fi
+# target_text must be set
+
+# check -l
+if [ -n "$linenum_flag" ]; then
+
+ if [ -z "$input_file" ]; then
+ # reading <INPUT> from stdin
+ print_verbose "-l option is ignored because <INPUT> is from stdin."
+ linenum=$linenum_default
+ else
+ if [ "$linenum" -lt "0" ]; then
+ print_error_then_exit "Error: only positive numbers in --line option are valid"
+ fi
+ if [ "$input_ext" == "yang" ]; then
+ if [ -z "$target_file" ]; then
+ print_error_then_exit "Error: Option -l with <INPUT> format yang is valid only if option -i is set too."
+ fi
+ text4linenum="$target_text"
+ else
+ text4linenum="$input_text"
+ fi
+ # check if linenum is not too big
+ lines_count=$(echo "$text4linenum" | wc -l)
+ if [ "$linenum" -gt "$lines_count" ]; then
+ print_error_then_exit "Error: number in --line option is too big"
+ fi
+ print_verbose "-l option is valid"
+ fi
+else
+ print_verbose "-l option is not set"
+ # rest of restrictions must be checked in option -i
+fi
+
+#---------- Formatting text ----------
+
+# warning: do not call this function in subshell $(formatting_yang_text)
+function formatting_yang_text()
+{
+ # parameters: modify global variable input_text, read yanglint_run, read tmpfile
+ echo "$input_text" > "$tmpfile"
+ # check if <INPUT> is valid yang file, store only stderr to variable
+ yanglint_output=$(eval ""$yanglint_run" "$tmpfile"" 2>&1) # do not add local
+ local yanglint_retval="$?"
+ if [ "$yanglint_retval" != "0" ]; then
+ print_verbose "$yanglint_output"
+ fi
+ $(exit $yanglint_retval)
+ exit_if_error "Error: yang-schema in is not valid."
+ input_text="$yanglint_output"
+}
+
+#---------- Main functions ----------
+
+# called from main run
+function cstring2yang()
+{
+ local ret
+ local input_text="$1"
+ local linenum="$2"
+ # extract statement from c language file from specific line
+ ret=$(echo "$input_text" |
+ awk -v linenum="$linenum" '
+ NR >= linenum {lineflag=1; print}
+ /;\s*$/ {if(lineflag == 1) exit;}
+ ')
+ # substitute special characters - for example \"
+ ret=$(printf "$ret")
+ # remove everything before first " and remove last "
+ ret=$(echo "$ret" | grep -oP '"\K.*' | sed 's/"\s*$//')
+ # but last line is not right due to "; at the end of line
+ # so get last line and remove ";
+ lastline=$(echo "$ret" | tail -n1 | sed -n 's/";\s*$//p')
+ # get everything before last line
+ ret=$(echo "$ret" | head -n -1)
+ # concatenate result
+ ret=$ret$lastline
+ echo "$ret"
+}
+
+# called from main run
+function yang2cstring()
+{
+ local ret
+ local input_text="$1"
+ # backslashing character "
+ ret=${input_text//\"/\\\"}
+ ret=$(echo "$ret" | awk -v s="\"" -v e="\\\n\"" '{print s$0e}')
+ ret="$ret"";"
+ echo "$ret"
+}
+
+# called from --Create temporary file--
+function get_yang_module_name()
+{
+ local text="$1"
+ local linenum="$2"
+ local input_ext="$3"
+ if [ "$input_ext" == "yang" ]; then
+ # module name search on line 1 in .yang file
+ linenum=1
+ fi
+ # else get module name on line $linenum in .c file
+ echo "$(echo "$text" | awk -v linenum="$linenum" 'NR >= linenum' | grep -oP -m 1 "\s*module\s+\K\S+")"
+}
+
+# called from tabs2spaces_in_line and insert_indentation
+function number2spaces()
+{
+ local number="$1"
+ # return string containing <number> spaces
+ echo "$(printf ' %.0s' $(seq 1 $number))"
+}
+
+# called from count_indentation_in_line function
+function tabs2spaces_in_line()
+{
+ local text="$1"
+ local linenum="$2"
+ local tabinspaces="$(number2spaces 4)" # 1 tab = 4 spaces
+ local line="$(get_one_line "$text" "$linenum")"
+ echo "$(echo "$line" | sed "s/\t/$tabinspaces/")"
+}
+
+# called from main run
+function count_indentation_in_line()
+{
+ local text="$1"
+ local linenum="$2"
+ local line="$(tabs2spaces_in_line "$1" "$2")"
+ echo "$(expr match "$line" "^ *")"
+}
+
+# called from main run
+function insert_indentation()
+{
+ local text="$1"
+ local number_of_spaces="$2" # from count_indentation_in_line
+ local spaces="$(number2spaces "$number_of_spaces")"
+ echo "$(echo "$text" | sed -e "s/^/$spaces/")"
+}
+
+# called from main run
+function insert_text2file()
+{
+ local text="$1"
+ local linenum="$2"
+ local filename="$3"
+
+ linenum=$((linenum + 1))
+ awk -i inplace -v text="$text" -v linenum="$linenum" 'NR == linenum {print text} 1' $filename
+}
+
+#---------- Create temporary file ----------
+
+module_name=$(get_yang_module_name "$input_text" "$linenum" "$input_ext")
+if [ -z "$module_name" ]; then
+ print_error_then_exit "Error: module name not found"
+fi
+tmpfile="/tmp/""$module_name"".yang"
+touch "$tmpfile"
+exit_if_error "Error: error while creating temporary file"
+# delete temporary file after script end
+trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
+exit_if_error "Error: trap return error"
+
+#---------- Main run ----------
+
+# print new line for clarity
+if [ -z "$input_file" ] && [ -z "$target_file" ]; then
+ echo ""
+fi
+
+if [ "$input_ext" == "yang" ]; then
+ if [ -z "$target_file" ]; then
+ # Options: (<y> -l, <y>, <y-stdin> -l, <y-stdin>)
+ print_verbose "Print c-string to output"
+ formatting_yang_text
+ echo "$(yang2cstring "$input_text")"
+ else
+ # Options: (<y-stdin> -i -l, <y> -i -l, <y> -i)
+ print_verbose "Insert c-string to target_file"
+
+ # formatting and converting
+ formatting_yang_text
+ inserted_text="$(yang2cstring "$input_text")"
+ # add extra backslash
+ inserted_text=${inserted_text//\\/\\\\}
+
+ # indentation
+ indentation="$(count_indentation_in_line "$target_text" "$linenum")"
+ print_verbose "indentation is: $indentation"
+ inserted_text="$(insert_indentation "$inserted_text" "$indentation")"
+
+ # inserting to file
+ insert_text2file "$inserted_text" "$linenum" "$target_file"
+ echo "Done"
+ fi
+elif [ "$input_ext" == "c" ] || [ "$input_ext" == "h" ]; then
+ # Options: (<c-stdin> -l, <c-stdin>, <c> -l, <c>)
+ print_verbose "Print yang to output or from file <c> print yang to output"
+ output="$(cstring2yang "$input_text" "$linenum")"
+ # "input_text" is input and output parameter for formatting_yang_text
+ input_text="$output"
+ formatting_yang_text
+ echo "$input_text"
+else
+ print_error_then_exit "Error: format \"$input_ext\" is not supported"
+fi
+
+exit 0
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
new file mode 100644
index 0000000..3da61e1
--- /dev/null
+++ b/tests/fuzz/CMakeLists.txt
@@ -0,0 +1,28 @@
+if(ENABLE_FUZZ_TARGETS)
+ set(fuzz_targets lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json yang_parse_module)
+
+ if(FUZZER STREQUAL "AFL")
+ foreach(target_name IN LISTS fuzz_targets)
+ add_executable(${target_name}_fuzz_harness ${target_name}.c main.c)
+ target_link_libraries(${target_name}_fuzz_harness yang)
+ endforeach()
+ elseif()
+ foreach(target_name IN LISTS fuzz_targets)
+ add_executable(${target_name}_fuzz_harness ${target_name}.c)
+ set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer")
+ target_link_libraries(${target_name}_fuzz_harness yang "-fsanitize=fuzzer")
+ endforeach()
+ endif()
+endif()
+
+if(ENABLE_TESTS)
+ add_executable(fuzz_regression_test fuzz_regression_test.c)
+ set(fuzz_regression_tests lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json)
+ foreach(target_name IN LISTS fuzz_regression_tests)
+ file(COPY ${CMAKE_SOURCE_DIR}/tests/fuzz/corpus/${target_name} DESTINATION ${CMAKE_BINARY_DIR}/tests/fuzz/)
+ add_executable(regress_fuzz_${target_name} ${target_name}.c main.c)
+ set_target_properties(regress_fuzz_${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests/fuzz/${target_name}")
+ target_link_libraries(regress_fuzz_${target_name} yang)
+ add_test(NAME regress_fuzz_${target_name} COMMAND fuzz_regression_test regress_fuzz_${target_name} . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests/fuzz/${target_name})
+ endforeach()
+endif()
diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md
new file mode 100644
index 0000000..d0abf4d
--- /dev/null
+++ b/tests/fuzz/README.md
@@ -0,0 +1,64 @@
+# FUZZING
+This directory contains a collection of fuzz harnesses, which are designed to
+be used with [AFL](http://lcamtuf.coredump.cx/afl/) and [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)
+fuzzers. The harnesses should also be easily reusable with other similar fuzzers.
+
+Two asciinema examples are available, one for LibFuzzer:
+https://asciinema.org/a/311035
+and one for AFL:
+https://asciinema.org/a/311060
+
+To build the fuzz targets, the ENABLE_FUZZ_TARGETS option has to be enabled.
+The FUZZER option specifies which fuzzer to use, currently only AFL and LibFuzzer
+are supported, with AFL being the default. LibFuzzer is based on the same
+principles that AFL works with, but has a different implementation of the fuzzing engine
+and is integrated with UBSAN by default, while AFL lacks official integration with UBSAN.
+
+To use the harnesses with AFL, one of AFL's compilers should be used.
+For example the AFL clang-fast compiler can be used with the cmake option shown below.
+It is recommended to set the build type to Release, since otherwise the fuzzer will
+detect failed asserts as crashes.
+If LibFuzzer is used, clang has to be used, as gcc doesn't support -fsanitize=fuzzer.
+
+```
+$ cmake -DENABLE_FUZZ_TARGETS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=path_to_afl/afl-clang-fast ..
+```
+
+After that the programs can be built by running make:
+```
+$ make
+```
+
+The target executables will then be available in tests/fuzz of the build directory that was used.
+
+The libyang yang test files available in the `tests/modules/` subdirectory can be used as initial
+test cases for fuzzing targets that receive YANG models, like the lys_parse_mem_fuzz_harness. However, a smaller corpus of YANG models should probably
+be used, as larger models decrease execution speed. A good place to start would be to collect
+small YANG files, each of which uses only a single YANG feature.
+
+The files that will be used as starting test cases should be copied into a single directory. Those files should then be minimized by using afl-cmin and afl-tmin.
+
+To increase the speed of fuzzing, the test cases and AFL output files should be stored on a temporary RAM disk.
+If a new fuzz target is used, AFL persistent mode should be used. More about persistent mode can be read in the official AFL documentation.
+
+When all of the above is done the fuzzing process can begin. AFL supports running multiple instances of the fuzzer, which can speed up the
+process on multi core CPUs. The first fuzz instance should be started in master mode, and the other instances in slave mode.
+The total number of instances should usually be equal to the number of cores.
+
+Below is an example of running 2 instances. The -i flag specifies the testcase input directory, and the -o file specifies the directory the fuzzer will use for output.
+```
+afl-fuzz -i minimised_testcases/ -o syncdir/ -M fuzzer1 -- libyang/build/tests/fuzz/lyd_parse_mem_fuzz_harness
+afl-fuzz -i minimised_testcases/ -o syncdir/ -S fuzzer2 -- libyang/build/tests/fuzz/lyd_parse_mem_fuzz_harness
+```
+
+To fuzz with LibFuzzer, at the most basic level, everything that is required is
+to run the compiled fuzz target.
+However, running the target like that invokes the harness with only one job
+on a single core, with no starting inputs.
+Multiple jobs running on separate cores should be used, with a starting input corpus.
+The options are described in the official LibFuzzer documentation (https://llvm.org/docs/LibFuzzer.html).
+
+## Fuzzing corpus and regression testing
+The `tests/fuzz/corpus` directory contains subdirectories for every fuzz target. Those subdirectories contain a collection of previous inputs that were found by fuzzing and caused visible issues or crashes. Every input file is named after the issue or pull request where it was originally reported. When a new issue is discovered, the input causing the issue should be added to the appropriate directory.
+
+These input files are then used by the fuzz_regression_test test which sends the corpus into the corresponding fuzz harness, to test whether any of the files crash and cause regressions.
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull11438 b/tests/fuzz/corpus/lyd_parse_mem_json/pull11438
new file mode 100644
index 0000000..d4722b2
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull11438
@@ -0,0 +1 @@
+{"0R:0::809e-47,-689e-47,-689e-489e-47":[809e-47,-689e-47,-689e-4709e-47,-689e-47,-689e-489e-47":[809e-47,-689e-47,-689e-47647,-688Je7,-6889e647,-688Je7,-6889e-47" \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1203 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1203
new file mode 100644
index 0000000..b732c50
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1203
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1269 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269
@@ -0,0 +1 @@
+[]
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1269_2 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269_2
new file mode 100644
index 0000000..9f553e6
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269_2
@@ -0,0 +1 @@
+[[], []]
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1280 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1280
new file mode 100644
index 0000000..0b15336
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1280
@@ -0,0 +1 @@
+"\u1 \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number
new file mode 100644
index 0000000..cbbee10
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number
@@ -0,0 +1 @@
+{"200 -11-10T23:00:00Z": 1E+2}
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings
new file mode 100644
index 0000000..9e93b42
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings
@@ -0,0 +1 @@
+{"200 -11-10T23:00:00Z": "hello w\rld"},
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1348 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1348
new file mode 100644
index 0000000..5759231
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1348
@@ -0,0 +1 @@
+ "a\tHt\\\\HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH(HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHXHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\\\\\\\\\"
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1460 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460
new file mode 100644
index 0000000..66bc72f
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460
@@ -0,0 +1 @@
+"viøonisionp\u\ \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1460_2 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460_2
new file mode 100644
index 0000000..1df63c8
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460_2
@@ -0,0 +1 @@
+"viøonisionp\uGAAA"
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1571 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1571
new file mode 100644
index 0000000..965dad8
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1571
@@ -0,0 +1 @@
+{"@types:uint32":{"typus:@uint32":1,"typus:@uint32":2,"typJs:uint32":3}}
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1585 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1585
new file mode 100644
index 0000000..e9c1d5d
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1585
@@ -0,0 +1 @@
+{"@types:uint32":{"@":{"ns:int32":{"a":[1
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1626 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1626
new file mode 100644
index 0000000..d8e0d57
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1626
@@ -0,0 +1 @@
+{"@types:uint32":{"@":{">:1,":9,"\\\\\\\\\\:2,":8,":3,":7,":-402
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1693 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1693
new file mode 100644
index 0000000..db6d1a4
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1693
@@ -0,0 +1 @@
+{"types:cont":{"":"","":{}}} \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_1 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_1
new file mode 100644
index 0000000..e411daa
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_1
@@ -0,0 +1 @@
+{"types:cont":{"leaflt30,1,10,2]xxnt32":1,"types:uint":1,"types:uinis2":922337203685477}} \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_2 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_2
new file mode 100644
index 0000000..623be41
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_2
@@ -0,0 +1 @@
+{"types:cont":{"leaflt30,1,GGGGGGGGGGGGGGGGGGGGGGGGGGGGG10,2]xxnt32":1,"types:ui~t":1,"types:uinis2":922337203685477}} \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1074 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1074
new file mode 100644
index 0000000..c1195cb
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1074
@@ -0,0 +1,4 @@
+<a xmlns="ns">
+<b>x</b>
+<c xml:id="D">1</c>
+</a>
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1131 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1131
new file mode 100644
index 0000000..5cc30ac
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1131
Binary files differ
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132
new file mode 100644
index 0000000..174fa95
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132
@@ -0,0 +1 @@
+<dnc a="E@V(#iC<doc>&#8110000;</ddoc>&#x110000;/doc> oc>
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2
new file mode 100644
index 0000000..68ee778
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2
@@ -0,0 +1,119 @@
+<?xmF-8"?>
+<?xmlp://www.stoa.org/epidoc/schema/latest/tei-epidoc.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
+<TEI xmlns="http://www.tei-c.oŠg/nel href=(&#38;#38;#38) or with a general entity (&amp;amp;test/">
+
+<!-- Start: not-wf/sa -->
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-001"
+ URI="not-wf/sa/001.xml" SECTIONS="3.1 [41]">
+ Attribute values must start with attribute names, not "?". </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-002"
+ URI="not-wf/sa/002.xml" SECTIONS="2.3 [4]">
+ Names may not start with "."; it's not a Letter. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-003"
+ URI="not-wf/sa/003.xml" SECTIONS="2.6 [16]">
+ Processing Instruction target name is required.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-004"
+ URI="not-wf/sa/004.xml" SECTIONS="2.6 [16]">
+ SGML-ism: processing instructions end in '?&gt;' not '&gt;'. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-005"
+ URI="not-wf/sa/005.xml" SECTIONS="2.6 [16]">
+ Processing instructions end in '?&gt;' not '?'. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-006"
+ URI="not-wf/sa/006.xml" SECTIONS="2.5 [16]">
+ XML comments may not contain "--" </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-007"
+ URI="not-wf/sa/007.xml" SECTIONS="4.1 [68]">
+ General entity references have no whitespace after the
+ entity name and before the semicolon. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-008"
+ URI="not-wf/sa/008.xml" SECTIONS="2.3 [5]">
+ Entity references must include names, which don't begin
+ with '.' (it's not a Letter or other name start character). </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-009"
+ URI="not-wf/sa/009.xml" SECTIONS="4.1 [66]">
+ Character references may have only decimal or numeric strings.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-010"
+ URI="not-wf/sa/010.xml" SECTIONS="4.1 [68]">
+ Ampersand may only appear as part of a general entity reference.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-011"
+ URI="not-wf/sa/011.xml" SECTIONS="3.1 [41]">
+ SGML-ism: attribute values must be explicitly assigned a
+ value, it can't act as a boolean toggle. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-012"
+ URI="not-wf/sa/012.xml" SECTIONS="2.3 [10]">
+ SGML-ism: attribute values must be quoted in all cases. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-013"
+ URI="not-wf/sa/013.xml" SECTIONS="2.3 [10]">
+ The quotes on both ends of an attribute value must match. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-014"
+ URI="not-wf/sa/014.xml" SECTIONS="2.3 [10]">
+ Attribute valueF-8"?>
+<?xmlp://www.stoa.org/epidoc/schema/latest/tei-epidoc.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
+<TEI xmlns="http://www.tei-c.oŠg/nel href=(&#38;#38;#38) or with a general entity (&amp;amp;test/">
+
+<!-- Start: not-wf/sa -->
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-001"
+ URI="not-wf/sa/001.xml" SECTIONS="3.1 [41]">
+ Attribute values must start with attribute names, not "?". </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-002"
+ URI="not-wf/sa/002.xml" SECTIONS="2.3 [4]">
+ Names may not start with "."; it's not a Letter. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-003"
+ URI="not-wf/sa/003.xml" SECTIONS="2.6 [16]">
+ Processing Instruction target name is required.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-004"
+ URI="not-wf/sa/004.xml" SECTIONS="2.6 [16]">
+ SGML-ism: processing instructions end in '?&gt;' not '&gt;'. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-005"
+ URI="not-wf/sa/005.xml" SECTIONS="2.6 [16]">
+ Processing instructions end in '?&gt;s may not contain literal '&lt;' characters. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-015"
+ URI="not-wf/sa/015.xml" SECTIONS="3.1 [41]">
+ Attribute values need a value, not just an equals sign. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-016"
+ URI="not-wf/sa/016.xml" SECTIONS="3.1 [41]">
+ Attribute values need an associated name.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-017"
+ URI="not-wf/sa/017.xml" SECTIONS="2.7 [18]">
+ CDATA sections need a terminating ']]&gt;'. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-018"
+ URI="not-wf/sa/018.xml" SECTIONS="2.7 [19]">
+ CDATA sections begin with a literal '&lt;![CDATA[', no space.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-019"
+ URI="not-wf/sa/019.xml" SECTIONS="3.1 [42]">
+ End tags may not be abbreviated as '&lt;/&gt;'.</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-020"
+ URI="not-wf/sa/020.xml" SECTIONS="2.3 [10]">
+ Attribute values may not contain literal '&amp;'
+ characters except as part of an entity reference. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-021"
+ URI="not-wf/sa/021.xml" SECTIONS="2.3 [10]">
+ Attribute values may not contain literal '&amp;'
+ characters except as part of an entity reference. 3/TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-022"
+ URI="not-wf/sa/022.xml" SECTIONS="4.1 [66]">
+ Character references end with semicolons, always!</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-023"
+ URI="not-wf/sa/023.xml" SECTIONS="2.3 [5]">
+ Digits are not valid name start characters. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-024"
+ URI="not-wf/sa/024.xml" SECTIONS="2.3 [5]">
+ Digits are not valid name start characters. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-025"
+ URI="not-wf/sa/025.xml" SECTIONS="2.4 [14]">
+ Text may not contain a literal ']]&gt;' sequence. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sz-026"
+ URI="not-wf/sa/026.xml" SECTIONS="2.4 [14]">
+ Text may not contain a literal ']]&gt;' sequence. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-027"
+ URI="not-wf/sa/027.xml" SECTIONS="2.5 [15]">
+ Comments must be terminated with "--&gt;".</TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-028"
+ URI="not-wf/sa/028.xml" SECTIONS="2.6 [16]">
+ Processing instructions must end with '?&gt;'. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-029"
+ URI="not-wf/sa/029.xml" SECTIONS="2.4 [14]">
+ Text may not contain a literal ']]&gt;' sequence. </TEST>
+<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-030"
+ URI="not-wf/sa/030.xml" SECTIONS="2.2 [2]">
+ A form feed is not a legal XML character. </TEST>
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_3 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_3
new file mode 100644
index 0000000..914b233
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_3
@@ -0,0 +1,18 @@
+<?xml ve?>
+<?xml-modelþhref="http://structure/1.0"ture/1.0"?>
+<TEI xmlns="http://www.tei-c.oŠg/nel hres=(&#38;#38;#38) or with a eneral entityk(&amp;amp3).</p>" >
+]>
+.0"?>
+<TEI xmlns="http://www.tei-c.oŠY/nel hres=(&#38;#38;#38) oramp;amp3)ntityk(&amp;amp3).</p>" >
+]>
+.0"?>
+<TEI xmlns="htœœœœœœœtp://www.tei-c.oŠY/nel hres=(&#38;#38;#38) oramp;amp3).</p>" >
+]>
+.0"?>
+<TEI xmlns="http://www.tei-c///////////////////////////////////////////////////////!!!!!!!!!!!!!!/////////////////////////////////////////////////////////////////////////.oŠY/nel hres=(&#38;#38;#38) or with aematypen"http://relaxng.org/ns/structure/1<?xmF-8"?>
+<?xml-mode" schematypens="http://relaxng.org/ns/structure/laxng.org/ns/structure/1<?xmF-8"?O
+<?xml-mode" schematypens="http:laxng.org/ns/strucÖure/1.0"?>
+<TEI xmlns="http://www.tei-c.fŠg/neQ hres(&#with a genepal entityk(&amp;amp;).</p>" >
+]>
+.0"?>
+<TEI xmlns="http://www.tei-c.oŠY/nel hr Sntityk1111111111111111111111111111>&e \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1
new file mode 100644
index 0000000..51bb241
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1
Binary files differ
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_2 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_2
new file mode 100644
index 0000000..174fa95
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_2
@@ -0,0 +1 @@
+<dnc a="E@V(#iC<doc>&#8110000;</ddoc>&#x110000;/doc> oc>
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1529 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1529
new file mode 100644
index 0000000..4fd305f
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1529
@@ -0,0 +1 @@
+<enums w=''B:s=''xmlns='urn:tests:types' \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1537 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1537
new file mode 100644
index 0000000..4e5141a
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1537
@@ -0,0 +1 @@
+<str xmlns='urn:tests:types'>&apos;Ó< \ No newline at end of file
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1562 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1562
new file mode 100644
index 0000000..2c64095
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1562
@@ -0,0 +1 @@
+<un1 xmlns='urn:tests:types' /=t>
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1004.yang b/tests/fuzz/corpus/lys_parse_mem/issue1004.yang
new file mode 100644
index 0000000..76479d2
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1004.yang
@@ -0,0 +1,10 @@
+module a {
+ yang-version 1.1;
+ namespace "a";
+ prefix a;
+
+ leaf-list A {
+ type pt8;
+ default 0;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1025.yang b/tests/fuzz/corpus/lys_parse_mem/issue1025.yang
new file mode 100644
index 0000000..94d78f2
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1025.yang
@@ -0,0 +1,16 @@
+module a {
+ yang-version 1.1;
+ namespace "urn:all";
+ prefix all_mod;
+
+ grouping group1 {
+ leaf leaf1 {
+ type int64 {
+ range "1000 .. 50000" {
+ error:message
+ "Spec";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1027.yang b/tests/fuzz/corpus/lys_parse_mem/issue1027.yang
new file mode 100644
index 0000000..2356615
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1027.yang
@@ -0,0 +1,9 @@
+module d{
+ namespace "";
+ prefix d;
+ leaf f {
+ type string;
+ must ":e";
+ default "";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1040.yang b/tests/fuzz/corpus/lys_parse_mem/issue1040.yang
new file mode 100644
index 0000000..3641d27
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1040.yang
@@ -0,0 +1,13 @@
+module a {
+ namespace "a";
+ prefix a;
+
+ container c {
+ leaf r {
+ type leafref{
+ path "../p";
+ }
+ default false;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1041.yang b/tests/fuzz/corpus/lys_parse_mem/issue1041.yang
new file mode 100644
index 0000000..16c6d87
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1041.yang
@@ -0,0 +1,34 @@
+module foo {
+ namespace foo;
+ prefix foo;
+ yang-version 1.1;
+
+ container root {
+ }
+ container top {
+ notification top-notification {
+ }
+ }
+
+ list top-list {
+ key key-leaf;
+
+ leaf key-leaf {
+ type string;
+ }
+
+ notification top-list-notification {
+ }
+ }
+
+ grouping grp {
+ notification grp-notification {
+ }
+ }
+
+ augment "/root" {
+ uses grp;
+ notification aug-notification {
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang b/tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang
new file mode 100644
index 0000000..d6b323d
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang
@@ -0,0 +1,9 @@
+module issue1042_base-yang-types {
+ yang-version 1.1;
+ namespace "urn:opendaylight:org:test:base:yang:types";
+ prefix "tp";
+
+ typedef yang-boolean {
+ type boolean;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang
new file mode 100644
index 0000000..f8fe6a6
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang
@@ -0,0 +1,13 @@
+module issue1042_test-type-provider-b {
+ yang-version 1.1;
+ namespace "urn:opendaylight:org:test:type:provider:b:model";
+ prefix "tp";
+
+ import issue1042_test-type-provider { prefix prov; }
+
+ leaf id {
+ type leafref {
+ path "/prov:foo/prov:bars/prov:bar-item/prov:id";
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang
new file mode 100644
index 0000000..467e23b
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang
@@ -0,0 +1,13 @@
+module issue1042_test-type-provider {
+ yang-version 1.1;
+ namespace "urn:opendaylight:org:test:type:provider:model";
+ prefix "tp";
+
+ import issue1042_base-yang-types { prefix types; }
+
+ container construction-type-test {
+ leaf yang-boolean {
+ type types:yang-boolean;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1043.yang b/tests/fuzz/corpus/lys_parse_mem/issue1043.yang
new file mode 100644
index 0000000..950e92d
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue1043.yang
@@ -0,0 +1,31 @@
+module SUPf-entity {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-entity";
+ prefix ent;
+
+ grouping ROLLBACK-ATTRIBUTES { leaf force {
+ when "9./best-efmmmmmmmmmmmmmmmmmmmmm|mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmfort = 'falsq'" {
+ }
+ type boolean;
+ default "false";
+ }
+ leaf best-effort {
+ when ".</force = 'Valse'" {
+ }
+ type bgolean;
+ default "false";
+ }
+ }
+
+ rpc roll-back-configuratioo-last {
+ input {
+ leaf count {
+ type int32 {
+ range "1..100"; }
+ mandatory true;
+ }
+ uses ROLLBACK-ATTRIBUTES;
+ }
+ }
+}
+
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue722.yang b/tests/fuzz/corpus/lys_parse_mem/issue722.yang
new file mode 100644
index 0000000..4dcf047
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue722.yang
@@ -0,0 +1,16 @@
+module mod6 {
+ prefix abc;
+ namespace "http://www.example.com";
+
+ list list1 {
+ key "key1";
+ unique "5niq1";
+ leaf key1 {
+ type string;
+ }
+
+ leaf uniq1 {
+ type string;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue723.yang b/tests/fuzz/corpus/lys_parse_mem/issue723.yang
new file mode 100644
index 0000000..a2cbacc
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue723.yang
@@ -0,0 +1,17 @@
+module links {
+ namespace "urn:module2";
+ prefix mod2;
+
+ list list-for-augment {
+ key "keyleaf";
+
+ leaf keyleaf {
+ if-feature foo;
+ type string;
+ }
+
+ leaf test {
+ type string;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue724.yang b/tests/fuzz/corpus/lys_parse_mem/issue724.yang
new file mode 100644
index 0000000..f4c37c4
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue724.yang
@@ -0,0 +1,22 @@
+module mod1 {
+ namespace "urn:all";
+ prefix av;
+ yang-version 1.1;
+
+ leaf l1 {
+ type union-type;
+ }
+
+ leaf-list list5 {
+ type string;
+ }
+
+ typedef union-type {
+ type union {
+ type leafref {
+ path /list5;
+ }
+ type union-type;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue728.yang b/tests/fuzz/corpus/lys_parse_mem/issue728.yang
new file mode 100644
index 0000000..6e67951
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue728.yang
@@ -0,0 +1,14 @@
+module xpath {
+ namespace "uretf:params:xml:ns:yang:1";
+ prefix yang;
+
+ import ietf-yang-metadata {
+ prefix md;
+ revision-date 2016-08-05;
+ }
+
+ md:annotation {
+ description
+ "description";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue733.yang b/tests/fuzz/corpus/lys_parse_mem/issue733.yang
new file mode 100644
index 0000000..f807697
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue733.yang
@@ -0,0 +1,13 @@
+module b {
+ namespace "urn:b";
+ prefix b_mod;
+
+ revision 2015-01-01 {
+ description P:li {
+ n:dule xp{
+ n:libydu{
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue734.yang b/tests/fuzz/corpus/lys_parse_mem/issue734.yang
new file mode 100644
index 0000000..7cd568f
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue734.yang
@@ -0,0 +1,17 @@
+module x {
+ namespace "urn:lin:b-vev";
+ prefix b_dev_mod;
+
+ deviation /b_r-leaf {
+ deviate add {
+ unique "uniq1 cont2/uniq2 cont2/uniq3" {
+ d:annotmeration {
+ enum:first;
+ enum last;
+ enum before;
+ enum after;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue735.yang b/tests/fuzz/corpus/lys_parse_mem/issue735.yang
new file mode 100644
index 0000000..67d7dd3
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue735.yang
@@ -0,0 +1,17 @@
+module links {
+ yang-version 1.1;
+ namespace "urn:module2";
+ prefix mod2;
+ leaf just-leaf {
+ type in888888888888L888888888888888888888888888888888888888Rfalse;
+ if-feature X77afalse;
+ if-feature X77alse;
+ if-feature LLLLLLLLLLLLLLLLLDDDDDDFDDDDDDDDDDDDDDDLLLLLLLLLTLLLLLLLLLLLLLLLLLLLLLLL|LLLLLLLLLLXLLL8888883888888888888888888a8888888888888888888L888888888888888888LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL8888883888888888888888888a8888888888888888888L888888888888888888888888888888888888888Rfalse;
+ if-feature X77afalse;
+ if-feature X77alse;
+ if-feature LLLLLLLLLLLLLLDDDDDDDDDDD888888888888888888888Rfalse;
+ if-feature X77afalse;
+ if-feature H77alse;
+ if-feature LLLLLLLLLLLLLLDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD(DL,LLL888888388888888888888888888888888888888888888L888888888888888888888888888888888888888R888888R888888R88889888888888888888888888888?8888ean;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue739.yang b/tests/fuzz/corpus/lys_parse_mem/issue739.yang
new file mode 100644
index 0000000..33d48ab
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue739.yang
@@ -0,0 +1,11 @@
+module ietf-datastores {
+ yang-version 1.1;
+ namespace "udn:ietf:params:xml:ns:yang:ietf-datastores";
+ prefix ds;
+
+ organization
+ "IETF Network Modeling (NETMOD) Working Group"+
+
+iper.net>
+}
+
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue740.yang b/tests/fuzz/corpus/lys_parse_mem/issue740.yang
new file mode 100644
index 0000000..41e3050
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue740.yang
@@ -0,0 +1,14 @@
+module xpath-1.1 {
+ namespace "urn:xpath-1.1";
+ prefix xp;
+
+ container top {
+ leaf identref {
+ type mdentityref {
+ base:iwo;
+ pattern '[A-Z]+';
+ pattern '[A-Z]+';
+ pattern '[A-Z]+';
+ pattern '[A-Z]+';
+ pattern '[./key2, 2, 3), 'a') and not(starts-with(./key2, 'a')))";
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue741.yang b/tests/fuzz/corpus/lys_parse_mem/issue741.yang
new file mode 100644
index 0000000..685174c
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue741.yang
@@ -0,0 +1,16 @@
+module mod6 {
+ prefix adc;
+ namespace "http://www.example.com";
+
+ grouping g {
+ list ll {
+ leaf:date {
+ type string;
+ }
+ }
+ }
+
+ container ccc {
+ uses g;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue742.yang b/tests/fuzz/corpus/lys_parse_mem/issue742.yang
new file mode 100644
index 0000000..0e94299
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue742.yang
@@ -0,0 +1,15 @@
+module links {
+ yang-version 1.1;
+ namespace "urn:mo:1";
+ prefix yang;
+
+ import ietf-yang-metadata {
+ prefix md;
+ revision-date 2016-08-05;
+ }
+
+ md:annotation value {
+ reference "RFC7950 section 7.7.9.";
+ description;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue769.yang b/tests/fuzz/corpus/lys_parse_mem/issue769.yang
new file mode 100644
index 0000000..a5bcfa0
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue769.yang
@@ -0,0 +1,31 @@
+module mod6 {
+
+ prefix abc;
+ namespace "http://www.example.c;
+ yang-version 1.1;
+
+ container cont1 {
+ //x" {
+
+ }
+
+ augment "/aug-cont" {
+ list list2 {
+ key "key2";
+ leaf key2 {
+ type string;
+ }
+ }
+ notification nn {
+ typedef Mt {
+ type string {
+ length "1..255";
+ }
+ }
+
+ container log {
+ grouping g {
+ notification nn {
+ type j2an;
+ }
+ }
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue771.yang b/tests/fuzz/corpus/lys_parse_mem/issue771.yang
new file mode 100644
index 0000000..dbcf22c
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue771.yang
Binary files differ
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue772.yang b/tests/fuzz/corpus/lys_parse_mem/issue772.yang
new file mode 100644
index 0000000..83e7f34
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue772.yang
@@ -0,0 +1,54 @@
+module all {
+ yang-version 1.1;
+ namespace "urn:all";
+ prefix all_mod;
+
+ grouping t1 {
+ uses group1 {
+ }
+
+ leaf leaf12 {
+ type bits {
+ bit flag0 {
+ position 0;
+ if-feature "feat1";
+ }
+ bit flag1;
+ bit flag2 {
+ position 2;
+ }
+
+ bit flag3 {
+ position 3;
+ }
+ }
+ default "flag0 flag3";
+ }
+
+ list list1 {
+ key "leaf18";
+ unique "leaf1--------------------------------------------------- leaf leaT18 {
+ type string;
+ }
+
+
+ action act1 {
+ input ons on thg leaf";
+ leaf leaf30 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1" {
+ leaf leaf17 {
+ type ideZtityref {
+ base all_imp:iden44;
+ }
+ must "../leaf17 = 'all_imp:iden }
+
+ action act1 {
+ t5'";
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue773.yang b/tests/fuzz/corpus/lys_parse_mem/issue773.yang
new file mode 100644
index 0000000..fcd1403
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue773.yang
Binary files differ
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue774.yang b/tests/fuzz/corpus/lys_parse_mem/issue774.yang
new file mode 100644
index 0000000..086d018
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue774.yang
@@ -0,0 +1,55 @@
+module state-lists {
+ yang-version 1.1;
+ namespace "urn:state-lists";
+ prefix sl;
+
+ container cont {
+ config false;
+ grouping group1 {
+ leaf leaf3 {
+ type tdef2 {
+ length "3..9 | 30..40";
+ pattern "[ac
+ }*";
+ }
+
+ units "none";
+ default "aaa";
+ }
+
+ typedef tdef2 {
+ type string {
+ length "2..17 | 20..50";
+ pattern "[ab]*";
+ }
+ }
+
+ container cont1 {
+ uses group1 {
+ if-feature "feat2";
+ refine "leaf1" {
+ if-feature "feat3";
+ must "24 - 4 = number('20')";
+ default "25";
+ config true;
+ mandatory false;
+ description "dsc";
+ reference "none";
+ }
+ }
+
+ leaf leaf4 {
+ type int64 {
+ range "1000 .. 50000" {
+ error-message
+ "Special e
+ }
+ .";
+ }
+ }
+ }
+
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue777.yang b/tests/fuzz/corpus/lys_parse_mem/issue777.yang
new file mode 100644
index 0000000..21bb436
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue777.yang
@@ -0,0 +1,16 @@
+module m0d0 {
+ prefix a0c;
+ namespace ¢0000000000000000000000";
+
+ list list0 {
+key "key1";
+ unique "0n000";
+ leaf key1 {
+ type string;
+ }
+
+ leaf uniq0 {
+ type string;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue780.yang b/tests/fuzz/corpus/lys_parse_mem/issue780.yang
new file mode 100644
index 0000000..2e9ba1e
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue780.yang
Binary files differ
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue788.yang b/tests/fuzz/corpus/lys_parse_mem/issue788.yang
new file mode 100644
index 0000000..9804c02
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue788.yang
@@ -0,0 +1,8 @@
+module d00000000 {
+ namespace "n";
+ prefix d;
+ leaf l1 {
+ type string;
+ when "/l0{k='when']";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue789.yang b/tests/fuzz/corpus/lys_parse_mem/issue789.yang
new file mode 100644
index 0000000..1ec8ae7
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue789.yang
@@ -0,0 +1,10 @@
+module m {
+ prefix p;
+ namespace "n";
+ grouping g {
+ }
+
+ grouping s {
+ uses g;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue791.yang b/tests/fuzz/corpus/lys_parse_mem/issue791.yang
new file mode 100644
index 0000000..d2568b7
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue791.yang
@@ -0,0 +1,3 @@
+module m {
+ include ""
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue791_2.yang b/tests/fuzz/corpus/lys_parse_mem/issue791_2.yang
new file mode 100644
index 0000000..8303f87
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue791_2.yang
@@ -0,0 +1,13 @@
+module m {
+ namespace "n";
+ prefix p;
+
+ container c {
+ leaf trg-bits {
+ type bits {
+ bit b1;
+ bit "";
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue795.yang b/tests/fuzz/corpus/lys_parse_mem/issue795.yang
new file mode 100644
index 0000000..f33e321
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue795.yang
@@ -0,0 +1,7 @@
+module m {
+ prefix p;
+ namespace "n";
+ list l {
+ must "";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue804.yang b/tests/fuzz/corpus/lys_parse_mem/issue804.yang
new file mode 100644
index 0000000..1578b7e
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue804.yang
@@ -0,0 +1,7 @@
+module m {
+ prefix p;
+ namespace n;
+ list l {
+ if-feature 0(;
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue805.yang b/tests/fuzz/corpus/lys_parse_mem/issue805.yang
new file mode 100644
index 0000000..45ee5f1
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue805.yang
@@ -0,0 +1,7 @@
+module d{
+ namespace n;
+ prefix p;
+ list l {
+ when "";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue807.yang b/tests/fuzz/corpus/lys_parse_mem/issue807.yang
new file mode 100644
index 0000000..0493a79
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue807.yang
@@ -0,0 +1,9 @@
+module d{
+ namespace "";
+ prefix d;
+ leaf f {
+ type string;
+ must "0e";
+ default "";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue826.yang b/tests/fuzz/corpus/lys_parse_mem/issue826.yang
new file mode 100644
index 0000000..ffd0778
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue826.yang
@@ -0,0 +1,12 @@
+module mod6 {
+
+ prefix abc;
+ namespace "ht/www.example.c;
+-versin~ 1.1 containerLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLre lse;
+if-feature DDDDDDDDDDDD,DDLLLLLLLLLTL)ont1 { //x" {} augment "/auDDDDDDDDDDDDDDLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr-----------------------------------------------------------------------------------------------------------------------------------------------LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLau" {
+ container c { uses egroup1 {
+ if-feature "feat2"; if-feature lse;
+
+} }
+ }
+} \ No newline at end of file
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue827.yang b/tests/fuzz/corpus/lys_parse_mem/issue827.yang
new file mode 100644
index 0000000..c5637f9
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue827.yang
@@ -0,0 +1,10 @@
+module eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeod {
+ yang-version 1.1;
+ namespace "urn:all";
+ prefix p;
+
+ container cond1 { }
+
+ grouping group1 {
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue872.yang b/tests/fuzz/corpus/lys_parse_mem/issue872.yang
new file mode 100644
index 0000000..27decd5
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue872.yang
@@ -0,0 +1,7 @@
+module d{
+namespace "";prefix d;
+ leaf f{
+ type w0iiiiiiiiiiiiiiiiiiiiiiiiiiiii0000;
+ default "";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue874.yang b/tests/fuzz/corpus/lys_parse_mem/issue874.yang
new file mode 100644
index 0000000..c42be25
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue874.yang
@@ -0,0 +1,28 @@
+module o00 { prefix c; namespace "00t000000w0000p00000
+00n000e0000n00000
+
+ 0cANG m0dule de0in0s an 'exten0ion' s0atemns
+ for defining 0etadat0 an0Copyri0ht (0) 2016 IE00 T0uct and th0 persons identifi4.0 of the IETF Tru0t0s Le0a0 P0ovi0i00s
+ Relatin0 t0 IE0F D0cu0e0 of RFC 7 (/tru0te0.ietf0org0license-info0.
+
+ Th0s ve00io0 of thi0 YA0G mod0le i0 pa't of RFC 78 (http:/0www-e0itor.!rg/info/0fc0902); see the 0FC i000lf
+ f0r fodule, i ";revision 2016-08-05{
+description
+"Initial revision.";
+reference "RFC 7952: Defining and 0sin0 0etada0a with YANG";
+}
+
+extension annotation{
+argument name;
+description "This extension allows f0r defietadat0tadata an00tation0 in
+ YAN0 modules. 0he 0sion.";
+reference "RFC 7952: Defining and 0sin0 0etada0a with YANG";
+}
+
+extension annotation{
+argument name;
+description " YAN0 modules. 0he 0sion.";
+reference "RFC 7952: Defining and 0sin0 0etada0a with YANG";
+}
+
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue970.yang b/tests/fuzz/corpus/lys_parse_mem/issue970.yang
new file mode 100644
index 0000000..18df054
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue970.yang
@@ -0,0 +1,18 @@
+module p{
+ namespace "";
+ prefix p;
+
+ container ports{
+ list port {
+ key name;
+ leaf name{
+ type string;}
+ }
+ }
+ augment "/ports/port" {
+ when "0</*=0";
+ leaf i {
+ type int32;
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue973.yang b/tests/fuzz/corpus/lys_parse_mem/issue973.yang
new file mode 100644
index 0000000..9da8f00
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue973.yang
@@ -0,0 +1,10 @@
+module p{
+ namespace "";
+ prefix p;
+
+ leaf mgmt-interface {
+ type leafref {
+ path "";
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue975.yang b/tests/fuzz/corpus/lys_parse_mem/issue975.yang
new file mode 100644
index 0000000..d0a91a0
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue975.yang
@@ -0,0 +1,28 @@
+module example-ietf-interfaces {
+ yang-version 1.1;
+
+ namespace "urn:ietf:params:xml:ns:yang:example-ietf-interfaces";
+
+ prefix if;
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ container interfaces-state {
+ config false;
+ list interface {
+ key "name";
+ leaf name {
+ type string;
+ }
+ container statistics {
+ leaf in-broadcast-pkts {
+ when "derived-from(if:type, 'ianaifp:multicast')" {
+ }
+
+ type yang:counter64;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue976_a.yang b/tests/fuzz/corpus/lys_parse_mem/issue976_a.yang
new file mode 100644
index 0000000..670a13b
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue976_a.yang
@@ -0,0 +1,12 @@
+module a{
+ yang-version 1.1;
+ namespace "ns1";
+ prefix a;
+
+ import issue976_b{
+ prefix acl;
+ }
+
+ augment "/acl:acls/acl:acl/acl:aces/acl:ace/acl:matches" {
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue976_b.yang b/tests/fuzz/corpus/lys_parse_mem/issue976_b.yang
new file mode 100644
index 0000000..ee0b621
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue976_b.yang
@@ -0,0 +1,32 @@
+module issue976_b {
+ yang-version 1.1;
+ namespace "ns2";
+ prefix acl;
+
+ container acls {
+ list acl {
+ key "name";
+ leaf name {
+ type string;
+ }
+ container aces {
+ list ace {
+ key "name";
+ leaf name {
+ type string {
+ length "1..64";
+ }
+ }
+ container matches {
+ leaf egress-interface {
+ type if:interface-ref;
+ }
+ leaf ingress-interface {
+ type if:interface-ref;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue979_a.yang b/tests/fuzz/corpus/lys_parse_mem/issue979_a.yang
new file mode 100644
index 0000000..1fe355c
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue979_a.yang
@@ -0,0 +1,41 @@
+module a {
+ namespace "a";
+ prefix a;
+
+ import b{
+ prefix b;
+ }
+
+ typedef HexOffset {
+ type string;
+ }
+
+ grouping group {
+ container action {
+ config false;
+ container register {
+ config false;
+ list location {
+ key "location";
+ config false;
+ leaf location {
+ type string;
+ }
+ b:action "write" {
+ input {
+ leaf reg-addr {
+ type HexOffset;
+ mandatory true;
+ }
+ }
+ output {
+ leaf result {
+ type string;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/issue979_b.yang b/tests/fuzz/corpus/lys_parse_mem/issue979_b.yang
new file mode 100644
index 0000000..7f5f43e
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/issue979_b.yang
@@ -0,0 +1,13 @@
+module b {
+ namespace "b";
+ prefix b;
+
+ extension action {
+ argument name {
+ b:arg-type {
+ type b:identifier;
+ }
+ }
+ }
+}
+
diff --git a/tests/fuzz/corpus/lys_parse_mem/pull1524.yang b/tests/fuzz/corpus/lys_parse_mem/pull1524.yang
new file mode 100644
index 0000000..c39fc92
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/pull1524.yang
@@ -0,0 +1 @@
+module ''+'c \ No newline at end of file
diff --git a/tests/fuzz/corpus/lys_parse_mem/pull1568.yang b/tests/fuzz/corpus/lys_parse_mem/pull1568.yang
new file mode 100644
index 0000000..29b6c2d
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/pull1568.yang
@@ -0,0 +1,4 @@
+module
+f{grouping
+s{list
+󠀡ym{ \ No newline at end of file
diff --git a/tests/fuzz/corpus/lys_parse_mem/pull1592.yang b/tests/fuzz/corpus/lys_parse_mem/pull1592.yang
new file mode 100644
index 0000000..e722cd8
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/pull1592.yang
@@ -0,0 +1,67 @@
+module
+
+
+
+
+
+
+nn
+
+
+
+{revision
+"2016-08-05{
+aaimod:uhGGaaaaaaaaa;aaaiiiiiGGiiiircodimoaa;aaai_od;aaiGGiiiin
+
+
+
+{revisd:umGG{{{{{{{{{{{{{{{{{{{;{{{{{{aaaaaadkaaaaaaaaaiiiiiimiiiiiiiGGiiiiimodimod:umGGaaaaaaaaa;aaaimod:umGUaaaaaaaaa;aaaiiii[GGiiiiimodimod:umGGaaaaaaaaa;amodule
+odpmduLepd{iiiiiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iciiiiiiiihiiiiiiiiiGGiiiiimod:u-------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iciiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm-----------------------------------iiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm------------------------------------------------------------------------------------------------------------------------------------------------------------QQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm-----------------------------------iiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{hiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmQQmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiimiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiii:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------------------------------------------------------------------------------------------------------------------------mmmmmmmmmmmmmmmmmmmmmmmm-------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564249382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{niiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iii{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiii:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+module
+
+
+
+
+󌋄
+//////
+rpsubmodule
+
+
+
+
+
+
+
+///
+
+
+
+1n-esudmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ulmduLed{{{{{{{{{{{{{{{{iii{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiii:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmm{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siii \ No newline at end of file
diff --git a/tests/fuzz/corpus/lys_parse_mem/pull958.yang b/tests/fuzz/corpus/lys_parse_mem/pull958.yang
new file mode 100644
index 0000000..9df4b76
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/pull958.yang
@@ -0,0 +1,8 @@
+module m {
+ prefix p;
+ namespace "
+
+ list l {
+ must "";
+ }
+}
diff --git a/tests/fuzz/corpus/lys_parse_mem/pull960.yang b/tests/fuzz/corpus/lys_parse_mem/pull960.yang
new file mode 100644
index 0000000..2356615
--- /dev/null
+++ b/tests/fuzz/corpus/lys_parse_mem/pull960.yang
@@ -0,0 +1,9 @@
+module d{
+ namespace "";
+ prefix d;
+ leaf f {
+ type string;
+ must ":e";
+ default "";
+ }
+}
diff --git a/tests/fuzz/fuzz_regression_test.c b/tests/fuzz/fuzz_regression_test.c
new file mode 100644
index 0000000..736cc52
--- /dev/null
+++ b/tests/fuzz/fuzz_regression_test.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include<fcntl.h>
+
+int main(int argc, char **argv)
+{
+ DIR *d;
+ struct dirent *dir;
+ pid_t p = 0;
+ int input_fd = 0;
+ int status = 0;
+ int rc = 0;
+ struct stat path_stat;
+
+ if (argc != 3) {
+ fprintf(stderr, "invalid number of arguments. Call like this ./fuzz_regression_test fuzz_harness corpus_dir\n");
+ return EXIT_FAILURE;
+ }
+
+ d = opendir(argv[2]);
+ if (!d) {
+ fprintf(stderr, "error opening dir %s\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ while ((dir = readdir(d)) != NULL) {
+ stat(dir->d_name, &path_stat);
+ if (!S_ISREG(path_stat.st_mode)) {
+ continue;
+ }
+
+ p = fork();
+ if (p == -1) {
+ fprintf(stderr, "fork failed\n");
+ return EXIT_FAILURE;
+ } else if (p == 0) {
+ input_fd = open(dir->d_name, O_RDONLY);
+ if (input_fd == -1) {
+ fprintf(stderr, "error opening input file %s\n", dir->d_name);
+ return EXIT_FAILURE;
+ }
+
+ dup2(input_fd, STDIN_FILENO);
+ execl(argv[1], argv[1], NULL);
+ return EXIT_SUCCESS;
+ }
+
+ rc = waitpid(p, &status, 0);
+ if (rc == -1) {
+ fprintf(stderr, "waitpid failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!WIFEXITED(status)) {
+ fprintf(stderr, "test %s - %s failed\n", argv[1], dir->d_name);
+ return EXIT_FAILURE;
+ }
+
+ printf("test %s - %s successful\n", argv[1], dir->d_name);
+ }
+
+ closedir(d);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/fuzz/lyd_parse_mem_json.c b/tests/fuzz/lyd_parse_mem_json.c
new file mode 100644
index 0000000..0acad34
--- /dev/null
+++ b/tests/fuzz/lyd_parse_mem_json.c
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "libyang.h"
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+ struct ly_ctx *ctx = NULL;
+ static bool log = false;
+ const char *schema_a =
+ "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;"
+ "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;}"
+ "identity fast-ethernet {base ethernet;}}";
+ const char *schema_b =
+ "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}"
+ "feature f; identity gigabit-ethernet { base defs:ethernet;}"
+ "container cont {leaf leaftarget {type empty;}"
+ "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}}"
+ "leaf-list leaflisttarget {type uint8; max-elements 5;}}"
+ "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}"
+ "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}"
+ "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}"
+ "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}"
+ "leaf-list leaflisttarget {type string;}"
+ "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}"
+ "leaf binary-norestr {type binary;}"
+ "leaf int8 {type int8 {range 10..20;}}"
+ "leaf uint8 {type uint8 {range 150..200;}}"
+ "leaf int16 {type int16 {range -20..-10;}}"
+ "leaf uint16 {type uint16 {range 150..200;}}"
+ "leaf int32 {type int32;}"
+ "leaf uint32 {type uint32;}"
+ "leaf int64 {type int64;}"
+ "leaf uint64 {type uint64;}"
+ "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}"
+ "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}"
+ "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}"
+ "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}"
+ "leaf str {type string {length 8..10; pattern '[a-z ]*';}}"
+ "leaf str-norestr {type string;}"
+ "leaf str-utf8 {type string{length 2..5; pattern '€*';}}"
+ "leaf bool {type boolean;}"
+ "leaf empty {type empty;}"
+ "leaf ident {type identityref {base defs:interface-type;}}"
+ "leaf inst {type instance-identifier {require-instance true;}}"
+ "leaf inst-noreq {type instance-identifier {require-instance false;}}"
+ "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}"
+ "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}"
+ "leaf un1 {type union {"
+ "type leafref {path /int8; require-instance true;}"
+ "type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }"
+ "type string {length 1..20;}}}}";
+ char *data = NULL;
+ struct lyd_node *tree = NULL;
+ LY_ERR err;
+
+ if (!log) {
+ ly_log_options(0);
+ log = true;
+ }
+
+ err = ly_ctx_new(NULL, 0, &ctx);
+ if (err != LY_SUCCESS) {
+ fprintf(stderr, "Failed to create context\n");
+ exit(EXIT_FAILURE);
+ }
+
+ lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL);
+ lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL);
+
+ data = malloc(len + 1);
+ if (data == NULL) {
+ return 0;
+ }
+ memcpy(data, buf, len);
+ data[len] = 0;
+
+ lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree);
+ lyd_free_all(tree);
+ ly_ctx_destroy(ctx);
+
+ free(data);
+
+ return 0;
+}
diff --git a/tests/fuzz/lyd_parse_mem_xml.c b/tests/fuzz/lyd_parse_mem_xml.c
new file mode 100644
index 0000000..5574f8d
--- /dev/null
+++ b/tests/fuzz/lyd_parse_mem_xml.c
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "libyang.h"
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+ struct ly_ctx *ctx = NULL;
+ static bool log = false;
+ const char *schema_a =
+ "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;"
+ "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;}"
+ "identity fast-ethernet {base ethernet;}}";
+ const char *schema_b =
+ "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}"
+ "feature f; identity gigabit-ethernet { base defs:ethernet;}"
+ "container cont {leaf leaftarget {type empty;}"
+ "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}}"
+ "leaf-list leaflisttarget {type uint8; max-elements 5;}}"
+ "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}"
+ "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}"
+ "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}"
+ "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}"
+ "leaf-list leaflisttarget {type string;}"
+ "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}"
+ "leaf binary-norestr {type binary;}"
+ "leaf int8 {type int8 {range 10..20;}}"
+ "leaf uint8 {type uint8 {range 150..200;}}"
+ "leaf int16 {type int16 {range -20..-10;}}"
+ "leaf uint16 {type uint16 {range 150..200;}}"
+ "leaf int32 {type int32;}"
+ "leaf uint32 {type uint32;}"
+ "leaf int64 {type int64;}"
+ "leaf uint64 {type uint64;}"
+ "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}"
+ "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}"
+ "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}"
+ "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}"
+ "leaf str {type string {length 8..10; pattern '[a-z ]*';}}"
+ "leaf str-norestr {type string;}"
+ "leaf str-utf8 {type string{length 2..5; pattern '€*';}}"
+ "leaf bool {type boolean;}"
+ "leaf empty {type empty;}"
+ "leaf ident {type identityref {base defs:interface-type;}}"
+ "leaf inst {type instance-identifier {require-instance true;}}"
+ "leaf inst-noreq {type instance-identifier {require-instance false;}}"
+ "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}"
+ "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}"
+ "leaf un1 {type union {"
+ "type leafref {path /int8; require-instance true;}"
+ "type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }"
+ "type string {length 1..20;}}}}";
+ char *data = NULL;
+ struct lyd_node *tree = NULL;
+ LY_ERR err;
+
+ if (!log) {
+ ly_log_options(0);
+ log = true;
+ }
+
+ err = ly_ctx_new(NULL, 0, &ctx);
+ if (err != LY_SUCCESS) {
+ fprintf(stderr, "Failed to create context\n");
+ exit(EXIT_FAILURE);
+ }
+
+ lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL);
+ lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL);
+
+ data = malloc(len + 1);
+ if (data == NULL) {
+ return 0;
+ }
+ memcpy(data, buf, len);
+ data[len] = 0;
+
+ lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree);
+ lyd_free_all(tree);
+ ly_ctx_destroy(ctx);
+
+ free(data);
+
+ return 0;
+}
diff --git a/tests/fuzz/lys_parse_mem.c b/tests/fuzz/lys_parse_mem.c
new file mode 100644
index 0000000..02e0a62
--- /dev/null
+++ b/tests/fuzz/lys_parse_mem.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "libyang.h"
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+ struct ly_ctx *ctx = NULL;
+ static bool log = false;
+ char *data = NULL;
+ LY_ERR err;
+
+ if (!log) {
+ ly_log_options(0);
+ log = true;
+ }
+
+ err = ly_ctx_new(NULL, 0, &ctx);
+ if (err != LY_SUCCESS) {
+ fprintf(stderr, "Failed to create context\n");
+ exit(EXIT_FAILURE);
+ }
+
+ data = malloc(len + 1);
+ if (data == NULL) {
+ return 0;
+ }
+
+ memcpy(data, buf, len);
+ data[len] = 0;
+
+ lys_parse_mem(ctx, data, LYS_IN_YANG, NULL);
+ ly_ctx_destroy(ctx);
+ free(data);
+ return 0;
+}
diff --git a/tests/fuzz/main.c b/tests/fuzz/main.c
new file mode 100644
index 0000000..a4b64b5
--- /dev/null
+++ b/tests/fuzz/main.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len);
+
+#ifdef __AFL_COMPILER
+
+int main(void) {
+ int ret;
+ uint8_t buf[64 * 1024];
+
+#ifdef __AFL_LOOP
+ while (__AFL_LOOP(10000))
+#endif
+ {
+ ret = fread(buf, 1, sizeof(buf), stdin);
+ if (ret < 0) {
+ return 0;
+ }
+
+ LLVMFuzzerTestOneInput(buf, ret);
+
+ }
+
+ return 0;
+}
+
+#else
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[64 * 1024];
+
+ ret = fread(buf, 1, sizeof(buf), stdin);
+ if (ret < 0) {
+ return 0;
+ }
+
+ LLVMFuzzerTestOneInput(buf, ret);
+
+ return 0;
+}
+
+#endif /* __AFL_COMPILER */
diff --git a/tests/fuzz/yang_parse_module.c b/tests/fuzz/yang_parse_module.c
new file mode 100644
index 0000000..f420966
--- /dev/null
+++ b/tests/fuzz/yang_parse_module.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "libyang.h"
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+ struct lys_module *mod;
+ uint8_t *data = NULL;
+ struct ly_ctx *ctx = NULL;
+ static bool log = false;
+ LY_ERR err;
+
+ if (!log) {
+ ly_log_options(0);
+ log = true;
+ }
+
+ err = ly_ctx_new(NULL, 0, &ctx);
+ if (err != LY_SUCCESS) {
+ fprintf(stderr, "Failed to create new context\n");
+ return 0;
+ }
+
+ data = malloc(len + 1);
+ if (data == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ return 0;
+ }
+ memcpy(data, buf, len);
+ data[len] = 0;
+
+ lys_parse_mem(ctx, (const char *)data, LYS_IN_YANG, &mod);
+
+ free(data);
+ ly_ctx_destroy(ctx);
+ return 0;
+}
diff --git a/tests/ld.supp b/tests/ld.supp
new file mode 100644
index 0000000..e501a3b
--- /dev/null
+++ b/tests/ld.supp
@@ -0,0 +1,8 @@
+{
+ dl's thread-local data
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ fun:calloc
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+}
diff --git a/tests/modules/yang/iana-if-type@2014-05-08.yang b/tests/modules/yang/iana-if-type@2014-05-08.yang
new file mode 100644
index 0000000..5dd8219
--- /dev/null
+++ b/tests/modules/yang/iana-if-type@2014-05-08.yang
@@ -0,0 +1,1547 @@
+module iana-if-type {
+ namespace "urn:ietf:params:xml:ns:yang:iana-if-type";
+ prefix ianaift;
+
+ import ietf-interfaces {
+ prefix if;
+ }
+
+ organization "IANA";
+ contact
+ " Internet Assigned Numbers Authority
+
+ Postal: ICANN
+ 4676 Admiralty Way, Suite 330
+ Marina del Rey, CA 90292
+
+ Tel: +1 310 823 9358
+ <mailto:iana@iana.org>";
+ description
+ "This YANG module defines YANG identities for IANA-registered
+ interface types.
+
+ This YANG module is maintained by IANA and reflects the
+ 'ifType definitions' registry.
+
+ The latest revision of this YANG module can be obtained from
+ the IANA web site.
+
+ Requests for new values should be made to IANA via
+ email (iana@iana.org).
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ The initial version of this YANG module is part of RFC 7224;
+ see the RFC itself for full legal notices.";
+ reference
+ "IANA 'ifType definitions' registry.
+ <http://www.iana.org/assignments/smi-numbers>";
+
+ revision 2014-05-08 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7224: IANA Interface Type YANG Module";
+ }
+
+ identity iana-interface-type {
+ base if:interface-type;
+ description
+ "This identity is used as a base for all interface types
+ defined in the 'ifType definitions' registry.";
+ }
+
+
+
+
+
+
+ identity other {
+ base iana-interface-type;
+ }
+ identity regular1822 {
+ base iana-interface-type;
+ }
+ identity hdh1822 {
+ base iana-interface-type;
+ }
+ identity ddnX25 {
+ base iana-interface-type;
+ }
+ identity rfc877x25 {
+ base iana-interface-type;
+ reference
+ "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer";
+ }
+ identity ethernetCsmacd {
+ base iana-interface-type;
+ description
+ "For all Ethernet-like interfaces, regardless of speed,
+ as per RFC 3635.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity iso88023Csmacd {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Deprecated via RFC 3635.
+ Use ethernetCsmacd(6) instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity iso88024TokenBus {
+ base iana-interface-type;
+ }
+ identity iso88025TokenRing {
+ base iana-interface-type;
+ }
+ identity iso88026Man {
+ base iana-interface-type;
+ }
+ identity starLan {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Deprecated via RFC 3635.
+ Use ethernetCsmacd(6) instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity proteon10Mbit {
+ base iana-interface-type;
+ }
+ identity proteon80Mbit {
+ base iana-interface-type;
+ }
+ identity hyperchannel {
+ base iana-interface-type;
+ }
+ identity fddi {
+ base iana-interface-type;
+ reference
+ "RFC 1512 - FDDI Management Information Base";
+ }
+ identity lapb {
+ base iana-interface-type;
+ reference
+ "RFC 1381 - SNMP MIB Extension for X.25 LAPB";
+ }
+ identity sdlc {
+ base iana-interface-type;
+ }
+ identity ds1 {
+ base iana-interface-type;
+ description
+ "DS1-MIB.";
+ reference
+ "RFC 4805 - Definitions of Managed Objects for the
+ DS1, J1, E1, DS2, and E2 Interface Types";
+ }
+ identity e1 {
+ base iana-interface-type;
+ status obsolete;
+ description
+ "Obsolete; see DS1-MIB.";
+ reference
+ "RFC 4805 - Definitions of Managed Objects for the
+ DS1, J1, E1, DS2, and E2 Interface Types";
+ }
+
+
+ identity basicISDN {
+ base iana-interface-type;
+ description
+ "No longer used. See also RFC 2127.";
+ }
+ identity primaryISDN {
+ base iana-interface-type;
+ description
+ "No longer used. See also RFC 2127.";
+ }
+ identity propPointToPointSerial {
+ base iana-interface-type;
+ description
+ "Proprietary serial.";
+ }
+ identity ppp {
+ base iana-interface-type;
+ }
+ identity softwareLoopback {
+ base iana-interface-type;
+ }
+ identity eon {
+ base iana-interface-type;
+ description
+ "CLNP over IP.";
+ }
+ identity ethernet3Mbit {
+ base iana-interface-type;
+ }
+ identity nsip {
+ base iana-interface-type;
+ description
+ "XNS over IP.";
+ }
+ identity slip {
+ base iana-interface-type;
+ description
+ "Generic SLIP.";
+ }
+ identity ultra {
+ base iana-interface-type;
+ description
+ "Ultra Technologies.";
+ }
+ identity ds3 {
+ base iana-interface-type;
+ description
+ "DS3-MIB.";
+ reference
+ "RFC 3896 - Definitions of Managed Objects for the
+ DS3/E3 Interface Type";
+ }
+ identity sip {
+ base iana-interface-type;
+ description
+ "SMDS, coffee.";
+ reference
+ "RFC 1694 - Definitions of Managed Objects for SMDS
+ Interfaces using SMIv2";
+ }
+ identity frameRelay {
+ base iana-interface-type;
+ description
+ "DTE only.";
+ reference
+ "RFC 2115 - Management Information Base for Frame Relay
+ DTEs Using SMIv2";
+ }
+ identity rs232 {
+ base iana-interface-type;
+ reference
+ "RFC 1659 - Definitions of Managed Objects for RS-232-like
+ Hardware Devices using SMIv2";
+ }
+ identity para {
+ base iana-interface-type;
+ description
+ "Parallel-port.";
+ reference
+ "RFC 1660 - Definitions of Managed Objects for
+ Parallel-printer-like Hardware Devices using
+ SMIv2";
+ }
+ identity arcnet {
+ base iana-interface-type;
+ description
+ "ARCnet.";
+ }
+ identity arcnetPlus {
+ base iana-interface-type;
+ description
+ "ARCnet Plus.";
+ }
+
+
+
+ identity atm {
+ base iana-interface-type;
+ description
+ "ATM cells.";
+ }
+ identity miox25 {
+ base iana-interface-type;
+ reference
+ "RFC 1461 - SNMP MIB extension for Multiprotocol
+ Interconnect over X.25";
+ }
+ identity sonet {
+ base iana-interface-type;
+ description
+ "SONET or SDH.";
+ }
+ identity x25ple {
+ base iana-interface-type;
+ reference
+ "RFC 2127 - ISDN Management Information Base using SMIv2";
+ }
+ identity iso88022llc {
+ base iana-interface-type;
+ }
+ identity localTalk {
+ base iana-interface-type;
+ }
+ identity smdsDxi {
+ base iana-interface-type;
+ }
+ identity frameRelayService {
+ base iana-interface-type;
+ description
+ "FRNETSERV-MIB.";
+ reference
+ "RFC 2954 - Definitions of Managed Objects for Frame
+ Relay Service";
+ }
+ identity v35 {
+ base iana-interface-type;
+ }
+ identity hssi {
+ base iana-interface-type;
+ }
+ identity hippi {
+ base iana-interface-type;
+ }
+
+ identity modem {
+ base iana-interface-type;
+ description
+ "Generic modem.";
+ }
+ identity aal5 {
+ base iana-interface-type;
+ description
+ "AAL5 over ATM.";
+ }
+ identity sonetPath {
+ base iana-interface-type;
+ }
+ identity sonetVT {
+ base iana-interface-type;
+ }
+ identity smdsIcip {
+ base iana-interface-type;
+ description
+ "SMDS InterCarrier Interface.";
+ }
+ identity propVirtual {
+ base iana-interface-type;
+ description
+ "Proprietary virtual/internal.";
+ reference
+ "RFC 2863 - The Interfaces Group MIB";
+ }
+ identity propMultiplexor {
+ base iana-interface-type;
+ description
+ "Proprietary multiplexing.";
+ reference
+ "RFC 2863 - The Interfaces Group MIB";
+ }
+ identity ieee80212 {
+ base iana-interface-type;
+ description
+ "100BaseVG.";
+ }
+ identity fibreChannel {
+ base iana-interface-type;
+ description
+ "Fibre Channel.";
+ }
+
+
+
+ identity hippiInterface {
+ base iana-interface-type;
+ description
+ "HIPPI interfaces.";
+ }
+ identity frameRelayInterconnect {
+ base iana-interface-type;
+ status obsolete;
+ description
+ "Obsolete; use either
+ frameRelay(32) or frameRelayService(44).";
+ }
+ identity aflane8023 {
+ base iana-interface-type;
+ description
+ "ATM Emulated LAN for 802.3.";
+ }
+ identity aflane8025 {
+ base iana-interface-type;
+ description
+ "ATM Emulated LAN for 802.5.";
+ }
+ identity cctEmul {
+ base iana-interface-type;
+ description
+ "ATM Emulated circuit.";
+ }
+ identity fastEther {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Obsoleted via RFC 3635.
+ ethernetCsmacd(6) should be used instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity isdn {
+ base iana-interface-type;
+ description
+ "ISDN and X.25.";
+ reference
+ "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN
+ in the Packet Mode";
+ }
+
+
+
+ identity v11 {
+ base iana-interface-type;
+ description
+ "CCITT V.11/X.21.";
+ }
+ identity v36 {
+ base iana-interface-type;
+ description
+ "CCITT V.36.";
+ }
+ identity g703at64k {
+ base iana-interface-type;
+ description
+ "CCITT G703 at 64Kbps.";
+ }
+ identity g703at2mb {
+ base iana-interface-type;
+ status obsolete;
+ description
+ "Obsolete; see DS1-MIB.";
+ }
+ identity qllc {
+ base iana-interface-type;
+ description
+ "SNA QLLC.";
+ }
+ identity fastEtherFX {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Obsoleted via RFC 3635.
+ ethernetCsmacd(6) should be used instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity channel {
+ base iana-interface-type;
+ description
+ "Channel.";
+ }
+ identity ieee80211 {
+ base iana-interface-type;
+ description
+ "Radio spread spectrum.";
+ }
+ identity ibm370parChan {
+ base iana-interface-type;
+ description
+ "IBM System 360/370 OEMI Channel.";
+ }
+ identity escon {
+ base iana-interface-type;
+ description
+ "IBM Enterprise Systems Connection.";
+ }
+ identity dlsw {
+ base iana-interface-type;
+ description
+ "Data Link Switching.";
+ }
+ identity isdns {
+ base iana-interface-type;
+ description
+ "ISDN S/T interface.";
+ }
+ identity isdnu {
+ base iana-interface-type;
+ description
+ "ISDN U interface.";
+ }
+ identity lapd {
+ base iana-interface-type;
+ description
+ "Link Access Protocol D.";
+ }
+ identity ipSwitch {
+ base iana-interface-type;
+ description
+ "IP Switching Objects.";
+ }
+ identity rsrb {
+ base iana-interface-type;
+ description
+ "Remote Source Route Bridging.";
+ }
+ identity atmLogical {
+ base iana-interface-type;
+ description
+ "ATM Logical Port.";
+ reference
+ "RFC 3606 - Definitions of Supplemental Managed Objects
+ for ATM Interface";
+ }
+ identity ds0 {
+ base iana-interface-type;
+ description
+ "Digital Signal Level 0.";
+ reference
+ "RFC 2494 - Definitions of Managed Objects for the DS0
+ and DS0 Bundle Interface Type";
+ }
+ identity ds0Bundle {
+ base iana-interface-type;
+ description
+ "Group of ds0s on the same ds1.";
+ reference
+ "RFC 2494 - Definitions of Managed Objects for the DS0
+ and DS0 Bundle Interface Type";
+ }
+ identity bsc {
+ base iana-interface-type;
+ description
+ "Bisynchronous Protocol.";
+ }
+ identity async {
+ base iana-interface-type;
+ description
+ "Asynchronous Protocol.";
+ }
+ identity cnr {
+ base iana-interface-type;
+ description
+ "Combat Net Radio.";
+ }
+ identity iso88025Dtr {
+ base iana-interface-type;
+ description
+ "ISO 802.5r DTR.";
+ }
+ identity eplrs {
+ base iana-interface-type;
+ description
+ "Ext Pos Loc Report Sys.";
+ }
+ identity arap {
+ base iana-interface-type;
+ description
+ "Appletalk Remote Access Protocol.";
+ }
+ identity propCnls {
+ base iana-interface-type;
+ description
+ "Proprietary Connectionless Protocol.";
+ }
+ identity hostPad {
+ base iana-interface-type;
+ description
+ "CCITT-ITU X.29 PAD Protocol.";
+ }
+ identity termPad {
+ base iana-interface-type;
+ description
+ "CCITT-ITU X.3 PAD Facility.";
+ }
+ identity frameRelayMPI {
+ base iana-interface-type;
+ description
+ "Multiproto Interconnect over FR.";
+ }
+ identity x213 {
+ base iana-interface-type;
+ description
+ "CCITT-ITU X213.";
+ }
+ identity adsl {
+ base iana-interface-type;
+ description
+ "Asymmetric Digital Subscriber Loop.";
+ }
+ identity radsl {
+ base iana-interface-type;
+ description
+ "Rate-Adapt. Digital Subscriber Loop.";
+ }
+ identity sdsl {
+ base iana-interface-type;
+ description
+ "Symmetric Digital Subscriber Loop.";
+ }
+ identity vdsl {
+ base iana-interface-type;
+ description
+ "Very H-Speed Digital Subscrib. Loop.";
+ }
+ identity iso88025CRFPInt {
+ base iana-interface-type;
+ description
+ "ISO 802.5 CRFP.";
+ }
+ identity myrinet {
+ base iana-interface-type;
+ description
+ "Myricom Myrinet.";
+ }
+ identity voiceEM {
+ base iana-interface-type;
+ description
+ "Voice recEive and transMit.";
+ }
+ identity voiceFXO {
+ base iana-interface-type;
+ description
+ "Voice Foreign Exchange Office.";
+ }
+ identity voiceFXS {
+ base iana-interface-type;
+ description
+ "Voice Foreign Exchange Station.";
+ }
+ identity voiceEncap {
+ base iana-interface-type;
+ description
+ "Voice encapsulation.";
+ }
+ identity voiceOverIp {
+ base iana-interface-type;
+ description
+ "Voice over IP encapsulation.";
+ }
+ identity atmDxi {
+ base iana-interface-type;
+ description
+ "ATM DXI.";
+ }
+ identity atmFuni {
+ base iana-interface-type;
+ description
+ "ATM FUNI.";
+ }
+ identity atmIma {
+ base iana-interface-type;
+ description
+ "ATM IMA.";
+ }
+ identity pppMultilinkBundle {
+ base iana-interface-type;
+ description
+ "PPP Multilink Bundle.";
+ }
+ identity ipOverCdlc {
+ base iana-interface-type;
+ description
+ "IBM ipOverCdlc.";
+ }
+ identity ipOverClaw {
+ base iana-interface-type;
+ description
+ "IBM Common Link Access to Workstn.";
+ }
+ identity stackToStack {
+ base iana-interface-type;
+ description
+ "IBM stackToStack.";
+ }
+ identity virtualIpAddress {
+ base iana-interface-type;
+ description
+ "IBM VIPA.";
+ }
+ identity mpc {
+ base iana-interface-type;
+ description
+ "IBM multi-protocol channel support.";
+ }
+ identity ipOverAtm {
+ base iana-interface-type;
+ description
+ "IBM ipOverAtm.";
+ reference
+ "RFC 2320 - Definitions of Managed Objects for Classical IP
+ and ARP Over ATM Using SMIv2 (IPOA-MIB)";
+ }
+ identity iso88025Fiber {
+ base iana-interface-type;
+ description
+ "ISO 802.5j Fiber Token Ring.";
+ }
+ identity tdlc {
+ base iana-interface-type;
+ description
+ "IBM twinaxial data link control.";
+ }
+ identity gigabitEthernet {
+ base iana-interface-type;
+ status deprecated;
+
+
+ description
+ "Obsoleted via RFC 3635.
+ ethernetCsmacd(6) should be used instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity hdlc {
+ base iana-interface-type;
+ description
+ "HDLC.";
+ }
+ identity lapf {
+ base iana-interface-type;
+ description
+ "LAP F.";
+ }
+ identity v37 {
+ base iana-interface-type;
+ description
+ "V.37.";
+ }
+ identity x25mlp {
+ base iana-interface-type;
+ description
+ "Multi-Link Protocol.";
+ }
+ identity x25huntGroup {
+ base iana-interface-type;
+ description
+ "X25 Hunt Group.";
+ }
+ identity transpHdlc {
+ base iana-interface-type;
+ description
+ "Transp HDLC.";
+ }
+ identity interleave {
+ base iana-interface-type;
+ description
+ "Interleave channel.";
+ }
+ identity fast {
+ base iana-interface-type;
+ description
+ "Fast channel.";
+ }
+
+ identity ip {
+ base iana-interface-type;
+ description
+ "IP (for APPN HPR in IP networks).";
+ }
+ identity docsCableMaclayer {
+ base iana-interface-type;
+ description
+ "CATV Mac Layer.";
+ }
+ identity docsCableDownstream {
+ base iana-interface-type;
+ description
+ "CATV Downstream interface.";
+ }
+ identity docsCableUpstream {
+ base iana-interface-type;
+ description
+ "CATV Upstream interface.";
+ }
+ identity a12MppSwitch {
+ base iana-interface-type;
+ description
+ "Avalon Parallel Processor.";
+ }
+ identity tunnel {
+ base iana-interface-type;
+ description
+ "Encapsulation interface.";
+ }
+ identity coffee {
+ base iana-interface-type;
+ description
+ "Coffee pot.";
+ reference
+ "RFC 2325 - Coffee MIB";
+ }
+ identity ces {
+ base iana-interface-type;
+ description
+ "Circuit Emulation Service.";
+ }
+ identity atmSubInterface {
+ base iana-interface-type;
+ description
+ "ATM Sub Interface.";
+ }
+
+ identity l2vlan {
+ base iana-interface-type;
+ description
+ "Layer 2 Virtual LAN using 802.1Q.";
+ }
+ identity l3ipvlan {
+ base iana-interface-type;
+ description
+ "Layer 3 Virtual LAN using IP.";
+ }
+ identity l3ipxvlan {
+ base iana-interface-type;
+ description
+ "Layer 3 Virtual LAN using IPX.";
+ }
+ identity digitalPowerline {
+ base iana-interface-type;
+ description
+ "IP over Power Lines.";
+ }
+ identity mediaMailOverIp {
+ base iana-interface-type;
+ description
+ "Multimedia Mail over IP.";
+ }
+ identity dtm {
+ base iana-interface-type;
+ description
+ "Dynamic synchronous Transfer Mode.";
+ }
+ identity dcn {
+ base iana-interface-type;
+ description
+ "Data Communications Network.";
+ }
+ identity ipForward {
+ base iana-interface-type;
+ description
+ "IP Forwarding Interface.";
+ }
+ identity msdsl {
+ base iana-interface-type;
+ description
+ "Multi-rate Symmetric DSL.";
+ }
+ identity ieee1394 {
+ base iana-interface-type;
+
+ description
+ "IEEE1394 High Performance Serial Bus.";
+ }
+ identity if-gsn {
+ base iana-interface-type;
+ description
+ "HIPPI-6400.";
+ }
+ identity dvbRccMacLayer {
+ base iana-interface-type;
+ description
+ "DVB-RCC MAC Layer.";
+ }
+ identity dvbRccDownstream {
+ base iana-interface-type;
+ description
+ "DVB-RCC Downstream Channel.";
+ }
+ identity dvbRccUpstream {
+ base iana-interface-type;
+ description
+ "DVB-RCC Upstream Channel.";
+ }
+ identity atmVirtual {
+ base iana-interface-type;
+ description
+ "ATM Virtual Interface.";
+ }
+ identity mplsTunnel {
+ base iana-interface-type;
+ description
+ "MPLS Tunnel Virtual Interface.";
+ }
+ identity srp {
+ base iana-interface-type;
+ description
+ "Spatial Reuse Protocol.";
+ }
+ identity voiceOverAtm {
+ base iana-interface-type;
+ description
+ "Voice over ATM.";
+ }
+ identity voiceOverFrameRelay {
+ base iana-interface-type;
+ description
+ "Voice Over Frame Relay.";
+ }
+ identity idsl {
+ base iana-interface-type;
+ description
+ "Digital Subscriber Loop over ISDN.";
+ }
+ identity compositeLink {
+ base iana-interface-type;
+ description
+ "Avici Composite Link Interface.";
+ }
+ identity ss7SigLink {
+ base iana-interface-type;
+ description
+ "SS7 Signaling Link.";
+ }
+ identity propWirelessP2P {
+ base iana-interface-type;
+ description
+ "Prop. P2P wireless interface.";
+ }
+ identity frForward {
+ base iana-interface-type;
+ description
+ "Frame Forward Interface.";
+ }
+ identity rfc1483 {
+ base iana-interface-type;
+ description
+ "Multiprotocol over ATM AAL5.";
+ reference
+ "RFC 1483 - Multiprotocol Encapsulation over ATM
+ Adaptation Layer 5";
+ }
+ identity usb {
+ base iana-interface-type;
+ description
+ "USB Interface.";
+ }
+ identity ieee8023adLag {
+ base iana-interface-type;
+ description
+ "IEEE 802.3ad Link Aggregate.";
+ }
+ identity bgppolicyaccounting {
+ base iana-interface-type;
+ description
+ "BGP Policy Accounting.";
+ }
+ identity frf16MfrBundle {
+ base iana-interface-type;
+ description
+ "FRF.16 Multilink Frame Relay.";
+ }
+ identity h323Gatekeeper {
+ base iana-interface-type;
+ description
+ "H323 Gatekeeper.";
+ }
+ identity h323Proxy {
+ base iana-interface-type;
+ description
+ "H323 Voice and Video Proxy.";
+ }
+ identity mpls {
+ base iana-interface-type;
+ description
+ "MPLS.";
+ }
+ identity mfSigLink {
+ base iana-interface-type;
+ description
+ "Multi-frequency signaling link.";
+ }
+ identity hdsl2 {
+ base iana-interface-type;
+ description
+ "High Bit-Rate DSL - 2nd generation.";
+ }
+ identity shdsl {
+ base iana-interface-type;
+ description
+ "Multirate HDSL2.";
+ }
+ identity ds1FDL {
+ base iana-interface-type;
+ description
+ "Facility Data Link (4Kbps) on a DS1.";
+ }
+ identity pos {
+ base iana-interface-type;
+ description
+ "Packet over SONET/SDH Interface.";
+ }
+
+
+
+ identity dvbAsiIn {
+ base iana-interface-type;
+ description
+ "DVB-ASI Input.";
+ }
+ identity dvbAsiOut {
+ base iana-interface-type;
+ description
+ "DVB-ASI Output.";
+ }
+ identity plc {
+ base iana-interface-type;
+ description
+ "Power Line Communications.";
+ }
+ identity nfas {
+ base iana-interface-type;
+ description
+ "Non-Facility Associated Signaling.";
+ }
+ identity tr008 {
+ base iana-interface-type;
+ description
+ "TR008.";
+ }
+ identity gr303RDT {
+ base iana-interface-type;
+ description
+ "Remote Digital Terminal.";
+ }
+ identity gr303IDT {
+ base iana-interface-type;
+ description
+ "Integrated Digital Terminal.";
+ }
+ identity isup {
+ base iana-interface-type;
+ description
+ "ISUP.";
+ }
+ identity propDocsWirelessMaclayer {
+ base iana-interface-type;
+ description
+ "Cisco proprietary Maclayer.";
+ }
+
+
+
+ identity propDocsWirelessDownstream {
+ base iana-interface-type;
+ description
+ "Cisco proprietary Downstream.";
+ }
+ identity propDocsWirelessUpstream {
+ base iana-interface-type;
+ description
+ "Cisco proprietary Upstream.";
+ }
+ identity hiperlan2 {
+ base iana-interface-type;
+ description
+ "HIPERLAN Type 2 Radio Interface.";
+ }
+ identity propBWAp2Mp {
+ base iana-interface-type;
+ description
+ "PropBroadbandWirelessAccesspt2Multipt (use of this value
+ for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f
+ is deprecated, and ieee80216WMAN(237) should be used
+ instead).";
+ }
+ identity sonetOverheadChannel {
+ base iana-interface-type;
+ description
+ "SONET Overhead Channel.";
+ }
+ identity digitalWrapperOverheadChannel {
+ base iana-interface-type;
+ description
+ "Digital Wrapper.";
+ }
+ identity aal2 {
+ base iana-interface-type;
+ description
+ "ATM adaptation layer 2.";
+ }
+ identity radioMAC {
+ base iana-interface-type;
+ description
+ "MAC layer over radio links.";
+ }
+ identity atmRadio {
+ base iana-interface-type;
+ description
+ "ATM over radio links.";
+ }
+ identity imt {
+ base iana-interface-type;
+ description
+ "Inter-Machine Trunks.";
+ }
+ identity mvl {
+ base iana-interface-type;
+ description
+ "Multiple Virtual Lines DSL.";
+ }
+ identity reachDSL {
+ base iana-interface-type;
+ description
+ "Long Reach DSL.";
+ }
+ identity frDlciEndPt {
+ base iana-interface-type;
+ description
+ "Frame Relay DLCI End Point.";
+ }
+ identity atmVciEndPt {
+ base iana-interface-type;
+ description
+ "ATM VCI End Point.";
+ }
+ identity opticalChannel {
+ base iana-interface-type;
+ description
+ "Optical Channel.";
+ }
+ identity opticalTransport {
+ base iana-interface-type;
+ description
+ "Optical Transport.";
+ }
+ identity propAtm {
+ base iana-interface-type;
+ description
+ "Proprietary ATM.";
+ }
+ identity voiceOverCable {
+ base iana-interface-type;
+ description
+ "Voice Over Cable Interface.";
+ }
+
+
+
+ identity infiniband {
+ base iana-interface-type;
+ description
+ "Infiniband.";
+ }
+ identity teLink {
+ base iana-interface-type;
+ description
+ "TE Link.";
+ }
+ identity q2931 {
+ base iana-interface-type;
+ description
+ "Q.2931.";
+ }
+ identity virtualTg {
+ base iana-interface-type;
+ description
+ "Virtual Trunk Group.";
+ }
+ identity sipTg {
+ base iana-interface-type;
+ description
+ "SIP Trunk Group.";
+ }
+ identity sipSig {
+ base iana-interface-type;
+ description
+ "SIP Signaling.";
+ }
+ identity docsCableUpstreamChannel {
+ base iana-interface-type;
+ description
+ "CATV Upstream Channel.";
+ }
+ identity econet {
+ base iana-interface-type;
+ description
+ "Acorn Econet.";
+ }
+ identity pon155 {
+ base iana-interface-type;
+ description
+ "FSAN 155Mb Symetrical PON interface.";
+ }
+
+
+
+ identity pon622 {
+ base iana-interface-type;
+ description
+ "FSAN 622Mb Symetrical PON interface.";
+ }
+ identity bridge {
+ base iana-interface-type;
+ description
+ "Transparent bridge interface.";
+ }
+ identity linegroup {
+ base iana-interface-type;
+ description
+ "Interface common to multiple lines.";
+ }
+ identity voiceEMFGD {
+ base iana-interface-type;
+ description
+ "Voice E&M Feature Group D.";
+ }
+ identity voiceFGDEANA {
+ base iana-interface-type;
+ description
+ "Voice FGD Exchange Access North American.";
+ }
+ identity voiceDID {
+ base iana-interface-type;
+ description
+ "Voice Direct Inward Dialing.";
+ }
+ identity mpegTransport {
+ base iana-interface-type;
+ description
+ "MPEG transport interface.";
+ }
+ identity sixToFour {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "6to4 interface (DEPRECATED).";
+ reference
+ "RFC 4087 - IP Tunnel MIB";
+ }
+ identity gtp {
+ base iana-interface-type;
+ description
+ "GTP (GPRS Tunneling Protocol).";
+ }
+ identity pdnEtherLoop1 {
+ base iana-interface-type;
+ description
+ "Paradyne EtherLoop 1.";
+ }
+ identity pdnEtherLoop2 {
+ base iana-interface-type;
+ description
+ "Paradyne EtherLoop 2.";
+ }
+ identity opticalChannelGroup {
+ base iana-interface-type;
+ description
+ "Optical Channel Group.";
+ }
+ identity homepna {
+ base iana-interface-type;
+ description
+ "HomePNA ITU-T G.989.";
+ }
+ identity gfp {
+ base iana-interface-type;
+ description
+ "Generic Framing Procedure (GFP).";
+ }
+ identity ciscoISLvlan {
+ base iana-interface-type;
+ description
+ "Layer 2 Virtual LAN using Cisco ISL.";
+ }
+ identity actelisMetaLOOP {
+ base iana-interface-type;
+ description
+ "Acteleis proprietary MetaLOOP High Speed Link.";
+ }
+ identity fcipLink {
+ base iana-interface-type;
+ description
+ "FCIP Link.";
+ }
+ identity rpr {
+ base iana-interface-type;
+ description
+ "Resilient Packet Ring Interface Type.";
+ }
+
+
+
+ identity qam {
+ base iana-interface-type;
+ description
+ "RF Qam Interface.";
+ }
+ identity lmp {
+ base iana-interface-type;
+ description
+ "Link Management Protocol.";
+ reference
+ "RFC 4327 - Link Management Protocol (LMP) Management
+ Information Base (MIB)";
+ }
+ identity cblVectaStar {
+ base iana-interface-type;
+ description
+ "Cambridge Broadband Networks Limited VectaStar.";
+ }
+ identity docsCableMCmtsDownstream {
+ base iana-interface-type;
+ description
+ "CATV Modular CMTS Downstream Interface.";
+ }
+ identity adsl2 {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Asymmetric Digital Subscriber Loop Version 2
+ (DEPRECATED/OBSOLETED - please use adsl2plus(238)
+ instead).";
+ reference
+ "RFC 4706 - Definitions of Managed Objects for Asymmetric
+ Digital Subscriber Line 2 (ADSL2)";
+ }
+ identity macSecControlledIF {
+ base iana-interface-type;
+ description
+ "MACSecControlled.";
+ }
+ identity macSecUncontrolledIF {
+ base iana-interface-type;
+ description
+ "MACSecUncontrolled.";
+ }
+ identity aviciOpticalEther {
+ base iana-interface-type;
+ description
+ "Avici Optical Ethernet Aggregate.";
+ }
+ identity atmbond {
+ base iana-interface-type;
+ description
+ "atmbond.";
+ }
+ identity voiceFGDOS {
+ base iana-interface-type;
+ description
+ "Voice FGD Operator Services.";
+ }
+ identity mocaVersion1 {
+ base iana-interface-type;
+ description
+ "MultiMedia over Coax Alliance (MoCA) Interface
+ as documented in information provided privately to IANA.";
+ }
+ identity ieee80216WMAN {
+ base iana-interface-type;
+ description
+ "IEEE 802.16 WMAN interface.";
+ }
+ identity adsl2plus {
+ base iana-interface-type;
+ description
+ "Asymmetric Digital Subscriber Loop Version 2 -
+ Version 2 Plus and all variants.";
+ }
+ identity dvbRcsMacLayer {
+ base iana-interface-type;
+ description
+ "DVB-RCS MAC Layer.";
+ reference
+ "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+ }
+ identity dvbTdm {
+ base iana-interface-type;
+ description
+ "DVB Satellite TDM.";
+ reference
+ "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+ }
+ identity dvbRcsTdma {
+ base iana-interface-type;
+ description
+ "DVB-RCS TDMA.";
+ reference
+ "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+ }
+ identity x86Laps {
+ base iana-interface-type;
+ description
+ "LAPS based on ITU-T X.86/Y.1323.";
+ }
+ identity wwanPP {
+ base iana-interface-type;
+ description
+ "3GPP WWAN.";
+ }
+ identity wwanPP2 {
+ base iana-interface-type;
+ description
+ "3GPP2 WWAN.";
+ }
+ identity voiceEBS {
+ base iana-interface-type;
+ description
+ "Voice P-phone EBS physical interface.";
+ }
+ identity ifPwType {
+ base iana-interface-type;
+ description
+ "Pseudowire interface type.";
+ reference
+ "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)";
+ }
+ identity ilan {
+ base iana-interface-type;
+ description
+ "Internal LAN on a bridge per IEEE 802.1ap.";
+ }
+ identity pip {
+ base iana-interface-type;
+ description
+ "Provider Instance Port on a bridge per IEEE 802.1ah PBB.";
+ }
+ identity aluELP {
+ base iana-interface-type;
+ description
+ "Alcatel-Lucent Ethernet Link Protection.";
+ }
+ identity gpon {
+ base iana-interface-type;
+ description
+ "Gigabit-capable passive optical networks (G-PON) as per
+ ITU-T G.948.";
+ }
+ identity vdsl2 {
+ base iana-interface-type;
+ description
+ "Very high speed digital subscriber line Version 2
+ (as per ITU-T Recommendation G.993.2).";
+ reference
+ "RFC 5650 - Definitions of Managed Objects for Very High
+ Speed Digital Subscriber Line 2 (VDSL2)";
+ }
+ identity capwapDot11Profile {
+ base iana-interface-type;
+ description
+ "WLAN Profile Interface.";
+ reference
+ "RFC 5834 - Control and Provisioning of Wireless Access
+ Points (CAPWAP) Protocol Binding MIB for
+ IEEE 802.11";
+ }
+ identity capwapDot11Bss {
+ base iana-interface-type;
+ description
+ "WLAN BSS Interface.";
+ reference
+ "RFC 5834 - Control and Provisioning of Wireless Access
+ Points (CAPWAP) Protocol Binding MIB for
+ IEEE 802.11";
+ }
+ identity capwapWtpVirtualRadio {
+ base iana-interface-type;
+ description
+ "WTP Virtual Radio Interface.";
+ reference
+ "RFC 5833 - Control and Provisioning of Wireless Access
+ Points (CAPWAP) Protocol Base MIB";
+ }
+ identity bits {
+ base iana-interface-type;
+ description
+ "bitsport.";
+ }
+ identity docsCableUpstreamRfPort {
+ base iana-interface-type;
+ description
+ "DOCSIS CATV Upstream RF Port.";
+ }
+
+
+ identity cableDownstreamRfPort {
+ base iana-interface-type;
+ description
+ "CATV downstream RF Port.";
+ }
+ identity vmwareVirtualNic {
+ base iana-interface-type;
+ description
+ "VMware Virtual Network Interface.";
+ }
+ identity ieee802154 {
+ base iana-interface-type;
+ description
+ "IEEE 802.15.4 WPAN interface.";
+ reference
+ "IEEE 802.15.4-2006";
+ }
+ identity otnOdu {
+ base iana-interface-type;
+ description
+ "OTN Optical Data Unit.";
+ }
+ identity otnOtu {
+ base iana-interface-type;
+ description
+ "OTN Optical channel Transport Unit.";
+ }
+ identity ifVfiType {
+ base iana-interface-type;
+ description
+ "VPLS Forwarding Instance Interface Type.";
+ }
+ identity g9981 {
+ base iana-interface-type;
+ description
+ "G.998.1 bonded interface.";
+ }
+ identity g9982 {
+ base iana-interface-type;
+ description
+ "G.998.2 bonded interface.";
+ }
+ identity g9983 {
+ base iana-interface-type;
+ description
+ "G.998.3 bonded interface.";
+ }
+
+ identity aluEpon {
+ base iana-interface-type;
+ description
+ "Ethernet Passive Optical Networks (E-PON).";
+ }
+ identity aluEponOnu {
+ base iana-interface-type;
+ description
+ "EPON Optical Network Unit.";
+ }
+ identity aluEponPhysicalUni {
+ base iana-interface-type;
+ description
+ "EPON physical User to Network interface.";
+ }
+ identity aluEponLogicalLink {
+ base iana-interface-type;
+ description
+ "The emulation of a point-to-point link over the EPON
+ layer.";
+ }
+ identity aluGponOnu {
+ base iana-interface-type;
+ description
+ "GPON Optical Network Unit.";
+ reference
+ "ITU-T G.984.2";
+ }
+ identity aluGponPhysicalUni {
+ base iana-interface-type;
+ description
+ "GPON physical User to Network interface.";
+ reference
+ "ITU-T G.984.2";
+ }
+ identity vmwareNicTeam {
+ base iana-interface-type;
+ description
+ "VMware NIC Team.";
+ }
+}
diff --git a/tests/modules/yang/ietf-interfaces@2014-05-08.yang b/tests/modules/yang/ietf-interfaces@2014-05-08.yang
new file mode 100644
index 0000000..ad64425
--- /dev/null
+++ b/tests/modules/yang/ietf-interfaces@2014-05-08.yang
@@ -0,0 +1,725 @@
+module ietf-interfaces {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+ prefix if;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "This module contains a collection of YANG definitions for
+ managing network interfaces.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7223; see
+ the RFC itself for full legal notices.";
+
+ revision 2014-05-08 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7223: A YANG Data Model for Interface Management";
+ }
+
+ /*
+ * Typedefs
+ */
+
+ typedef interface-ref {
+ type leafref {
+ path "/if:interfaces/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ configured interfaces.";
+ }
+
+ typedef interface-state-ref {
+ type leafref {
+ path "/if:interfaces-state/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ the operationally present interfaces.";
+ }
+
+ /*
+ * Identities
+ */
+
+ identity interface-type {
+ description
+ "Base identity from which specific interface types are
+ derived.";
+ }
+
+ /*
+ * Features
+ */
+
+ feature arbitrary-names {
+ description
+ "This feature indicates that the device allows user-controlled
+ interfaces to be named arbitrarily.";
+ }
+ feature pre-provisioning {
+ description
+ "This feature indicates that the device supports
+ pre-provisioning of interface configuration, i.e., it is
+ possible to configure an interface whose physical interface
+ hardware is not present on the device.";
+ }
+
+ feature if-mib {
+ description
+ "This feature indicates that the device implements
+ the IF-MIB.";
+ reference
+ "RFC 2863: The Interfaces Group MIB";
+ }
+
+ /*
+ * Configuration data nodes
+ */
+
+ container interfaces {
+ description
+ "Interface configuration parameters.";
+
+ list interface {
+ key "name";
+
+ description
+ "The list of configured interfaces on the device.
+
+ The operational state of an interface is available in the
+ /interfaces-state/interface list. If the configuration of a
+ system-controlled interface cannot be used by the system
+ (e.g., the interface hardware present does not match the
+ interface type), then the configuration is not applied to
+ the system-controlled interface shown in the
+ /interfaces-state/interface list. If the configuration
+ of a user-controlled interface cannot be used by the system,
+ the configured interface is not instantiated in the
+ /interfaces-state/interface list.";
+
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A device MAY restrict the allowed values for this leaf,
+ possibly depending on the type of the interface.
+ For system-controlled interfaces, this leaf is the
+ device-specific name of the interface. The 'config false'
+ list /interfaces-state/interface contains the currently
+ existing interfaces on the device.
+
+ If a client tries to create configuration for a
+ system-controlled interface that is not present in the
+ /interfaces-state/interface list, the server MAY reject
+ the request if the implementation does not support
+ pre-provisioning of interfaces or if the name refers to
+ an interface that can never exist in the system. A
+ NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.
+
+ If the device supports pre-provisioning of interface
+ configuration, the 'pre-provisioning' feature is
+ advertised.
+
+ If the device allows arbitrarily named user-controlled
+ interfaces, the 'arbitrary-names' feature is advertised.
+
+ When a configured user-controlled interface is created by
+ the system, it is instantiated with the same name in the
+ /interface-state/interface list.";
+ }
+
+ leaf description {
+ type string;
+ description
+ "A textual description of the interface.
+
+ A server implementation MAY map this leaf to the ifAlias
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifAlias. The definition of
+ such a mechanism is outside the scope of this document.
+
+ Since ifAlias is defined to be stored in non-volatile
+ storage, the MIB implementation MUST map ifAlias to the
+ value of 'description' in the persistently stored
+ datastore.
+
+ Specifically, if the device supports ':startup', when
+ ifAlias is read the device MUST return the value of
+ 'description' in the 'startup' datastore, and when it is
+ written, it MUST be written to the 'running' and 'startup'
+ datastores. Note that it is up to the implementation to
+
+ decide whether to modify this single leaf in 'startup' or
+ perform an implicit copy-config from 'running' to
+ 'startup'.
+
+ If the device does not support ':startup', ifAlias MUST
+ be mapped to the 'description' leaf in the 'running'
+ datastore.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAlias";
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.
+
+ When an interface entry is created, a server MAY
+ initialize the type leaf with a valid value, e.g., if it
+ is possible to derive the type from the name of the
+ interface.
+
+ If a client tries to set the type of an interface to a
+ value that can never be used by the system, e.g., if the
+ type is not supported or if the type does not match the
+ name of the interface, the server MUST reject the request.
+ A NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+ }
+
+ leaf enabled {
+ type boolean;
+ default "true";
+ description
+ "This leaf contains the configured, desired state of the
+ interface.
+
+ Systems that implement the IF-MIB use the value of this
+ leaf in the 'running' datastore to set
+ IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+ has been initialized, as described in RFC 2863.
+
+
+
+ Changes in this leaf in the 'running' datastore are
+ reflected in ifAdminStatus, but if ifAdminStatus is
+ changed over SNMP, this leaf is not affected.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+ }
+
+ leaf link-up-down-trap-enable {
+ if-feature if-mib;
+ type enumeration {
+ enum enabled {
+ value 1;
+ }
+ enum disabled {
+ value 2;
+ }
+ }
+ description
+ "Controls whether linkUp/linkDown SNMP notifications
+ should be generated for this interface.
+
+ If this node is not configured, the value 'enabled' is
+ operationally used by the server for interfaces that do
+ not operate on top of any other interface (i.e., there are
+ no 'lower-layer-if' entries), and 'disabled' otherwise.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifLinkUpDownTrapEnable";
+ }
+ }
+ }
+
+ /*
+ * Operational state data nodes
+ */
+
+ container interfaces-state {
+ config false;
+ description
+ "Data nodes for the operational state of interfaces.";
+
+ list interface {
+ key "name";
+
+
+
+
+
+ description
+ "The list of interfaces on the device.
+
+ System-controlled interfaces created by the system are
+ always present in this list, whether they are configured or
+ not.";
+
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A server implementation MAY map this leaf to the ifName
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifName. The definition of
+ such a mechanism is outside the scope of this document.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifName";
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+ }
+
+ leaf admin-status {
+ if-feature if-mib;
+ type enumeration {
+ enum up {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum down {
+ value 2;
+ description
+ "Not ready to pass packets and not in some test mode.";
+ }
+
+
+
+ enum testing {
+ value 3;
+ description
+ "In some test mode.";
+ }
+ }
+ mandatory true;
+ description
+ "The desired state of the interface.
+
+ This leaf has the same read semantics as ifAdminStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+ }
+
+ leaf oper-status {
+ type enumeration {
+ enum up {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum down {
+ value 2;
+ description
+ "The interface does not pass any packets.";
+ }
+ enum testing {
+ value 3;
+ description
+ "In some test mode. No operational packets can
+ be passed.";
+ }
+ enum unknown {
+ value 4;
+ description
+ "Status cannot be determined for some reason.";
+ }
+ enum dormant {
+ value 5;
+ description
+ "Waiting for some external event.";
+ }
+ enum not-present {
+ value 6;
+ description
+ "Some component (typically hardware) is missing.";
+ }
+ enum lower-layer-down {
+ value 7;
+ description
+ "Down due to state of lower-layer interface(s).";
+ }
+ }
+ mandatory true;
+ description
+ "The current operational state of the interface.
+
+ This leaf has the same semantics as ifOperStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+ }
+
+ leaf last-change {
+ type yang:date-and-time;
+ description
+ "The time the interface entered its current operational
+ state. If the current state was entered prior to the
+ last re-initialization of the local network management
+ subsystem, then this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifLastChange";
+ }
+
+ leaf if-index {
+ if-feature if-mib;
+ type int32 {
+ range "1..2147483647";
+ }
+ mandatory true;
+ description
+ "The ifIndex value for the ifEntry represented by this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifIndex";
+ }
+
+ leaf phys-address {
+ type yang:phys-address;
+ description
+ "The interface's address at its protocol sub-layer. For
+ example, for an 802.x interface, this object normally
+ contains a Media Access Control (MAC) address. The
+ interface's media-specific modules must define the bit
+
+
+ and byte ordering and the format of the value of this
+ object. For interfaces that do not have such an address
+ (e.g., a serial line), this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+ }
+
+ leaf-list higher-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered on top of this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+ }
+
+ leaf-list lower-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered underneath this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+ }
+
+ leaf speed {
+ type yang:gauge64;
+ units "bits/second";
+ description
+ "An estimate of the interface's current bandwidth in bits
+ per second. For interfaces that do not vary in
+ bandwidth or for those where no accurate estimation can
+ be made, this node should contain the nominal bandwidth.
+ For interfaces that have no concept of bandwidth, this
+ node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifSpeed, ifHighSpeed";
+ }
+
+
+
+
+
+
+
+
+
+ container statistics {
+ description
+ "A collection of interface-related statistics objects.";
+
+ leaf discontinuity-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The time on the most recent occasion at which any one or
+ more of this interface's counters suffered a
+ discontinuity. If no such discontinuities have occurred
+ since the last re-initialization of the local management
+ subsystem, then this node contains the time the local
+ management subsystem re-initialized itself.";
+ }
+
+ leaf in-octets {
+ type yang:counter64;
+ description
+ "The total number of octets received on the interface,
+ including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+ }
+
+ leaf in-unicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were not addressed to a
+ multicast or broadcast address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+ }
+
+
+
+
+ leaf in-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a broadcast
+ address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInBroadcastPkts";
+ }
+
+ leaf in-multicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a multicast
+ address at this sub-layer. For a MAC-layer protocol,
+ this includes both Group and Functional addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInMulticastPkts";
+ }
+
+ leaf in-discards {
+ type yang:counter32;
+ description
+ "The number of inbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being deliverable to a higher-layer
+ protocol. One possible reason for discarding such a
+ packet could be to free up buffer space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+
+
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+ }
+
+ leaf in-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of inbound
+ packets that contained errors preventing them from being
+ deliverable to a higher-layer protocol. For character-
+ oriented or fixed-length interfaces, the number of
+ inbound transmission units that contained errors
+ preventing them from being deliverable to a higher-layer
+ protocol.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInErrors";
+ }
+
+ leaf in-unknown-protos {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of packets
+ received via the interface that were discarded because
+ of an unknown or unsupported protocol. For
+ character-oriented or fixed-length interfaces that
+ support protocol multiplexing, the number of
+ transmission units received via the interface that were
+ discarded because of an unknown or unsupported protocol.
+ For any interface that does not support protocol
+ multiplexing, this counter is not present.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+ }
+
+
+
+
+
+ leaf out-octets {
+ type yang:counter64;
+ description
+ "The total number of octets transmitted out of the
+ interface, including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+ }
+
+ leaf out-unicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were not addressed
+ to a multicast or broadcast address at this sub-layer,
+ including those that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+ }
+
+ leaf out-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ broadcast address at this sub-layer, including those
+ that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutBroadcastPkts";
+ }
+
+
+ leaf out-multicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ multicast address at this sub-layer, including those
+ that were discarded or not sent. For a MAC-layer
+ protocol, this includes both Group and Functional
+ addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutMulticastPkts";
+ }
+
+ leaf out-discards {
+ type yang:counter32;
+ description
+ "The number of outbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being transmitted. One possible reason
+ for discarding such a packet could be to free up buffer
+ space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+ }
+
+ leaf out-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of outbound
+ packets that could not be transmitted because of errors.
+ For character-oriented or fixed-length interfaces, the
+ number of outbound transmission units that could not be
+ transmitted because of errors.
+
+
+
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/modules/yang/ietf-ip@2014-06-16.yang b/tests/modules/yang/ietf-ip@2014-06-16.yang
new file mode 100644
index 0000000..1499120
--- /dev/null
+++ b/tests/modules/yang/ietf-ip@2014-06-16.yang
@@ -0,0 +1,758 @@
+module ietf-ip {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
+ prefix ip;
+
+ import ietf-interfaces {
+ prefix if;
+ }
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+
+
+
+
+
+
+
+
+
+ description
+ "This module contains a collection of YANG definitions for
+ configuring IP implementations.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7277; see
+ the RFC itself for full legal notices.";
+
+ revision 2014-06-16 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7277: A YANG Data Model for IP Management";
+ }
+
+ /*
+
+ * Features
+ */
+
+ feature ipv4-non-contiguous-netmasks {
+ description
+ "Indicates support for configuring non-contiguous
+ subnet masks.";
+ }
+
+ feature ipv6-privacy-autoconf {
+ description
+ "Indicates support for Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+ /*
+ * Typedefs
+ */
+
+ typedef ip-address-origin {
+ type enumeration {
+ enum other {
+ description
+ "None of the following.";
+ }
+ enum static {
+ description
+ "Indicates that the address has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum dhcp {
+ description
+ "Indicates an address that has been assigned to this
+ system by a DHCP server.";
+ }
+ enum link-layer {
+ description
+ "Indicates an address created by IPv6 stateless
+ autoconfiguration that embeds a link-layer address in its
+ interface identifier.";
+ }
+ enum random {
+ description
+ "Indicates an address chosen by the system at
+
+ random, e.g., an IPv4 address within 169.254/16, an
+ RFC 4941 temporary address, or an RFC 7217 semantically
+ opaque address.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ RFC 7217: A Method for Generating Semantically Opaque
+ Interface Identifiers with IPv6 Stateless
+ Address Autoconfiguration (SLAAC)";
+ }
+ }
+ description
+ "The origin of an address.";
+ }
+
+
+
+ typedef neighbor-origin {
+ type enumeration {
+ enum other {
+ description
+ "None of the following.";
+ }
+ enum static {
+ description
+ "Indicates that the mapping has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum dynamic {
+ description
+ "Indicates that the mapping has been dynamically resolved
+ using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery
+ protocol.";
+ }
+ }
+ description
+ "The origin of a neighbor entry.";
+ }
+
+ /*
+ * Configuration data nodes
+ */
+
+ augment "/if:interfaces/if:interface" {
+ description
+ "Parameters for configuring IP on interfaces.
+
+ If an interface is not capable of running IP, the server
+ must not allow the client to configure these parameters.";
+
+ container ipv4 {
+ presence
+ "Enables IPv4 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv4 address family.";
+
+
+
+
+
+
+
+
+ leaf enabled {
+ type boolean;
+ default true;
+ description
+ "Controls whether IPv4 is enabled or disabled on this
+ interface. When IPv4 is enabled, this interface is
+ connected to an IPv4 stack, and the interface can send
+ and receive IPv4 packets.";
+ }
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Controls IPv4 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv4 routers
+ forward datagrams. IPv4 hosts do not (except those
+ source-routed via the host).";
+ }
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 791: Internet Protocol";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv4 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+
+
+
+ choice subnet {
+ mandatory true;
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ }
+
+ }
+
+
+ container ipv6 {
+ presence
+ "Enables IPv6 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv6 address family.";
+
+ leaf enabled {
+ type boolean;
+ default true;
+ description
+ "Controls whether IPv6 is enabled or disabled on this
+ interface. When IPv6 is enabled, this interface is
+ connected to an IPv6 stack, and the interface can send
+ and receive IPv6 packets.";
+ }
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Controls IPv6 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv6 routers
+ forward datagrams. IPv6 hosts do not (except those
+ source-routed via the host).";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+ }
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+ }
+
+
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv6 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ }
+
+
+
+
+
+
+ leaf dup-addr-detect-transmits {
+ type uint32;
+ default 1;
+ description
+ "The number of consecutive Neighbor Solicitation messages
+ sent while performing Duplicate Address Detection on a
+ tentative address. A value of zero indicates that
+ Duplicate Address Detection is not performed on
+ tentative addresses. A value of one indicates a single
+ transmission with no follow-up retransmissions.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+ }
+ container autoconf {
+ description
+ "Parameters to control the autoconfiguration of IPv6
+ addresses, as described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+ leaf create-global-addresses {
+ type boolean;
+ default true;
+ description
+ "If enabled, the host creates global addresses as
+ described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration
+ Section 5.5";
+ }
+ leaf create-temporary-addresses {
+ if-feature ipv6-privacy-autoconf;
+ type boolean;
+ default false;
+ description
+ "If enabled, the host creates temporary addresses as
+ described in RFC 4941.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+
+
+ leaf temporary-valid-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default 604800;
+ description
+ "The time period during which the temporary address
+ is valid.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_VALID_LIFETIME";
+ }
+ leaf temporary-preferred-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default 86400;
+ description
+ "The time period during which the temporary address is
+ preferred.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_PREFERRED_LIFETIME";
+ }
+ }
+ }
+ }
+
+ /*
+ * Operational state data nodes
+ */
+
+ augment "/if:interfaces-state/if:interface" {
+ description
+ "Data nodes for the operational state of IP on interfaces.";
+
+ container ipv4 {
+ presence "Present if IPv4 is enabled on this interface";
+ config false;
+ description
+ "Interface-specific parameters for the IPv4 address family.";
+
+
+
+
+
+ leaf forwarding {
+ type boolean;
+ description
+ "Indicates whether IPv4 packet forwarding is enabled or
+ disabled on this interface.";
+ }
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 791: Internet Protocol";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of IPv4 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+ choice subnet {
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ }
+
+
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ This list represents the ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ }
+
+ }
+
+ container ipv6 {
+ presence "Present if IPv6 is enabled on this interface";
+ config false;
+ description
+ "Parameters for the IPv6 address family.";
+
+
+
+
+
+
+
+
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Indicates whether IPv6 packet forwarding is enabled or
+ disabled on this interface.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+ }
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of IPv6 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+
+
+
+ leaf status {
+ type enumeration {
+ enum preferred {
+ description
+ "This is a valid address that can appear as the
+ destination or source address of a packet.";
+ }
+ enum deprecated {
+ description
+ "This is a valid but deprecated address that should
+ no longer be used as a source address in new
+ communications, but packets addressed to such an
+ address are processed as expected.";
+ }
+ enum invalid {
+ description
+ "This isn't a valid address, and it shouldn't appear
+ as the destination or source address of a packet.";
+ }
+ enum inaccessible {
+ description
+ "The address is not accessible because the interface
+ to which this address is assigned is not
+ operational.";
+ }
+ enum unknown {
+ description
+ "The status cannot be determined for some reason.";
+ }
+ enum tentative {
+ description
+ "The uniqueness of the address on the link is being
+ verified. Addresses in this state should not be
+ used for general communication and should only be
+ used to determine the uniqueness of the address.";
+ }
+ enum duplicate {
+ description
+ "The address has been determined to be non-unique on
+ the link and so must not be used.";
+ }
+
+
+
+
+
+
+
+ enum optimistic {
+ description
+ "The address is available for use, subject to
+ restrictions, while its uniqueness on a link is
+ being verified.";
+ }
+ }
+ description
+ "The status of an address. Most of the states correspond
+ to states from the IPv6 Stateless Address
+ Autoconfiguration protocol.";
+ reference
+ "RFC 4293: Management Information Base for the
+ Internet Protocol (IP)
+ - IpAddressStatusTC
+ RFC 4862: IPv6 Stateless Address Autoconfiguration";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ This list represents the Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ leaf is-router {
+ type empty;
+ description
+ "Indicates that the neighbor node acts as a router.";
+ }
+ leaf state {
+ type enumeration {
+ enum incomplete {
+ description
+ "Address resolution is in progress, and the link-layer
+ address of the neighbor has not yet been
+ determined.";
+ }
+ enum reachable {
+ description
+ "Roughly speaking, the neighbor is known to have been
+ reachable recently (within tens of seconds ago).";
+ }
+ enum stale {
+ description
+ "The neighbor is no longer known to be reachable, but
+ until traffic is sent to the neighbor no attempt
+ should be made to verify its reachability.";
+ }
+ enum delay {
+ description
+ "The neighbor is no longer known to be reachable, and
+ traffic has recently been sent to the neighbor.
+ Rather than probe the neighbor immediately, however,
+ delay sending probes for a short while in order to
+ give upper-layer protocols a chance to provide
+ reachability confirmation.";
+ }
+ enum probe {
+ description
+ "The neighbor is no longer known to be reachable, and
+ unicast Neighbor Solicitation probes are being sent
+ to verify reachability.";
+ }
+ }
+ description
+ "The Neighbor Unreachability Detection state of this
+ entry.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 7.3.2";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang b/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang
new file mode 100644
index 0000000..bf4855f
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang
@@ -0,0 +1,464 @@
+module ietf-netconf-acm {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+
+ prefix nacm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "Network Configuration Access Control Model.
+
+ Copyright (c) 2012 - 2018 IETF Trust and the persons
+ identified as authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8341; see
+ the RFC itself for full legal notices.";
+
+ revision "2018-02-14" {
+ description
+ "Added support for YANG 1.1 actions and notifications tied to
+ data nodes. Clarified how NACM extensions can be used by
+ other data models.";
+ reference
+ "RFC 8341: Network Configuration Access Control Model";
+ }
+
+ revision "2012-02-22" {
+ description
+ "Initial version.";
+ reference
+ "RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model";
+ }
+
+ /*
+ * Extension statements
+ */
+
+ extension default-deny-write {
+ description
+ "Used to indicate that the data model node
+ represents a sensitive security system parameter.
+
+ If present, the NETCONF server will only allow the designated
+ 'recovery session' to have write access to the node. An
+ explicit access control rule is required for all other users.
+
+ If the NACM module is used, then it must be enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), or this extension
+ is ignored.
+
+ The 'default-deny-write' extension MAY appear within a data
+ definition statement. It is ignored otherwise.";
+ }
+
+ extension default-deny-all {
+ description
+ "Used to indicate that the data model node
+ controls a very sensitive security system parameter.
+
+ If present, the NETCONF server will only allow the designated
+ 'recovery session' to have read, write, or execute access to
+ the node. An explicit access control rule is required for all
+ other users.
+
+ If the NACM module is used, then it must be enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), or this extension
+ is ignored.
+
+ The 'default-deny-all' extension MAY appear within a data
+ definition statement, 'rpc' statement, or 'notification'
+ statement. It is ignored otherwise.";
+ }
+
+ /*
+ * Derived types
+ */
+
+ typedef user-name-type {
+ type string {
+ length "1..max";
+ }
+ description
+ "General-purpose username string.";
+ }
+
+ typedef matchall-string-type {
+ type string {
+ pattern '\*';
+ }
+ description
+ "The string containing a single asterisk '*' is used
+ to conceptually represent all possible values
+ for the particular leaf using this data type.";
+ }
+
+ typedef access-operations-type {
+ type bits {
+ bit create {
+ description
+ "Any protocol operation that creates a
+ new data node.";
+ }
+ bit read {
+ description
+ "Any protocol operation or notification that
+ returns the value of a data node.";
+ }
+ bit update {
+ description
+ "Any protocol operation that alters an existing
+ data node.";
+ }
+ bit delete {
+ description
+ "Any protocol operation that removes a data node.";
+ }
+ bit exec {
+ description
+ "Execution access to the specified protocol operation.";
+ }
+ }
+ description
+ "Access operation.";
+ }
+
+ typedef group-name-type {
+ type string {
+ length "1..max";
+ pattern '[^\*].*';
+ }
+ description
+ "Name of administrative group to which
+ users can be assigned.";
+ }
+
+ typedef action-type {
+ type enumeration {
+ enum permit {
+ description
+ "Requested action is permitted.";
+ }
+ enum deny {
+ description
+ "Requested action is denied.";
+ }
+ }
+ description
+ "Action taken by the server when a particular
+ rule matches.";
+ }
+
+ typedef node-instance-identifier {
+ type yang:xpath1.0;
+ description
+ "Path expression used to represent a special
+ data node, action, or notification instance-identifier
+ string.
+
+ A node-instance-identifier value is an
+ unrestricted YANG instance-identifier expression.
+ All the same rules as an instance-identifier apply,
+ except that predicates for keys are optional. If a key
+ predicate is missing, then the node-instance-identifier
+ represents all possible server instances for that key.
+
+ This XML Path Language (XPath) expression is evaluated in the
+ following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the
+ current session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.
+
+ The accessible tree includes actions and notifications tied
+ to data nodes.";
+ }
+
+ /*
+ * Data definition statements
+ */
+
+ container nacm {
+ nacm:default-deny-all;
+
+ description
+ "Parameters for NETCONF access control model.";
+
+ leaf enable-nacm {
+ type boolean;
+ default "true";
+ description
+ "Enables or disables all NETCONF access control
+ enforcement. If 'true', then enforcement
+ is enabled. If 'false', then enforcement
+ is disabled.";
+ }
+
+ leaf read-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether read access is granted if
+ no appropriate rule is found for a
+ particular read request.";
+ }
+
+ leaf write-default {
+ type action-type;
+ default "deny";
+ description
+ "Controls whether create, update, or delete access
+ is granted if no appropriate rule is found for a
+ particular write request.";
+ }
+
+ leaf exec-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether exec access is granted if no appropriate
+ rule is found for a particular protocol operation request.";
+ }
+
+ leaf enable-external-groups {
+ type boolean;
+ default "true";
+ description
+ "Controls whether the server uses the groups reported by the
+ NETCONF transport layer when it assigns the user to a set of
+ NACM groups. If this leaf has the value 'false', any group
+ names reported by the transport layer are ignored by the
+ server.";
+ }
+
+ leaf denied-operations {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request was denied.";
+ }
+
+ leaf denied-data-writes {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request to alter
+ a configuration datastore was denied.";
+ }
+
+ leaf denied-notifications {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that
+ a notification was dropped for a subscription because
+ access to the event type was denied.";
+ }
+
+ container groups {
+ description
+ "NETCONF access control groups.";
+
+ list group {
+ key name;
+
+ description
+ "One NACM group entry. This list will only contain
+ configured entries, not any entries learned from
+ any transport protocols.";
+
+ leaf name {
+ type group-name-type;
+ description
+ "Group name associated with this entry.";
+ }
+
+ leaf-list user-name {
+ type user-name-type;
+ description
+ "Each entry identifies the username of
+ a member of the group associated with
+ this entry.";
+ }
+ }
+ }
+
+ list rule-list {
+ key name;
+ ordered-by user;
+ description
+ "An ordered collection of access control rules.";
+
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule-list.";
+ }
+ leaf-list group {
+ type union {
+ type matchall-string-type;
+ type group-name-type;
+ }
+ description
+ "List of administrative groups that will be
+ assigned the associated access rights
+ defined by the 'rule' list.
+
+ The string '*' indicates that all groups apply to the
+ entry.";
+ }
+
+ list rule {
+ key name;
+ ordered-by user;
+ description
+ "One access control rule.
+
+ Rules are processed in user-defined order until a match is
+ found. A rule matches if 'module-name', 'rule-type', and
+ 'access-operations' match the request. If a rule
+ matches, the 'action' leaf determines whether or not
+ access is granted.";
+
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule.";
+ }
+
+ leaf module-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ default "*";
+ description
+ "Name of the module associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ object being accessed is defined in the module with the
+ specified module name.";
+ }
+ choice rule-type {
+ description
+ "This choice matches if all leafs present in the rule
+ match the request. If no leafs are present, the
+ choice matches all requests.";
+ case protocol-operation {
+ leaf rpc-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if
+ its value equals the requested protocol operation
+ name.";
+ }
+ }
+ case notification {
+ leaf notification-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if its
+ value equals the requested notification name.";
+ }
+ }
+
+ case data-node {
+ leaf path {
+ type node-instance-identifier;
+ mandatory true;
+ description
+ "Data node instance-identifier associated with the
+ data node, action, or notification controlled by
+ this rule.
+
+ Configuration data or state data
+ instance-identifiers start with a top-level
+ data node. A complete instance-identifier is
+ required for this type of path value.
+
+ The special value '/' refers to all possible
+ datastore contents.";
+ }
+ }
+ }
+
+ leaf access-operations {
+ type union {
+ type matchall-string-type;
+ type access-operations-type;
+ }
+ default "*";
+ description
+ "Access operations associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ bit corresponding to the requested operation is set.";
+ }
+
+ leaf action {
+ type action-type;
+ mandatory true;
+ description
+ "The access control action associated with the
+ rule. If a rule has been determined to match a
+ particular request, then this object is used
+ to determine whether to permit or deny the
+ request.";
+ }
+
+ leaf comment {
+ type string;
+ description
+ "A textual description of the access rule.";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang b/tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang
new file mode 100644
index 0000000..31dce50
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang
@@ -0,0 +1,385 @@
+ module ietf-netconf-nmda {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda";
+ prefix ncds;
+
+ import ietf-yang-types {
+ prefix yang;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+ import ietf-inet-types {
+ prefix inet;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+ import ietf-datastores {
+ prefix ds;
+ reference
+ "RFC 8342: Network Management Datastore Architecture
+ (NMDA)";
+ }
+ import ietf-origin {
+ prefix or;
+ reference
+ "RFC 8342: Network Management Datastore Architecture
+ (NMDA)";
+ }
+ import ietf-netconf {
+ prefix nc;
+ reference
+ "RFC 6241: Network Configuration Protocol (NETCONF)";
+ }
+ import ietf-netconf-with-defaults {
+ prefix ncwd;
+ reference
+ "RFC 6243: With-defaults Capability for NETCONF";
+ }
+
+ organization
+ "IETF NETCONF Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+
+ WG List: <mailto:netconf@ietf.org>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+
+ Author: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Author: Phil Shafer
+ <mailto:phil@juniper.net>
+
+ Author: Kent Watsen
+ <mailto:kent+ietf@watsen.net>
+
+ Author: Robert Wilton
+ <mailto:rwilton@cisco.com>";
+ description
+ "This YANG module defines a set of NETCONF operations to support
+ the Network Management Datastore Architecture (NMDA).
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.
+
+ Copyright (c) 2019 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Simplified BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8526; see
+ the RFC itself for full legal notices.";
+
+ revision 2019-01-07 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8526: NETCONF Extensions to Support the Network Management
+ Datastore Architecture";
+ }
+
+ feature origin {
+ description
+ "Indicates that the server supports the 'origin' annotation.";
+ reference
+ "RFC 8342: Network Management Datastore Architecture (NMDA)";
+ }
+
+ feature with-defaults {
+ description
+ "NETCONF :with-defaults capability. If the server advertises
+ the :with-defaults capability for a session, then this
+ feature must also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference
+ "RFC 6243: With-defaults Capability for NETCONF, Section 4; and
+ RFC 8526: NETCONF Extensions to Support the Network Management
+ Datastore Architecture, Section 3.1.1.2";
+ }
+
+ rpc get-data {
+ description
+ "Retrieve data from an NMDA datastore. The content returned
+ by get-data must satisfy all filters, i.e., the filter
+ criteria are logically ANDed.
+
+ Any ancestor nodes (including list keys) of nodes selected by
+ the filters are included in the response.
+
+ The 'with-origin' parameter is only valid for an operational
+ datastore. If 'with-origin' is used with an invalid
+ datastore, then the server MUST return an <rpc-error> element
+ with an <error-tag> value of 'invalid-value'.
+
+ The 'with-defaults' parameter only applies to the operational
+ datastore if the NETCONF :with-defaults and
+ :with-operational-defaults capabilities are both advertised.
+ If the 'with-defaults' parameter is present in a request for
+ which it is not supported, then the server MUST return an
+ <rpc-error> element with an <error-tag> value of
+ 'invalid-value'.";
+ input {
+ leaf datastore {
+ type ds:datastore-ref;
+ mandatory true;
+ description
+ "Datastore from which to retrieve data.
+
+ If the datastore is not supported by the server, then the
+ server MUST return an <rpc-error> element with an
+ <error-tag> value of 'invalid-value'.";
+ }
+ choice filter-spec {
+ description
+ "The content filter specification for this request.";
+ anydata subtree-filter {
+ description
+ "This parameter identifies the portions of the
+ target datastore to retrieve.";
+ reference
+ "RFC 6241: Network Configuration Protocol (NETCONF),
+ Section 6";
+ }
+ leaf xpath-filter {
+ if-feature "nc:xpath";
+ type yang:xpath1.0;
+ description
+ "This parameter contains an XPath expression identifying
+ the portions of the target datastore to retrieve.
+
+ If the expression returns a node-set, all nodes in the
+ node-set are selected by the filter. Otherwise, if the
+ expression does not return a node-set, then the
+ <get-data> operation fails.
+
+ The expression is evaluated in the following XPath
+ context:
+
+ o The set of namespace declarations are those in
+ scope on the 'xpath-filter' leaf element.
+
+ o The set of variable bindings is empty.
+
+ o The function library is the core function library,
+ and the XPath functions are defined in Section 10
+ of RFC 7950.
+
+ o The context node is the root node of the target
+ datastore.";
+ }
+ }
+ leaf config-filter {
+ type boolean;
+ description
+ "Filter for nodes with the given value for their 'config'
+ property. When this leaf is set to 'true', only 'config
+ true' nodes are selected, and when set to 'false', only
+ 'config false' nodes are selected. If this leaf is not
+ present, no nodes are filtered.";
+ }
+ choice origin-filters {
+ when 'derived-from-or-self(datastore, "ds:operational")';
+ if-feature "origin";
+ description
+ "Filters configuration nodes based on the 'origin'
+ annotation. Configuration nodes that do not have an
+ 'origin' annotation are treated as if they have the
+ 'origin' annotation 'or:unknown'.
+
+ System state nodes are not affected by origin-filters and
+ thus not filtered. Note that system state nodes can be
+ filtered with the 'config-filter' leaf.";
+
+ leaf-list origin-filter {
+ type or:origin-ref;
+ description
+ "Filter based on the 'origin' annotation. A
+ configuration node matches the filter if its 'origin'
+ annotation is derived from or equal to any of the given
+ filter values.";
+ }
+ leaf-list negated-origin-filter {
+ type or:origin-ref;
+ description
+ "Filter based on the 'origin' annotation. A
+ configuration node matches the filter if its 'origin'
+ annotation is neither derived from nor equal to any of
+ the given filter values.";
+ }
+ }
+ leaf max-depth {
+ type union {
+ type uint16 {
+ range "1..65535";
+ }
+ type enumeration {
+ enum unbounded {
+ description
+ "All descendant nodes are included.";
+ }
+ }
+ }
+ default "unbounded";
+ description
+ "For each node selected by the filters, this parameter
+ selects how many conceptual subtree levels should be
+ returned in the reply. If the depth is 1, the reply
+ includes just the selected nodes but no children. If the
+ depth is 'unbounded', all descendant nodes are included.";
+ }
+ leaf with-origin {
+ when 'derived-from-or-self(../datastore, "ds:operational")';
+ if-feature "origin";
+ type empty;
+ description
+ "If this parameter is present, the server will return
+ the 'origin' annotation for the nodes that have one.";
+ }
+ uses ncwd:with-defaults-parameters {
+ if-feature "with-defaults";
+ }
+ }
+ output {
+ anydata data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data
+ container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+
+ rpc edit-data {
+ description
+ "Edit data in an NMDA datastore.
+
+ If an error condition occurs such that an error severity
+ <rpc-error> element is generated, the server will stop
+ processing the <edit-data> operation and restore the
+ specified configuration to its complete state at
+ the start of this <edit-data> operation.";
+ input {
+ leaf datastore {
+ type ds:datastore-ref;
+ mandatory true;
+ description
+ "Datastore that is the target of the <edit-data> operation.
+
+ If the target datastore is not writable, or is not
+ supported by the server, then the server MUST return an
+ <rpc-error> element with an <error-tag> value of
+ 'invalid-value'.";
+ }
+ leaf default-operation {
+ type enumeration {
+ enum merge {
+ description
+ "The default operation is merge.";
+ }
+ enum replace {
+ description
+ "The default operation is replace.";
+ }
+ enum none {
+ description
+ "There is no default operation.";
+ }
+ }
+ default "merge";
+ description
+ "The default operation to use.";
+ }
+ choice edit-content {
+ mandatory true;
+ description
+ "The content for the edit operation.";
+ anydata config {
+ description
+ "Inline config content.";
+ }
+ leaf url {
+ if-feature "nc:url";
+ type inet:uri;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ /*
+ * Augment the <lock> and <unlock> operations with a
+ * "datastore" parameter.
+ */
+
+ augment "/nc:lock/nc:input/nc:target/nc:config-target" {
+ description
+ "Add NMDA datastore as target.";
+ leaf datastore {
+ type ds:datastore-ref;
+ description
+ "Datastore to lock.
+
+ The <lock> operation is only supported on writable
+ datastores.
+
+ If the <lock> operation is not supported by the server on
+ the specified target datastore, then the server MUST return
+ an <rpc-error> element with an <error-tag> value of
+ 'invalid-value'.";
+ }
+ }
+
+ augment "/nc:unlock/nc:input/nc:target/nc:config-target" {
+ description
+ "Add NMDA datastore as target.";
+ leaf datastore {
+ type ds:datastore-ref;
+ description
+ "Datastore to unlock.
+
+ The <unlock> operation is only supported on writable
+ datastores.
+
+ If the <unlock> operation is not supported by the server on
+ the specified target datastore, then the server MUST return
+ an <rpc-error> element with an <error-tag> value of
+ 'invalid-value'.";
+ }
+ }
+
+ /*
+ * Augment the <validate> operation with a
+ * "datastore" parameter.
+ */
+
+ augment "/nc:validate/nc:input/nc:source/nc:config-source" {
+ description
+ "Add NMDA datastore as source.";
+ leaf datastore {
+ type ds:datastore-ref;
+ description
+ "Datastore to validate.
+
+ The <validate> operation is supported only on configuration
+ datastores.
+
+ If the <validate> operation is not supported by the server
+ on the specified target datastore, then the server MUST
+ return an <rpc-error> element with an <error-tag> value of
+ 'invalid-value'.";
+ }
+ }
+ }
diff --git a/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang b/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang
new file mode 100644
index 0000000..e19d2b3
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang
@@ -0,0 +1,140 @@
+module ietf-netconf-with-defaults {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults";
+
+ prefix ncwd;
+
+ import ietf-netconf { prefix nc; }
+
+ organization
+ "IETF NETCONF (Network Configuration Protocol) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>
+
+ Editor: Balazs Lengyel
+ <balazs.lengyel@ericsson.com>";
+
+ description
+ "This module defines an extension to the NETCONF protocol
+ that allows the NETCONF client to control how default
+ values are handled by the server in particular NETCONF
+ operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6243; see
+ the RFC itself for full legal notices.";
+
+ revision 2011-06-01 {
+ description
+ "Initial version.";
+ reference
+ "RFC 6243: With-defaults Capability for NETCONF";
+ }
+
+ typedef with-defaults-mode {
+ description
+ "Possible modes to report default data.";
+ reference
+ "RFC 6243; Section 3.";
+ type enumeration {
+ enum report-all {
+ description
+ "All default data is reported.";
+ reference
+ "RFC 6243; Section 3.1";
+ }
+ enum report-all-tagged {
+ description
+ "All default data is reported.
+ Any nodes considered to be default data
+ will contain a 'default' XML attribute,
+ set to 'true' or '1'.";
+ reference
+ "RFC 6243; Section 3.4";
+ }
+ enum trim {
+ description
+ "Values are not reported if they contain the default.";
+ reference
+ "RFC 6243; Section 3.2";
+ }
+ enum explicit {
+ description
+ "Report values that contain the definition of
+ explicitly set data.";
+ reference
+ "RFC 6243; Section 3.3";
+ }
+ }
+ }
+
+ grouping with-defaults-parameters {
+ description
+ "Contains the <with-defaults> parameter for control
+ of defaults in NETCONF retrieval operations.";
+
+ leaf with-defaults {
+ description
+ "The explicit defaults processing mode requested.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ type with-defaults-mode;
+ }
+ }
+
+ // extending the get-config operation
+ augment /nc:get-config/nc:input {
+ description
+ "Adds the <with-defaults> parameter to the
+ input of the NETCONF <get-config> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+ // extending the get operation
+ augment /nc:get/nc:input {
+ description
+ "Adds the <with-defaults> parameter to
+ the input of the NETCONF <get> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+ // extending the copy-config operation
+ augment /nc:copy-config/nc:input {
+ description
+ "Adds the <with-defaults> parameter to
+ the input of the NETCONF <copy-config> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+}
diff --git a/tests/modules/yang/ietf-netconf@2011-06-01.yang b/tests/modules/yang/ietf-netconf@2011-06-01.yang
new file mode 100644
index 0000000..6449421
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf@2011-06-01.yang
@@ -0,0 +1,934 @@
+module ietf-netconf {
+
+ // the namespace for NETCONF XML definitions is unchanged
+ // from RFC 4741, which this document replaces
+ namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+ prefix nc;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import ietf-netconf-acm { prefix nacm; }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Martin Bjorklund
+ <mbj@tail-f.com>
+
+ Editor: Juergen Schoenwaelder
+ <j.schoenwaelder@jacobs-university.de>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>";
+ description
+ "NETCONF Protocol Data Types and Protocol Operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6241; see
+ the RFC itself for full legal notices.";
+
+ revision 2011-06-01 {
+ description
+ "Initial revision;
+ 2013-09-29: Updated to include NACM attributes,
+ as specified in RFC 6536: sec 3.2.5 and 3.2.8";
+ reference
+ "RFC 6241: Network Configuration Protocol";
+ }
+
+ extension get-filter-element-attributes {
+ description
+ "If this extension is present within an 'anyxml'
+ statement named 'filter', which must be conceptually
+ defined within the RPC input section for the <get>
+ and <get-config> protocol operations, then the
+ following unqualified XML attribute is supported
+ within the <filter> element, within a <get> or
+ <get-config> protocol operation:
+
+ type : optional attribute with allowed
+ value strings 'subtree' and 'xpath'.
+ If missing, the default value is 'subtree'.
+
+ If the 'xpath' feature is supported, then the
+ following unqualified XML attribute is
+ also supported:
+
+ select: optional attribute containing a
+ string representing an XPath expression.
+ The 'type' attribute must be equal to 'xpath'
+ if this attribute is present.";
+ }
+
+ // NETCONF capabilities defined as features
+ feature writable-running {
+ description
+ "NETCONF :writable-running capability;
+ If the server advertises the :writable-running
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.2";
+ }
+
+ feature candidate {
+ description
+ "NETCONF :candidate capability;
+ If the server advertises the :candidate
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.3";
+ }
+
+ feature confirmed-commit {
+ if-feature candidate;
+ description
+ "NETCONF :confirmed-commit:1.1 capability;
+ If the server advertises the :confirmed-commit:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+
+ reference "RFC 6241, Section 8.4";
+ }
+
+ feature rollback-on-error {
+ description
+ "NETCONF :rollback-on-error capability;
+ If the server advertises the :rollback-on-error
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.5";
+ }
+
+ feature validate {
+ description
+ "NETCONF :validate:1.1 capability;
+ If the server advertises the :validate:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.6";
+ }
+
+ feature startup {
+ description
+ "NETCONF :startup capability;
+ If the server advertises the :startup
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.7";
+ }
+
+ feature url {
+ description
+ "NETCONF :url capability;
+ If the server advertises the :url
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.8";
+ }
+
+ feature xpath {
+ description
+ "NETCONF :xpath capability;
+ If the server advertises the :xpath
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.9";
+ }
+
+ // NETCONF Simple Types
+
+ typedef session-id-type {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "NETCONF Session Id";
+ }
+
+ typedef session-id-or-zero-type {
+ type uint32;
+ description
+ "NETCONF Session Id or Zero to indicate none";
+ }
+ typedef error-tag-type {
+ type enumeration {
+ enum in-use {
+ description
+ "The request requires a resource that
+ already is in use.";
+ }
+ enum invalid-value {
+ description
+ "The request specifies an unacceptable value for one
+ or more parameters.";
+ }
+ enum too-big {
+ description
+ "The request or response (that would be generated) is
+ too large for the implementation to handle.";
+ }
+ enum missing-attribute {
+ description
+ "An expected attribute is missing.";
+ }
+ enum bad-attribute {
+ description
+ "An attribute value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-attribute {
+ description
+ "An unexpected attribute is present.";
+ }
+ enum missing-element {
+ description
+ "An expected element is missing.";
+ }
+ enum bad-element {
+ description
+ "An element value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-element {
+ description
+ "An unexpected element is present.";
+ }
+ enum unknown-namespace {
+ description
+ "An unexpected namespace is present.";
+ }
+ enum access-denied {
+ description
+ "Access to the requested protocol operation or
+ data model is denied because authorization failed.";
+ }
+ enum lock-denied {
+ description
+ "Access to the requested lock is denied because the
+ lock is currently held by another entity.";
+ }
+ enum resource-denied {
+ description
+ "Request could not be completed because of
+ insufficient resources.";
+ }
+ enum rollback-failed {
+ description
+ "Request to roll back some configuration change (via
+ rollback-on-error or <discard-changes> operations)
+ was not completed for some reason.";
+
+ }
+ enum data-exists {
+ description
+ "Request could not be completed because the relevant
+ data model content already exists. For example,
+ a 'create' operation was attempted on data that
+ already exists.";
+ }
+ enum data-missing {
+ description
+ "Request could not be completed because the relevant
+ data model content does not exist. For example,
+ a 'delete' operation was attempted on
+ data that does not exist.";
+ }
+ enum operation-not-supported {
+ description
+ "Request could not be completed because the requested
+ operation is not supported by this implementation.";
+ }
+ enum operation-failed {
+ description
+ "Request could not be completed because the requested
+ operation failed for some reason not covered by
+ any other error condition.";
+ }
+ enum partial-operation {
+ description
+ "This error-tag is obsolete, and SHOULD NOT be sent
+ by servers conforming to this document.";
+ }
+ enum malformed-message {
+ description
+ "A message could not be handled because it failed to
+ be parsed correctly. For example, the message is not
+ well-formed XML or it uses an invalid character set.";
+ }
+ }
+ description "NETCONF Error Tag";
+ reference "RFC 6241, Appendix A";
+ }
+
+ typedef error-severity-type {
+ type enumeration {
+ enum error {
+ description "Error severity";
+ }
+ enum warning {
+ description "Warning severity";
+ }
+ }
+ description "NETCONF Error Severity";
+ reference "RFC 6241, Section 4.3";
+ }
+
+ typedef edit-operation-type {
+ type enumeration {
+ enum merge {
+ description
+ "The configuration data identified by the
+ element containing this attribute is merged
+ with the configuration at the corresponding
+ level in the configuration datastore identified
+ by the target parameter.";
+ }
+ enum replace {
+ description
+ "The configuration data identified by the element
+ containing this attribute replaces any related
+ configuration in the configuration datastore
+ identified by the target parameter. If no such
+ configuration data exists in the configuration
+ datastore, it is created. Unlike a
+ <copy-config> operation, which replaces the
+ entire target configuration, only the configuration
+ actually present in the config parameter is affected.";
+ }
+ enum create {
+ description
+ "The configuration data identified by the element
+ containing this attribute is added to the
+ configuration if and only if the configuration
+ data does not already exist in the configuration
+ datastore. If the configuration data exists, an
+ <rpc-error> element is returned with an
+ <error-tag> value of 'data-exists'.";
+ }
+ enum delete {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if and only if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, an <rpc-error> element is returned with
+ an <error-tag> value of 'data-missing'.";
+ }
+ enum remove {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, the 'remove' operation is silently ignored
+ by the server.";
+ }
+ }
+ default "merge";
+ description "NETCONF 'operation' attribute values";
+ reference "RFC 6241, Section 7.2";
+ }
+
+ // NETCONF Standard Protocol Operations
+
+ rpc get-config {
+ description
+ "Retrieve all or part of a specified configuration.";
+
+ reference "RFC 6241, Section 7.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to retrieve.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration to retrieve.";
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.
+ This is optional-to-implement on the server because
+ not all servers will support filtering for this
+ datastore.";
+ }
+ }
+ }
+
+ anyxml filter {
+ description
+ "Subtree or XPath filter to use.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data container
+ indicates that the request did not produce any results.";
+ }
+ }
+ }
+
+ rpc edit-config {
+ description
+ "The <edit-config> operation loads all or part of a specified
+ configuration to the specified target configuration.";
+
+ reference "RFC 6241, Section 7.2";
+
+ input {
+ container target {
+ description
+ "Particular configuration to edit.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ }
+ }
+
+ leaf default-operation {
+ type enumeration {
+ enum merge {
+ description
+ "The default operation is merge.";
+ }
+ enum replace {
+ description
+ "The default operation is replace.";
+ }
+ enum none {
+ description
+ "There is no default operation.";
+ }
+ }
+ default "merge";
+ description
+ "The default operation to use.";
+ }
+
+ leaf test-option {
+ if-feature validate;
+ type enumeration {
+ enum test-then-set {
+ description
+ "The server will test and then set if no errors.";
+ }
+ enum set {
+ description
+ "The server will set without a test first.";
+ }
+
+ enum test-only {
+ description
+ "The server will only test and not set, even
+ if there are no errors.";
+ }
+ }
+ default "test-then-set";
+ description
+ "The test option to use.";
+ }
+
+ leaf error-option {
+ type enumeration {
+ enum stop-on-error {
+ description
+ "The server will stop on errors.";
+ }
+ enum continue-on-error {
+ description
+ "The server may continue on errors.";
+ }
+ enum rollback-on-error {
+ description
+ "The server will roll back on errors.
+ This value can only be used if the 'rollback-on-error'
+ feature is supported.";
+ }
+ }
+ default "stop-on-error";
+ description
+ "The error option to use.";
+ }
+
+ choice edit-content {
+ mandatory true;
+ description
+ "The content for the edit operation.";
+
+ anyxml config {
+ description
+ "Inline Config content.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ rpc copy-config {
+ description
+ "Create or replace an entire configuration datastore with the
+ contents of another complete configuration datastore.";
+
+ reference "RFC 6241, Section 7.3";
+
+ input {
+ container target {
+ description
+ "Particular configuration to copy to.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target of the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config target.
+ This is optional-to-implement on the server.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+
+ container source {
+ description
+ "Particular configuration to copy from.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source for the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc delete-config {
+ nacm:default-deny-all;
+ description
+ "Delete a configuration datastore.";
+
+ reference "RFC 6241, Section 7.4";
+
+ input {
+ container target {
+ description
+ "Particular configuration to delete.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to delete.";
+
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc lock {
+ description
+ "The lock operation allows the client to lock the configuration
+ system of a device.";
+
+ reference "RFC 6241, Section 7.5";
+
+ input {
+ container target {
+ description
+ "Particular configuration to lock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to lock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc unlock {
+ description
+ "The unlock operation is used to release a configuration lock,
+ previously obtained with the 'lock' operation.";
+
+ reference "RFC 6241, Section 7.6";
+
+ input {
+ container target {
+ description
+ "Particular configuration to unlock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to unlock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc get {
+ description
+ "Retrieve running configuration and device state information.";
+
+ reference "RFC 6241, Section 7.7";
+
+ input {
+ anyxml filter {
+ description
+ "This parameter specifies the portion of the system
+ configuration and state data to retrieve.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+
+ rpc close-session {
+ description
+ "Request graceful termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.8";
+ }
+
+ rpc kill-session {
+ nacm:default-deny-all;
+ description
+ "Force the termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.9";
+
+ input {
+ leaf session-id {
+ type session-id-type;
+ mandatory true;
+ description
+ "Particular session to kill.";
+ }
+ }
+ }
+
+ rpc commit {
+ if-feature candidate;
+
+ description
+ "Commit the candidate configuration as the device's new
+ current configuration.";
+
+ reference "RFC 6241, Section 8.3.4.1";
+
+ input {
+ leaf confirmed {
+ if-feature confirmed-commit;
+ type empty;
+ description
+ "Requests a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf confirm-timeout {
+ if-feature confirmed-commit;
+ type uint32 {
+ range "1..max";
+ }
+ units "seconds";
+ default "600"; // 10 minutes
+ description
+ "The timeout interval for a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is used to make a confirmed commit
+ persistent. A persistent confirmed commit is not aborted
+ if the NETCONF session terminates. The only way to abort
+ a persistent confirmed commit is to let the timer expire,
+ or to use the <cancel-commit> operation.
+
+ The value of this parameter is a token that must be given
+ in the 'persist-id' parameter of <commit> or
+ <cancel-commit> operations in order to confirm or cancel
+ the persistent confirmed commit.
+
+ The token should be a random string.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist-id {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is given in order to commit a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ }
+ }
+
+ rpc discard-changes {
+ if-feature candidate;
+
+ description
+ "Revert the candidate configuration to the current
+ running configuration.";
+ reference "RFC 6241, Section 8.3.4.2";
+ }
+
+ rpc cancel-commit {
+ if-feature confirmed-commit;
+ description
+ "This operation is used to cancel an ongoing confirmed commit.
+ If the confirmed commit is persistent, the parameter
+ 'persist-id' must be given, and it must match the value of the
+ 'persist' parameter.";
+ reference "RFC 6241, Section 8.4.4.1";
+
+ input {
+ leaf persist-id {
+ type string;
+ description
+ "This parameter is given in order to cancel a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ }
+ }
+ }
+
+ rpc validate {
+ if-feature validate;
+
+ description
+ "Validates the contents of the specified configuration.";
+
+ reference "RFC 6241, Section 8.6.4.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to validate.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source to validate.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/tests/modules/yang/ietf-origin@2018-02-14.yang b/tests/modules/yang/ietf-origin@2018-02-14.yang
new file mode 100644
index 0000000..3080c91
--- /dev/null
+++ b/tests/modules/yang/ietf-origin@2018-02-14.yang
@@ -0,0 +1,147 @@
+module ietf-origin {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-origin";
+ prefix or;
+
+ import ietf-yang-metadata {
+ prefix md;
+ }
+
+ organization
+ "IETF Network Modeling (NETMOD) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netmod/>
+
+ WG List: <mailto:netmod@ietf.org>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+
+ Author: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Author: Phil Shafer
+ <mailto:phil@juniper.net>
+
+ Author: Kent Watsen
+ <mailto:kwatsen@juniper.net>
+
+ Author: Rob Wilton
+ <rwilton@cisco.com>";
+
+ description
+ "This YANG module defines an 'origin' metadata annotation and a
+ set of identities for the origin value.
+
+ Copyright (c) 2018 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Simplified BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8342
+ (https://www.rfc-editor.org/info/rfc8342); see the RFC itself
+ for full legal notices.";
+
+ revision 2018-02-14 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8342: Network Management Datastore Architecture (NMDA)";
+ }
+
+ /*
+ * Identities
+ */
+
+ identity origin {
+ description
+ "Abstract base identity for the origin annotation.";
+ }
+
+ identity intended {
+ base origin;
+ description
+ "Denotes configuration from the intended configuration
+ datastore.";
+ }
+
+ identity dynamic {
+ base origin;
+ description
+ "Denotes configuration from a dynamic configuration
+ datastore.";
+ }
+
+ identity system {
+ base origin;
+ description
+ "Denotes configuration originated by the system itself.
+
+ Examples of system configuration include applied configuration
+ for an always-existing loopback interface, or interface
+ configuration that is auto-created due to the hardware
+ currently present in the device.";
+ }
+
+ identity learned {
+ base origin;
+ description
+ "Denotes configuration learned from protocol interactions with
+ other devices, instead of via either the intended
+ configuration datastore or any dynamic configuration
+ datastore.
+
+ Examples of protocols that provide learned configuration
+ include link-layer negotiations, routing protocols, and
+ DHCP.";
+ }
+
+ identity default {
+ base origin;
+ description
+ "Denotes configuration that does not have a configured or
+ learned value but has a default value in use. Covers both
+ values defined in a 'default' statement and values defined
+ via an explanation in a 'description' statement.";
+ }
+
+ identity unknown {
+ base origin;
+ description
+ "Denotes configuration for which the system cannot identify the
+ origin.";
+ }
+
+ /*
+ * Type definitions
+ */
+
+ typedef origin-ref {
+ type identityref {
+ base origin;
+ }
+ description
+ "An origin identity reference.";
+ }
+
+ /*
+ * Metadata annotations
+ */
+
+ md:annotation origin {
+ type origin-ref;
+ description
+ "The 'origin' annotation can be present on any configuration
+ data node in the operational state datastore. It specifies
+ from where the node originated. If not specified for a given
+ configuration data node, then the origin is the same as the
+ origin of its parent node in the data tree. The origin for
+ any top-level configuration data nodes must be specified.";
+ }
+}
diff --git a/tests/modules/yang/ietf-restconf@2017-01-26.yang b/tests/modules/yang/ietf-restconf@2017-01-26.yang
new file mode 100644
index 0000000..b47455b
--- /dev/null
+++ b/tests/modules/yang/ietf-restconf@2017-01-26.yang
@@ -0,0 +1,278 @@
+module ietf-restconf {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+ prefix "rc";
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+
+ Author: Kent Watsen
+ <mailto:kwatsen@juniper.net>";
+
+ description
+ "This module contains conceptual YANG specifications
+ for basic RESTCONF media type definitions used in
+ RESTCONF protocol messages.
+
+ Note that the YANG definitions within this module do not
+ represent configuration data of any kind.
+ The 'restconf-media-type' YANG extension statement
+ provides a normative syntax for XML and JSON
+ message-encoding purposes.
+
+ Copyright (c) 2017 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8040; see
+ the RFC itself for full legal notices.";
+
+ revision 2017-01-26 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8040: RESTCONF Protocol.";
+ }
+
+ extension yang-data {
+ argument name {
+ yin-element true;
+ }
+ description
+ "This extension is used to specify a YANG data template that
+ represents conceptual data defined in YANG. It is
+ intended to describe hierarchical data independent of
+ protocol context or specific message-encoding format.
+ Data definition statements within a yang-data extension
+ specify the generic syntax for the specific YANG data
+ template, whose name is the argument of the 'yang-data'
+ extension statement.
+
+ Note that this extension does not define a media type.
+ A specification using this extension MUST specify the
+ message-encoding rules, including the content media type.
+
+ The mandatory 'name' parameter value identifies the YANG
+ data template that is being defined. It contains the
+ template name.
+
+ This extension is ignored unless it appears as a top-level
+ statement. It MUST contain data definition statements
+ that result in exactly one container data node definition.
+ An instance of a YANG data template can thus be translated
+ into an XML instance document, whose top-level element
+ corresponds to the top-level container.
+ The module name and namespace values for the YANG module using
+ the extension statement are assigned to instance document data
+ conforming to the data definition statements within
+ this extension.
+
+ The substatements of this extension MUST follow the
+ 'data-def-stmt' rule in the YANG ABNF.
+
+ The XPath document root is the extension statement itself,
+ such that the child nodes of the document root are
+ represented by the data-def-stmt substatements within
+ this extension. This conceptual document is the context
+ for the following YANG statements:
+
+ - must-stmt
+ - when-stmt
+ - path-stmt
+ - min-elements-stmt
+ - max-elements-stmt
+ - mandatory-stmt
+ - unique-stmt
+ - ordered-by
+ - instance-identifier data type
+
+ The following data-def-stmt substatements are constrained
+ when used within a 'yang-data' extension statement.
+
+ - The list-stmt is not required to have a key-stmt defined.
+ - The if-feature-stmt is ignored if present.
+ - The config-stmt is ignored if present.
+ - The available identity values for any 'identityref'
+ leaf or leaf-list nodes are limited to the module
+ containing this extension statement and the modules
+ imported into that module.
+ ";
+ }
+
+ rc:yang-data yang-errors {
+ uses errors;
+ }
+
+ rc:yang-data yang-api {
+ uses restconf;
+ }
+
+ grouping errors {
+ description
+ "A grouping that contains a YANG container
+ representing the syntax and semantics of a
+ YANG Patch error report within a response message.";
+
+ container errors {
+ description
+ "Represents an error report returned by the server if
+ a request results in an error.";
+
+ list error {
+ description
+ "An entry containing information about one
+ specific error that occurred while processing
+ a RESTCONF request.";
+ reference
+ "RFC 6241, Section 4.3.";
+
+ leaf error-type {
+ type enumeration {
+ enum transport {
+ description
+ "The transport layer.";
+ }
+ enum rpc {
+ description
+ "The rpc or notification layer.";
+ }
+ enum protocol {
+ description
+ "The protocol operation layer.";
+ }
+ enum application {
+ description
+ "The server application layer.";
+ }
+ }
+ mandatory true;
+ description
+ "The protocol layer where the error occurred.";
+ }
+
+ leaf error-tag {
+ type string;
+ mandatory true;
+ description
+ "The enumerated error-tag.";
+ }
+
+ leaf error-app-tag {
+ type string;
+ description
+ "The application-specific error-tag.";
+ }
+
+ leaf error-path {
+ type instance-identifier;
+ description
+ "The YANG instance identifier associated
+ with the error node.";
+ }
+
+ leaf error-message {
+ type string;
+ description
+ "A message describing the error.";
+ }
+
+ anydata error-info {
+ description
+ "This anydata value MUST represent a container with
+ zero or more data nodes representing additional
+ error information.";
+ }
+ }
+ }
+ }
+
+ grouping restconf {
+ description
+ "Conceptual grouping representing the RESTCONF
+ root resource.";
+
+ container restconf {
+ description
+ "Conceptual container representing the RESTCONF
+ root resource.";
+
+ container data {
+ description
+ "Container representing the datastore resource.
+ Represents the conceptual root of all state data
+ and configuration data supported by the server.
+ The child nodes of this container can be any data
+ resources that are defined as top-level data nodes
+ from the YANG modules advertised by the server in
+ the 'ietf-yang-library' module.";
+ }
+
+ container operations {
+ description
+ "Container for all operation resources.
+
+ Each resource is represented as an empty leaf with the
+ name of the RPC operation from the YANG 'rpc' statement.
+
+ For example, the 'system-restart' RPC operation defined
+ in the 'ietf-system' module would be represented as
+ an empty leaf in the 'ietf-system' namespace. This is
+ a conceptual leaf and will not actually be found in
+ the module:
+
+ module ietf-system {
+ leaf system-reset {
+ type empty;
+ }
+ }
+
+ To invoke the 'system-restart' RPC operation:
+
+ POST /restconf/operations/ietf-system:system-restart
+
+ To discover the RPC operations supported by the server:
+
+ GET /restconf/operations
+
+ In XML, the YANG module namespace identifies the module:
+
+ <system-restart
+ xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/>
+
+ In JSON, the YANG module name identifies the module:
+
+ { 'ietf-system:system-restart' : [null] }
+ ";
+ }
+ leaf yang-library-version {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}';
+ }
+ config false;
+ mandatory true;
+ description
+ "Identifies the revision date of the 'ietf-yang-library'
+ module that is implemented by this RESTCONF server.
+ Indicates the year, month, and day in YYYY-MM-DD
+ numeric format.";
+ }
+ }
+ }
+
+}
diff --git a/tests/modules/yang/notifications@2008-07-14.yang b/tests/modules/yang/notifications@2008-07-14.yang
new file mode 100644
index 0000000..b696f39
--- /dev/null
+++ b/tests/modules/yang/notifications@2008-07-14.yang
@@ -0,0 +1,95 @@
+module notifications {
+
+ namespace "urn:ietf:params:xml:ns:netconf:notification:1.0";
+ prefix "ncEvent";
+
+ import ietf-yang-types { prefix yang; }
+
+ organization
+ "IETF NETCONF WG";
+
+ contact
+ "netconf@ops.ietf.org";
+
+ description
+ "Conversion of the 'ncEvent' XSD in the
+ NETCONF Notifications RFC.";
+
+ reference
+ "RFC 5277.";
+
+ revision 2008-07-14 {
+ description "RFC 5277 version.";
+ }
+
+ typedef streamNameType {
+ description
+ "The name of an event stream.";
+ type string;
+ }
+
+ rpc create-subscription {
+ description
+ "The command to create a notification subscription. It
+ takes as argument the name of the notification stream
+ and filter. Both of those options limit the content of
+ the subscription. In addition, there are two time-related
+ parameters, startTime and stopTime, which can be used to
+ select the time interval of interest to the notification
+ replay feature.";
+
+ input {
+ leaf stream {
+ description
+ "An optional parameter that indicates which stream of events
+ is of interest. If not present, then events in the default
+ NETCONF stream will be sent.";
+ type streamNameType;
+ default "NETCONF";
+ }
+
+ anyxml filter {
+ description
+ "An optional parameter that indicates which subset of all
+ possible events is of interest. The format of this
+ parameter is the same as that of the filter parameter
+ in the NETCONF protocol operations. If not present,
+ all events not precluded by other parameters will
+ be sent.";
+ }
+
+ leaf startTime {
+ description
+ "A parameter used to trigger the replay feature and
+ indicates that the replay should start at the time
+ specified. If start time is not present, this is not a
+ replay subscription.";
+ type yang:date-and-time;
+ }
+
+ leaf stopTime {
+ // must ". >= ../startTime";
+ description
+ "An optional parameter used with the optional replay
+ feature to indicate the newest notifications of
+ interest. If stop time is not present, the notifications
+ will continue until the subscription is terminated.
+ Must be used with startTime.";
+ type yang:date-and-time;
+ }
+ }
+ }
+
+ /*container notification {
+ description "internal struct to start a notification";
+ config false;
+
+ leaf eventTime {
+ mandatory true;
+ type yang:date-and-time;
+ }
+
+ // eventType and any data content goes here
+ }*/
+}
+
diff --git a/tests/modules/yang/sm-extension.yang b/tests/modules/yang/sm-extension.yang
new file mode 100644
index 0000000..63d164a
--- /dev/null
+++ b/tests/modules/yang/sm-extension.yang
@@ -0,0 +1,17 @@
+module sm-extension {
+ yang-version 1.1;
+ namespace "urn:sm-ext";
+ prefix "sm-ext";
+
+ list tlist {
+ key "name";
+ leaf name {
+ type uint32;
+ }
+ }
+ container tcont {
+ leaf tleaf {
+ type uint32;
+ }
+ }
+}
diff --git a/tests/modules/yang/sm-mod.yang b/tests/modules/yang/sm-mod.yang
new file mode 100644
index 0000000..8ac834e
--- /dev/null
+++ b/tests/modules/yang/sm-mod.yang
@@ -0,0 +1,9 @@
+module sm-mod {
+ yang-version 1.1;
+ namespace "urn:sm-mod";
+ prefix "sm-mod";
+
+ import sm-modp {
+ prefix smp;
+ }
+}
diff --git a/tests/modules/yang/sm-modp.yang b/tests/modules/yang/sm-modp.yang
new file mode 100644
index 0000000..33f45b4
--- /dev/null
+++ b/tests/modules/yang/sm-modp.yang
@@ -0,0 +1,24 @@
+module sm-modp {
+ yang-version 1.1;
+ namespace "urn:sm-modp";
+ prefix "sm-modp";
+
+ import ietf-yang-schema-mount {
+ prefix yangmnt;
+ }
+
+ revision 2017-01-26;
+
+ container ncmp {
+ yangmnt:mount-point "root";
+ }
+
+ container not-compiled {
+ leaf first {
+ type string;
+ }
+ leaf second {
+ type string;
+ }
+ }
+}
diff --git a/tests/modules/yang/sm-rpcnotif.yang b/tests/modules/yang/sm-rpcnotif.yang
new file mode 100644
index 0000000..fda5442
--- /dev/null
+++ b/tests/modules/yang/sm-rpcnotif.yang
@@ -0,0 +1,25 @@
+module sm-rpcnotif {
+ yang-version 1.1;
+ namespace "urn:rpcnotif";
+ prefix "smrn";
+
+ container cont {
+ notification cn;
+ action cr {
+ input {
+ leaf in {
+ type string;
+ }
+ }
+ output {
+ leaf out {
+ type string;
+ }
+ }
+ }
+ }
+ rpc r1;
+ rpc r2;
+ notification n1;
+ notification n2;
+}
diff --git a/tests/modules/yang/sm.yang b/tests/modules/yang/sm.yang
new file mode 100644
index 0000000..3da9c9a
--- /dev/null
+++ b/tests/modules/yang/sm.yang
@@ -0,0 +1,39 @@
+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";
+ }
+ }
+ leaf target {
+ type string;
+ }
+
+ augment /if:interfaces/if:interface {
+ leaf sm-name {
+ type leafref {
+ path "/sm:target";
+ }
+ }
+ }
+}
diff --git a/tests/perf/CMakeLists.txt b/tests/perf/CMakeLists.txt
new file mode 100644
index 0000000..ec1dfb5
--- /dev/null
+++ b/tests/perf/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(format_sources
+ ${format_sources}
+ ${CMAKE_CURRENT_SOURCE_DIR}/perf.c
+ PARENT_SCOPE)
+
+add_executable(ly_perf ${CMAKE_CURRENT_SOURCE_DIR}/perf.c $<TARGET_OBJECTS:yangobj>)
+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)
+ target_link_libraries(ly_perf m)
+endif()
+
+add_test(NAME ly_perf_1000 COMMAND ly_perf 1000 10)
+add_test(NAME ly_perf_100000 COMMAND ly_perf 100000 3)
diff --git a/tests/perf/perf.c b/tests/perf/perf.c
new file mode 100644
index 0000000..e15a2c6
--- /dev/null
+++ b/tests/perf/perf.c
@@ -0,0 +1,814 @@
+/**
+ * @file perf.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief performance tests
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "libyang.h"
+#include "tests_config.h"
+
+#ifdef HAVE_CALLGRIND
+# include <valgrind/callgrind.h>
+#endif
+
+#define TEMP_FILE "perf_tmp"
+
+/**
+ * @brief Test state structure.
+ */
+struct test_state {
+ const struct lys_module *mod;
+ uint32_t count;
+ struct lyd_node *data1;
+ struct lyd_node *data2;
+};
+
+typedef LY_ERR (*setup_cb)(const struct lys_module *mod, uint32_t count, struct test_state *state);
+
+typedef LY_ERR (*test_cb)(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end);
+
+/**
+ * @brief Single test structure.
+ */
+struct test {
+ const char *name;
+ setup_cb setup;
+ test_cb test;
+};
+
+/**
+ * @brief Get current time as timespec.
+ *
+ * @param[out] ts Timespect to fill.
+ */
+static void
+time_get(struct timespec *ts)
+{
+#ifdef CLOCK_MONOTONIC_RAW
+ clock_gettime(CLOCK_MONOTONIC_RAW, ts);
+#elif defined (CLOCK_MONOTONIC)
+ clock_gettime(CLOCK_MONOTONIC, ts);
+#elif defined (CLOCK_REALTIME)
+ /* no monotonic clock available, return realtime */
+ clock_gettime(CLOCK_REALTIME, ts);
+#else
+ int rc;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ ts->tv_sec = (time_t)tv.tv_sec;
+ ts->tv_nsec = 1000L * (long)tv.tv_usec;
+#endif
+}
+
+/**
+ * @brief Get the difference of 2 timespecs in microseconds.
+ *
+ * @param[in] ts1 Smaller (older) timespec.
+ * @param[in] ts2 Larger (later) timespec.
+ * @return Difference of timespecs in usec.
+ */
+static uint64_t
+time_diff(const struct timespec *ts1, const struct timespec *ts2)
+{
+ uint64_t usec_diff = 0;
+ int64_t nsec_diff;
+
+ assert(ts1->tv_sec <= ts2->tv_sec);
+
+ /* seconds diff */
+ usec_diff += (ts2->tv_sec - ts1->tv_sec) * 1000000;
+
+ /* nanoseconds diff */
+ nsec_diff = ts2->tv_nsec - ts1->tv_nsec;
+ usec_diff += nsec_diff ? nsec_diff / 1000 : 0;
+
+ return usec_diff;
+}
+
+/**
+ * @brief Create data tree with list instances.
+ *
+ * @param[in] mod Module of the top-level node.
+ * @param[in] offset Starting offset of the identifier number values.
+ * @param[in] count Number of list instances to create, with increasing identifier numbers.
+ * @param[out] data Created data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+create_list_inst(const struct lys_module *mod, uint32_t offset, uint32_t count, struct lyd_node **data)
+{
+ LY_ERR ret;
+ uint32_t i;
+ char k1_val[32], k2_val[32], l_val[32], lfl_val[32];
+ struct lyd_node *list;
+
+ if ((ret = lyd_new_inner(NULL, mod, "cont", 0, data))) {
+ return ret;
+ }
+
+ for (i = 0; i < count; ++i) {
+ sprintf(k1_val, "%" PRIu32, i + offset);
+ sprintf(k2_val, "str%" PRIu32, i + offset);
+ sprintf(l_val, "l%" PRIu32, i + offset);
+
+ if ((ret = lyd_new_list(*data, NULL, "lst", 0, &list, k1_val, k2_val))) {
+ return ret;
+ }
+ if ((ret = lyd_new_term(list, NULL, "l", l_val, 0, NULL))) {
+ return ret;
+ }
+ }
+
+ /* Last list contains a "lfl" leaf-list with @p count terms. */
+ for (i = 0; i < count; ++i) {
+ sprintf(lfl_val, "%" PRIu32, i + offset);
+ if ((ret = lyd_new_term(list, NULL, "lfl", lfl_val, 0, NULL))) {
+ return ret;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Execute a test.
+ *
+ * @param[in] setup Setup callback to call once.
+ * @param[in] test Test callback.
+ * @param[in] name Name of the test.
+ * @param[in] mod Module of testing data.
+ * @param[in] count Count of list instances, size of the testing data set.
+ * @param[in] tries Number of (re)tries of the test to get more accurate measurements.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+exec_test(setup_cb setup, test_cb test, const char *name, const struct lys_module *mod, uint32_t count, uint32_t tries)
+{
+ LY_ERR ret;
+ struct timespec ts_start, ts_end;
+ struct test_state state = {0};
+ const uint32_t name_fixed_len = 38;
+ char str[name_fixed_len + 1];
+ uint32_t i, printed;
+ uint64_t time_usec = 0;
+
+ /* print test start */
+ printed = sprintf(str, "| %s ", name);
+ while (printed + 2 < name_fixed_len) {
+ printed += sprintf(str + printed, ".");
+ }
+ if (printed + 1 < name_fixed_len) {
+ printed += sprintf(str + printed, " ");
+ }
+ sprintf(str + printed, "|");
+ fputs(str, stdout);
+ fflush(stdout);
+
+ /* setup */
+ if ((ret = setup(mod, count, &state))) {
+ return ret;
+ }
+
+ /* test */
+ for (i = 0; i < tries; ++i) {
+ if ((ret = test(&state, &ts_start, &ts_end))) {
+ return ret;
+ }
+ time_usec += time_diff(&ts_start, &ts_end);
+ }
+ time_usec /= tries;
+
+ /* teardown */
+ lyd_free_siblings(state.data1);
+ lyd_free_siblings(state.data2);
+
+ /* print time */
+ printf(" %" PRIu64 ".%06" PRIu64 " s |\n", time_usec / 1000000, time_usec % 1000000);
+
+ return LY_SUCCESS;
+}
+
+static void
+TEST_START(struct timespec *ts)
+{
+ time_get(ts);
+
+#ifdef HAVE_CALLGRIND
+ CALLGRIND_START_INSTRUMENTATION;
+#endif
+}
+
+static void
+TEST_END(struct timespec *ts)
+{
+ time_get(ts);
+
+#ifdef HAVE_CALLGRIND
+ CALLGRIND_STOP_INSTRUMENTATION;
+#endif
+}
+
+/* TEST SETUP */
+static LY_ERR
+setup_basic(const struct lys_module *mod, uint32_t count, struct test_state *state)
+{
+ state->mod = mod;
+ state->count = count;
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+setup_data_single_tree(const struct lys_module *mod, uint32_t count, struct test_state *state)
+{
+ state->mod = mod;
+ state->count = count;
+
+ return create_list_inst(mod, 0, count, &state->data1);
+}
+
+static LY_ERR
+setup_data_same_trees(const struct lys_module *mod, uint32_t count, struct test_state *state)
+{
+ LY_ERR ret;
+
+ state->mod = mod;
+ state->count = count;
+
+ if ((ret = create_list_inst(mod, 0, count, &state->data1))) {
+ return ret;
+ }
+ if ((ret = create_list_inst(mod, 0, count, &state->data2))) {
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+setup_data_no_same_trees(const struct lys_module *mod, uint32_t count, struct test_state *state)
+{
+ LY_ERR ret;
+
+ state->mod = mod;
+ state->count = count;
+
+ if ((ret = create_list_inst(mod, 0, count, &state->data1))) {
+ return ret;
+ }
+ if ((ret = create_list_inst(mod, count, count, &state->data2))) {
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+setup_data_offset_tree(const struct lys_module *mod, uint32_t count, struct test_state *state)
+{
+ LY_ERR ret;
+
+ state->mod = mod;
+ state->count = count;
+
+ if ((ret = create_list_inst(mod, count, count, &state->data2))) {
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+/* TEST CB */
+static LY_ERR
+test_create_new_text(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data = NULL;
+
+ TEST_START(ts_start);
+
+ if ((r = create_list_inst(state->mod, 0, state->count, &data))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(data);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_create_new_bin(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data = NULL;
+ uint32_t i, k2_len, l_len;
+ char k2_val[32], l_val[32];
+ struct lyd_node *list;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_new_inner(NULL, state->mod, "cont", 0, &data))) {
+ return r;
+ }
+
+ for (i = 0; i < state->count; ++i) {
+ k2_len = sprintf(k2_val, "str%" PRIu32, i);
+ l_len = sprintf(l_val, "l%" PRIu32, i);
+
+ if ((r = lyd_new_list_bin(data, NULL, "lst", 0, &list, &i, sizeof i, k2_val, k2_len))) {
+ return r;
+ }
+ if ((r = lyd_new_term_bin(list, NULL, "l", l_val, l_len, 0, NULL))) {
+ return r;
+ }
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(data);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_create_path(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data = NULL;
+ uint32_t i;
+ char path[64], l_val[32];
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_new_inner(NULL, state->mod, "cont", 0, &data))) {
+ return r;
+ }
+
+ for (i = 0; i < state->count; ++i) {
+ sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", i, i);
+ sprintf(l_val, "l%" PRIu32, i);
+
+ if ((r = lyd_new_path(data, NULL, path, l_val, 0, NULL))) {
+ return r;
+ }
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(data);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_validate_all(&state->data1, NULL, LYD_VALIDATE_PRESENT, NULL))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+_test_parse(struct test_state *state, LYD_FORMAT format, ly_bool use_file, uint32_t print_options, uint32_t parse_options,
+ uint32_t validate_options, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *data = NULL;
+ char *buf = NULL;
+ struct ly_in *in = NULL;
+
+ if (use_file) {
+ if ((ret = lyd_print_path(TEMP_FILE, state->data1, format, print_options))) {
+ goto cleanup;
+ }
+ if ((ret = ly_in_new_filepath(TEMP_FILE, 0, &in))) {
+ goto cleanup;
+ }
+ } else {
+ if ((ret = lyd_print_mem(&buf, state->data1, format, print_options))) {
+ goto cleanup;
+ }
+ if ((ret = ly_in_new_memory(buf, &in))) {
+ goto cleanup;
+ }
+ }
+
+ TEST_START(ts_start);
+
+ if ((ret = lyd_parse_data(state->mod->ctx, NULL, in, format, parse_options, validate_options, &data))) {
+ goto cleanup;
+ }
+
+ TEST_END(ts_end);
+
+cleanup:
+ free(buf);
+ ly_in_free(in, 0);
+ lyd_free_siblings(data);
+ return ret;
+}
+
+static LY_ERR
+test_parse_xml_mem_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_XML, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_xml_mem_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_XML, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0,
+ ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_xml_file_no_validate_format(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_XML, 1, 0, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_json_mem_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_JSON, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_json_mem_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_JSON, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0,
+ ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_json_file_no_validate_format(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_JSON, 1, 0, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_lyb_mem_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_LYB, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_lyb_mem_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_LYB, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0,
+ ts_start, ts_end);
+}
+
+static LY_ERR
+test_parse_lyb_file_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_parse(state, LYD_LYB, 1, 0, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, ts_start, ts_end);
+}
+
+static LY_ERR
+_test_print(struct test_state *state, LYD_FORMAT format, uint32_t print_options, struct timespec *ts_start,
+ struct timespec *ts_end)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL;
+
+ TEST_START(ts_start);
+
+ if ((ret = lyd_print_mem(&buf, state->data1, format, print_options))) {
+ goto cleanup;
+ }
+
+ TEST_END(ts_end);
+
+cleanup:
+ free(buf);
+ return ret;
+}
+
+static LY_ERR
+test_print_xml(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_print(state, LYD_XML, LYD_PRINT_SHRINK, ts_start, ts_end);
+}
+
+static LY_ERR
+test_print_json(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_print(state, LYD_JSON, LYD_PRINT_SHRINK, ts_start, ts_end);
+}
+
+static LY_ERR
+test_print_lyb(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ return _test_print(state, LYD_LYB, LYD_PRINT_SHRINK, ts_start, ts_end);
+}
+
+static LY_ERR
+test_dup(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_dup_siblings(state->data1, NULL, LYD_DUP_RECURSIVE, &data))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(data);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_free(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data;
+
+ if ((r = create_list_inst(state->mod, 0, state->count, &data))) {
+ return r;
+ }
+
+ TEST_START(ts_start);
+
+ lyd_free_siblings(data);
+
+ TEST_END(ts_end);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_xpath_find(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct ly_set *set;
+ char path[64];
+
+ sprintf(path, "/perf:cont/lst[k1=%" PRIu32 " and k2='str%" PRIu32 "']", state->count / 2, state->count / 2);
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_find_xpath(state->data1, path, &set))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ ly_set_free(set, NULL);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_xpath_find_hash(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct ly_set *set;
+ char path[64];
+
+ sprintf(path, "/perf:cont/lst[k1=%" PRIu32 "][k2='str%" PRIu32 "']", state->count / 2, state->count / 2);
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_find_xpath(state->data1, path, &set))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ ly_set_free(set, NULL);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_compare_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_compare_siblings(state->data1, state->data2, LYD_COMPARE_FULL_RECURSION))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_diff_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *diff;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_diff_siblings(state->data1, state->data2, 0, &diff))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(diff);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_diff_no_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *diff;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_diff_siblings(state->data1, state->data2, 0, &diff))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(diff);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_merge_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_merge_siblings(&state->data1, state->data2, 0))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_merge_no_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data1;
+
+ if ((r = create_list_inst(state->mod, 0, state->count, &data1))) {
+ return r;
+ }
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_merge_siblings(&data1, state->data2, 0))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(data1);
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+test_merge_no_same_destruct(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
+{
+ LY_ERR r;
+ struct lyd_node *data1, *data2;
+
+ if ((r = create_list_inst(state->mod, 0, state->count, &data1))) {
+ return r;
+ }
+ if ((r = create_list_inst(state->mod, state->count, state->count, &data2))) {
+ return r;
+ }
+
+ TEST_START(ts_start);
+
+ if ((r = lyd_merge_siblings(&data1, data2, LYD_MERGE_DESTRUCT))) {
+ return r;
+ }
+
+ TEST_END(ts_end);
+
+ lyd_free_siblings(data1);
+
+ return LY_SUCCESS;
+}
+
+struct test tests[] = {
+ {"create new text", setup_basic, test_create_new_text},
+ {"create new bin", setup_basic, test_create_new_bin},
+ {"create path", setup_basic, test_create_path},
+ {"validate", setup_data_single_tree, test_validate},
+ {"parse xml mem validate", setup_data_single_tree, test_parse_xml_mem_validate},
+ {"parse xml mem no validate", setup_data_single_tree, test_parse_xml_mem_no_validate},
+ {"parse xml file no validate format", setup_data_single_tree, test_parse_xml_file_no_validate_format},
+ {"parse json mem validate", setup_data_single_tree, test_parse_json_mem_validate},
+ {"parse json mem no validate", setup_data_single_tree, test_parse_json_mem_no_validate},
+ {"parse json file no validate format", setup_data_single_tree, test_parse_json_file_no_validate_format},
+ {"parse lyb mem validate", setup_data_single_tree, test_parse_lyb_mem_validate},
+ {"parse lyb mem no validate", setup_data_single_tree, test_parse_lyb_mem_no_validate},
+ {"parse lyb file no validate", setup_data_single_tree, test_parse_lyb_file_no_validate},
+ {"print xml", setup_data_single_tree, test_print_xml},
+ {"print json", setup_data_single_tree, test_print_json},
+ {"print lyb", setup_data_single_tree, test_print_lyb},
+ {"dup", setup_data_single_tree, test_dup},
+ {"free", setup_basic, test_free},
+ {"xpath find", setup_data_single_tree, test_xpath_find},
+ {"xpath find hash", setup_data_single_tree, test_xpath_find_hash},
+ {"compare same", setup_data_same_trees, test_compare_same},
+ {"diff same", setup_data_same_trees, test_diff_same},
+ {"diff no same", setup_data_no_same_trees, test_diff_no_same},
+ {"merge same", setup_data_same_trees, test_merge_same},
+ {"merge no same", setup_data_offset_tree, test_merge_no_same},
+ {"merge no same destruct", setup_basic, test_merge_no_same_destruct},
+};
+
+int
+main(int argc, char **argv)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx = NULL;
+ const struct lys_module *mod;
+ uint32_t i, count, tries;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage:\n%s list-instance-count test-tries\n\n", argv[0]);
+ return LY_EINVAL;
+ }
+
+ count = atoi(argv[1]);
+ if (!count) {
+ fprintf(stderr, "Invalid count \"%s\".\n", argv[1]);
+ return LY_EINVAL;
+ }
+
+ tries = atoi(argv[2]);
+ if (!tries) {
+ fprintf(stderr, "Invalid tries \"%s\".\n", argv[2]);
+ return LY_EINVAL;
+ }
+
+ printf("\nly_perf:\n\tdata set size: %" PRIu32 "\n\teach test executed: %" PRIu32 " %s\n\n", count, tries,
+ (tries > 1) ? "times" : "time");
+
+ /* create context */
+ if ((ret = ly_ctx_new(TESTS_SRC "/perf", 0, &ctx))) {
+ goto cleanup;
+ }
+
+ /* load modules */
+ if (!(mod = ly_ctx_load_module(ctx, "perf", NULL, NULL))) {
+ ret = LY_ENOTFOUND;
+ goto cleanup;
+ }
+
+ /* tests */
+ for (i = 0; i < (sizeof tests / sizeof(struct test)); ++i) {
+ if ((ret = exec_test(tests[i].setup, tests[i].test, tests[i].name, mod, count, tries))) {
+ goto cleanup;
+ }
+ }
+
+ printf("\n");
+
+cleanup:
+ ly_ctx_destroy(ctx);
+ return ret;
+}
diff --git a/tests/perf/perf.yang b/tests/perf/perf.yang
new file mode 100644
index 0000000..0560270
--- /dev/null
+++ b/tests/perf/perf.yang
@@ -0,0 +1,27 @@
+module perf {
+ yang-version 1.1;
+ namespace "urn:sysrepo:tests:perf";
+ prefix p;
+
+ container cont {
+ list lst {
+ key "k1 k2";
+
+ leaf k1 {
+ type uint32;
+ }
+
+ leaf k2 {
+ type string;
+ }
+
+ leaf l {
+ type string;
+ }
+
+ leaf-list lfl {
+ type uint32;
+ }
+ }
+ }
+}
diff --git a/tests/plugins/CMakeLists.txt b/tests/plugins/CMakeLists.txt
new file mode 100644
index 0000000..1e21824
--- /dev/null
+++ b/tests/plugins/CMakeLists.txt
@@ -0,0 +1,19 @@
+include(CMakeParseArguments)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+function(ly_add_plugin)
+ cmake_parse_arguments(ADDPLUGIN "" "NAME" "SOURCES" ${ARGN})
+ set(PLUGIN_NAME plugin_${ADDPLUGIN_NAME})
+
+ foreach(PLUGIN_SOURCE ${ADDPLUGIN_SOURCES})
+ list(APPEND PLUGIN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_SOURCE})
+ endforeach()
+
+ add_library(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES})
+ set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "")
+ target_link_libraries(${PLUGIN_NAME} yang)
+endfunction(ly_add_plugin)
+
+ly_add_plugin(NAME invalid SOURCES invalid.c)
+ly_add_plugin(NAME simple SOURCES simple.c)
diff --git a/tests/plugins/invalid.c b/tests/plugins/invalid.c
new file mode 100644
index 0000000..000ccb4
--- /dev/null
+++ b/tests/plugins/invalid.c
@@ -0,0 +1,41 @@
+/**
+ * @file invalid.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Invalid testing plugins implementation
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <stdint.h>
+
+#include "libyang.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+
+/*
+ * EXTENSION PLUGIN
+ */
+
+/**
+ * @brief Instead of plugin description, only the API version is declared.
+ *
+ * Here should be LY_PLUGINS_EXTENSIONS used.
+ */
+uint32_t plugins_extensions_apiver__ = LYPLG_EXT_API_VERSION;
+
+/*
+ * TYPE PLUGIN
+ */
+
+/**
+ * @brief Instead of plugin description, only the API version is declared.
+ *
+ * Here should be LYPLG_TYPES used.
+ */
+uint32_t plugins_types_apiver__ = LYPLG_TYPE_API_VERSION;
diff --git a/tests/plugins/simple.c b/tests/plugins/simple.c
new file mode 100644
index 0000000..3595c76
--- /dev/null
+++ b/tests/plugins/simple.c
@@ -0,0 +1,92 @@
+/**
+ * @file simple.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Simple testing plugins implementation
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <stdlib.h>
+
+#include "libyang.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+
+/*
+ * EXTENSION PLUGIN
+ */
+
+/**
+ * @brief Compile simple extension instances.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
+ */
+static LY_ERR
+hint_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp, struct lysc_ext_instance *ext)
+{
+ /* check that the extension is instantiated at an allowed place - data node */
+ if (!(ext->parent_stmt & LY_STMT_DATA_NODE_MASK)) {
+ lyplg_ext_compile_log(cctx, ext, LY_LLWRN, 0,
+ "Extension %s is allowed only in a data nodes, but it is placed in \"%s\" statement.",
+ extp->name, lyplg_ext_stmt2str(ext->parent_stmt));
+ return LY_ENOT;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Plugin descriptions for the test extensions
+ */
+LYPLG_EXTENSIONS = {
+ {
+ .module = "libyang-plugins-simple",
+ .revision = NULL,
+ .name = "hint",
+
+ .plugin.id = "ly2 simple test v1",
+ .plugin.parse = NULL,
+ .plugin.compile = hint_compile,
+ .plugin.printer_info = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = NULL,
+ .plugin.cfree = NULL
+ },
+ {0} /* terminating zeroed item */
+};
+
+/*
+ * TYPE PLUGIN
+ */
+
+/**
+ * @brief Plugin information for note (string) type implementation.
+ *
+ * Everything is just the same as for built-in string.
+ */
+LYPLG_TYPES = {
+ {
+ .module = "libyang-plugins-simple",
+ .revision = NULL,
+ .name = "note",
+
+ .plugin.id = "ly2 simple test v1",
+ .plugin.store = lyplg_type_store_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 0
+ },
+ {0}
+};
diff --git a/tests/style/CMakeLists.txt b/tests/style/CMakeLists.txt
new file mode 100644
index 0000000..93d6049
--- /dev/null
+++ b/tests/style/CMakeLists.txt
@@ -0,0 +1,12 @@
+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})
+ add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cmake --build ${CMAKE_BINARY_DIR} --target format-check)
+endif()
+
+# just compile
+add_executable(cpp_compat cpp_compat.c $<TARGET_OBJECTS:yangobj>)
+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)
+target_compile_options(cpp_compat PUBLIC "-Werror=c++-compat")
diff --git a/tests/style/cpp_compat.c b/tests/style/cpp_compat.c
new file mode 100644
index 0000000..28a8ffd
--- /dev/null
+++ b/tests/style/cpp_compat.c
@@ -0,0 +1,95 @@
+/**
+ * @file cpp_compat.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for C++ compatibility of public headers (macros)
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "plugins_exts.h"
+#include "plugins_types.h"
+
+int
+main(void)
+{
+ struct ly_ctx *ctx = NULL;
+ const struct lys_module *mod;
+ int *sa = NULL, *item;
+ struct test_list {
+ int val;
+ struct test_list *next;
+ } *tl = NULL, *tl_item = NULL, *tl_next;
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lysc_node *scnode;
+ struct lyd_node *data = NULL, *next, *elem, *opaq = NULL;
+ LY_ERR ret = LY_SUCCESS;
+
+ if ((ret = ly_ctx_new(NULL, 0, &ctx))) {
+ goto cleanup;
+ }
+ if (!(mod = ly_ctx_get_module_latest(ctx, "ietf-yang-library"))) {
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ if ((ret = ly_ctx_get_yanglib_data(ctx, &data, "%u", ly_ctx_get_change_count(ctx)))) {
+ goto cleanup;
+ }
+ if ((ret = lyd_new_opaq(NULL, ctx, "name", "val", NULL, "module", &opaq))) {
+ goto cleanup;
+ }
+
+ /* tree_edit.h / tree.h */
+ LY_ARRAY_NEW_GOTO(ctx, sa, item, ret, cleanup);
+ LY_ARRAY_NEW_GOTO(ctx, sa, item, ret, cleanup);
+ LY_ARRAY_FOR(sa, int, item) {}
+ LY_ARRAY_FREE(sa);
+ sa = NULL;
+
+ LY_ARRAY_CREATE_GOTO(ctx, sa, 2, ret, cleanup);
+ LY_ARRAY_INCREMENT(sa);
+ LY_ARRAY_INCREMENT(sa);
+ LY_ARRAY_FOR(sa, u) {}
+ LY_ARRAY_DECREMENT_FREE(sa);
+ LY_ARRAY_DECREMENT_FREE(sa);
+
+ LY_LIST_NEW_GOTO(ctx, &tl, tl_item, next, ret, cleanup);
+ LY_LIST_NEW_GOTO(ctx, &tl, tl_item, next, ret, cleanup);
+ LY_LIST_FOR(tl, tl_item) {}
+ LY_LIST_FOR_SAFE(tl, tl_next, tl_item) {}
+ tl_item = tl->next;
+
+ /* tree_data.h */
+ LYD_TREE_DFS_BEGIN(data, elem) {
+ LYD_TREE_DFS_END(data, elem);
+ }
+ LYD_LIST_FOR_INST(data, data->schema, elem) {}
+ LYD_LIST_FOR_INST_SAFE(data, data->schema, next, elem) {}
+ (void)LYD_CTX(data);
+ (void)LYD_NAME(data);
+
+ /* tree_schema.h */
+ LYSC_TREE_DFS_BEGIN(mod->compiled->data, scnode) {
+ LYSC_TREE_DFS_END(mod->compiled->data, scnode);
+ }
+ (void)LYSP_MODULE_NAME(mod->parsed);
+ (void)lysc_is_userordered(data->schema);
+ (void)lysc_is_key(data->schema);
+ (void)lysc_is_np_cont(data->schema);
+ (void)lysc_is_dup_inst_list(data->schema);
+
+cleanup:
+ free(tl_item);
+ free(tl);
+ lyd_free_tree(opaq);
+ lyd_free_all(data);
+ ly_ctx_destroy(ctx);
+ return ret;
+}
diff --git a/tests/tests_config.h.in b/tests/tests_config.h.in
new file mode 100644
index 0000000..119fb35
--- /dev/null
+++ b/tests/tests_config.h.in
@@ -0,0 +1,27 @@
+/**
+ * @file tests_config.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief cmocka tests configuration header.
+ *
+ * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#ifndef LYTEST_CONFIG_H_
+#define LYTEST_CONFIG_H_
+
+#define TESTS_SRC "@CMAKE_CURRENT_SOURCE_DIR@"
+#define TESTS_BIN "@CMAKE_CURRENT_BINARY_DIR@"
+
+#define TESTS_DIR_MODULES_YANG TESTS_SRC"/modules/yang"
+
+/**
+ * @brief Macro for support of callgrind header and macros.
+ */
+#cmakedefine HAVE_CALLGRIND
+
+#endif /* LYTEST_CONFIG_H_ */
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
new file mode 100644
index 0000000..ac47611
--- /dev/null
+++ b/tests/utests/CMakeLists.txt
@@ -0,0 +1,77 @@
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+set(format_sources
+ ${format_sources}
+ ${CMAKE_CURRENT_SOURCE_DIR}/*.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/basic/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/data/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/extensions/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/schema/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/types/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/restriction/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/node/*.c
+ PARENT_SCOPE)
+
+ly_add_utest(NAME uint8 SOURCES types/uint8.c)
+ly_add_utest(NAME uint16 SOURCES types/uint16.c)
+ly_add_utest(NAME uint32 SOURCES types/uint32.c)
+ly_add_utest(NAME uint64 SOURCES types/uint64.c)
+ly_add_utest(NAME int8 SOURCES types/int8.c)
+ly_add_utest(NAME int16 SOURCES types/int16.c)
+ly_add_utest(NAME int32 SOURCES types/int32.c)
+ly_add_utest(NAME int64 SOURCES types/int64.c)
+ly_add_utest(NAME string SOURCES types/string.c)
+ly_add_utest(NAME bits SOURCES types/bits.c)
+ly_add_utest(NAME binary SOURCES types/binary.c)
+ly_add_utest(NAME inet_types SOURCES types/inet_types.c)
+ly_add_utest(NAME yang_types SOURCES types/yang_types.c)
+ly_add_utest(NAME enumeration SOURCES types/enumeration.c)
+ly_add_utest(NAME instanceid SOURCES types/instanceid.c)
+ly_add_utest(NAME instanceid_keys SOURCES types/instanceid_keys.c)
+ly_add_utest(NAME union SOURCES types/union.c)
+ly_add_utest(NAME boolean SOURCES types/boolean.c)
+ly_add_utest(NAME decimal64 SOURCES types/decimal64.c)
+ly_add_utest(NAME empty SOURCES types/empty.c)
+ly_add_utest(NAME identityref SOURCES types/identityref.c)
+ly_add_utest(NAME leafref SOURCES types/leafref.c)
+
+ly_add_utest(NAME range SOURCES restriction/test_range.c)
+ly_add_utest(NAME pattern SOURCES restriction/test_pattern.c)
+
+ly_add_utest(NAME list SOURCES node/list.c)
+
+ly_add_utest(NAME common SOURCES basic/test_common.c)
+ly_add_utest(NAME set SOURCES basic/test_set.c)
+ly_add_utest(NAME hash_table SOURCES basic/test_hash_table.c)
+ly_add_utest(NAME inout SOURCES basic/test_inout.c)
+ly_add_utest(NAME context SOURCES basic/test_context.c)
+if(NOT WIN32)
+ly_add_utest(NAME plugins SOURCES basic/test_plugins.c)
+endif()
+ly_add_utest(NAME xml SOURCES basic/test_xml.c)
+ly_add_utest(NAME json SOURCES basic/test_json.c)
+ly_add_utest(NAME xpath SOURCES basic/test_xpath.c)
+ly_add_utest(NAME yanglib SOURCES basic/test_yanglib.c)
+
+ly_add_utest(NAME schema SOURCES schema/test_schema.c)
+ly_add_utest(NAME yang SOURCES schema/test_yang.c)
+ly_add_utest(NAME yin SOURCES schema/test_yin.c)
+ly_add_utest(NAME tree_schema_compile SOURCES schema/test_tree_schema_compile.c)
+ly_add_utest(NAME printer_tree SOURCES schema/test_printer_tree.c)
+
+ly_add_utest(NAME tree_data SOURCES data/test_tree_data.c)
+ly_add_utest(NAME new SOURCES data/test_new.c)
+ly_add_utest(NAME parser_xml SOURCES data/test_parser_xml.c)
+ly_add_utest(NAME printer_xml SOURCES data/test_printer_xml.c)
+ly_add_utest(NAME parser_json SOURCES data/test_parser_json.c)
+ly_add_utest(NAME lyb SOURCES data/test_lyb.c)
+ly_add_utest(NAME validation SOURCES data/test_validation.c)
+ly_add_utest(NAME merge SOURCES data/test_merge.c)
+ly_add_utest(NAME diff SOURCES data/test_diff.c)
+
+ly_add_utest(NAME metadata SOURCES extensions/test_metadata.c)
+ly_add_utest(NAME nacm SOURCES extensions/test_nacm.c)
+ly_add_utest(NAME yangdata SOURCES extensions/test_yangdata.c)
+ly_add_utest(NAME schema_mount SOURCES extensions/test_schema_mount.c)
+ly_add_utest(NAME structure SOURCES extensions/test_structure.c)
diff --git a/tests/utests/basic/test_common.c b/tests/utests/basic/test_common.c
new file mode 100644
index 0000000..75235a2
--- /dev/null
+++ b/tests/utests/basic/test_common.c
@@ -0,0 +1,416 @@
+/*
+ * @file test_common.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from common.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "common.h"
+
+static void
+test_utf8(void **UNUSED(state))
+{
+ char buf[5] = {0};
+ const char *str = buf;
+ unsigned int c;
+ size_t len;
+
+ /* test invalid UTF-8 characters in lyxml_getutf8
+ * - https://en.wikipedia.org/wiki/UTF-8 */
+ buf[0] = (char)0x04;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[0] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = (char)0xc0;
+ buf[1] = (char)0x00;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = (char)0xe0;
+ buf[1] = (char)0x00;
+ buf[2] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = (char)0xf0;
+ buf[1] = (char)0x00;
+ buf[2] = (char)0x80;
+ buf[3] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+}
+
+static void
+test_parse_int(void **UNUSED(state))
+{
+ const char *str;
+ int64_t i = 500;
+
+ str = "10";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ assert_int_equal(i, 10);
+
+ /* leading zeros are allowed, trailing whitespaces are allowed */
+ str = "000\n\t ";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ assert_int_equal(i, 0);
+
+ /* negative value */
+ str = "-10";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ assert_int_equal(i, -10);
+
+ /* non-NULL terminated string */
+ str = "+5sometext";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, 2, -10, 10, 10, &i));
+ assert_int_equal(i, 5);
+
+ /* out of bounds value */
+ str = "11";
+ assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ str = "-11";
+ assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+
+ /* NaN */
+ str = "zero";
+ assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+
+ /* mixing number with text */
+ str = "10zero";
+ assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+
+ str = "10 zero";
+ assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+}
+
+static void
+test_parse_uint(void **UNUSED(state))
+{
+ const char *str;
+ uint64_t u = 500;
+
+ str = "10";
+ assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u));
+ assert_int_equal(u, 10);
+
+ /* leading zeros are allowed, trailing whitespaces are allowed */
+ str = "000\n\t ";
+ assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u));
+ assert_int_equal(u, 0);
+ /* non-NULL terminated string */
+ str = "+5sometext";
+ assert_int_equal(LY_SUCCESS, ly_parse_uint(str, 2, 10, 10, &u));
+ assert_int_equal(u, 5);
+
+ /* out of bounds value */
+ str = "11";
+ assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), 10, 10, &u));
+ str = "-1";
+ assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), (uint64_t)-1, 10, &u));
+
+ /* NaN */
+ str = "zero";
+ assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u));
+
+ /* mixing number with text */
+ str = "10zero";
+ assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u));
+
+ str = "10 zero";
+ assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u));
+}
+
+static void
+test_parse_nodeid(void **UNUSED(state))
+{
+ const char *str;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+
+ str = "123";
+ assert_int_equal(LY_EINVAL, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+
+ str = "a12_-.!";
+ assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_null(prefix);
+ assert_int_equal(0, prefix_len);
+ assert_non_null(name);
+ assert_int_equal(6, name_len);
+ assert_int_equal(0, strncmp("a12_-.", name, name_len));
+ assert_string_equal("!", str);
+
+ str = "a12_-.:_b2 xxx";
+ assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_non_null(prefix);
+ assert_int_equal(6, prefix_len);
+ assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len));
+ assert_non_null(name);
+ assert_int_equal(3, name_len);
+ assert_int_equal(0, strncmp("_b2", name, name_len));
+ assert_string_equal(" xxx", str);
+}
+
+static void
+test_parse_instance_predicate(void **UNUSED(state))
+{
+ const char *str, *errmsg;
+ const char *prefix, *id, *value;
+ size_t prefix_len, id_len, value_len;
+
+ str = "[ex:name='fred']";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "");
+ assert_string_equal(prefix, "ex:name='fred']");
+ assert_int_equal(prefix_len, 2);
+ assert_string_equal(id, "name='fred']");
+ assert_int_equal(id_len, 4);
+ assert_string_equal(value, "fred']");
+ assert_int_equal(value_len, 4);
+
+ str = "[ex:ip = \"[192.0.2.1]\"][ex:port='80']";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "[ex:port='80']");
+ assert_string_equal(prefix, "ex:ip = \"[192.0.2.1]\"][ex:port='80']");
+ assert_int_equal(prefix_len, 2);
+ assert_string_equal(id, "ip = \"[192.0.2.1]\"][ex:port='80']");
+ assert_int_equal(id_len, 2);
+ assert_string_equal(value, "[192.0.2.1]\"][ex:port='80']");
+ assert_int_equal(value_len, 11);
+
+ str = "[. = 'blowfish-cbc']";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "");
+ assert_null(prefix);
+ assert_int_equal(prefix_len, 0);
+ assert_string_equal(id, ". = 'blowfish-cbc']");
+ assert_int_equal(id_len, 1);
+ assert_string_equal(value, "blowfish-cbc']");
+ assert_int_equal(value_len, 12);
+
+ str = "[ 3 ]";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "");
+ assert_null(prefix);
+ assert_int_equal(prefix_len, 0);
+ assert_null(id);
+ assert_int_equal(id_len, 0);
+ assert_string_equal(value, "3 ]");
+ assert_int_equal(value_len, 1);
+
+ /* invalid predicates */
+ /* position must be positive integer */
+ str = "[0]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "The position predicate cannot be zero.");
+ str = "[-1]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Invalid instance predicate format (negative position or invalid node-identifier).");
+
+ /* invalid node-identifier */
+ str = "[$node='value']";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Invalid node-identifier.");
+ str = "[.node='value']";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Unexpected character instead of '=' in leaf-list-predicate.");
+ str = "[13node='value']";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate (pos) is not terminated by \']\' character.");
+
+ str = "[ex:node]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Unexpected character instead of '=' in key-predicate.");
+
+ str = "[ex:node= value]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "String value is not quoted.");
+
+ str = "[ex:node='value\"]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Value is not terminated quoted-string.");
+
+ str = "[ex:node='value ]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Value is not terminated quoted-string.");
+
+ str = "[ex:node=\"value\"[3]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate (key-predicate) is not terminated by \']\' character.");
+ str = "[.=\"value\"[3]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate (leaf-list-predicate) is not terminated by \']\' character.");
+
+ /* the limit of the string is too short, it ends one character earlier */
+ str = "[ex:node='value']";
+ assert_int_equal(LY_EINVAL, ly_parse_instance_predicate(&str, strlen(str) - 1, LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate is incomplete.");
+}
+
+static void
+test_value_prefix_next(void **UNUSED(state))
+{
+ const char *next;
+ ly_bool is_prefix;
+ uint32_t bytes;
+
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(NULL, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(0, bytes);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next("", NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(0, bytes);
+
+ /* prefix */
+ next = "pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* no-prefix */
+ next = "node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix */
+ next = "::::";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix */
+ next = "//a/:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix */
+ next = "//a//";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* prefix, prefix */
+ next = "pref1:pref2:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_string_equal(next, "pref2:");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* prefix, no-prefix */
+ next = "pref:node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix, prefix */
+ next = "/pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(1, bytes);
+ assert_string_equal(next, "pref:");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* no-prefix, prefix */
+ next = "//pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(2, bytes);
+ assert_string_equal(next, "pref:");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* no-prefix, prefix, no-prefix */
+ next = "/pref:node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(1, bytes);
+ assert_string_equal(next, "pref:node");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* prefix, no-prefix, prefix */
+ next = "pref:node pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node pref:");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_string_equal(next, "pref:");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* prefix, no-prefix, prefix, no-prefix */
+ next = "pref:node /pref:node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node /pref:node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(6, bytes);
+ assert_string_equal(next, "pref:node");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_utf8),
+ UTEST(test_parse_int),
+ UTEST(test_parse_uint),
+ UTEST(test_parse_nodeid),
+ UTEST(test_parse_instance_predicate),
+ UTEST(test_value_prefix_next),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c
new file mode 100644
index 0000000..4c4cc3f
--- /dev/null
+++ b/tests/utests/basic/test_context.c
@@ -0,0 +1,1087 @@
+/*
+ * @file test_context.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from context.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "common.h"
+#include "context.h"
+#include "in.h"
+#include "schema_compile.h"
+#include "tests_config.h"
+#include "tree_schema_internal.h"
+#ifdef _WIN32
+static void
+slashes_to_backslashes(char *path)
+{
+ while ((path = strchr(path, '/'))) {
+ *path++ = '\\';
+ }
+}
+
+static void
+test_searchdirs(void **state)
+{
+ const char * const *list;
+ char *path1 = strdup(TESTS_BIN "/utests");
+ char *path2 = strdup(TESTS_SRC);
+
+ slashes_to_backslashes(path1);
+ slashes_to_backslashes(path2);
+
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_set_searchdir()).", NULL);
+ assert_null(ly_ctx_get_searchdirs(NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_searchdirs()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_unset_searchdir()).", NULL);
+
+ /* correct path */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path1));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]);
+
+ /* duplicated paths */
+ assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, path1));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]);
+
+ /* another path */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path2));
+ assert_int_equal(2, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(path2, UTEST_LYCTX->search_paths.objs[1]);
+
+ /* get searchpaths */
+ list = ly_ctx_get_searchdirs(UTEST_LYCTX);
+ assert_non_null(list);
+ assert_string_equal(path1, list[0]);
+ assert_string_equal(path2, list[1]);
+ assert_null(list[2]);
+
+ /* removing searchpaths */
+ /* nonexisting */
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile"));
+ CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL);
+
+ /* first */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path1));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_not_equal(path1, list[0]);
+
+ /* second */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path2));
+ assert_int_equal(0, UTEST_LYCTX->search_paths.count);
+
+ free(path1);
+ free(path2);
+}
+
+#else
+
+static void
+test_searchdirs(void **state)
+{
+ const char * const *list;
+
+ /* invalid arguments */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_set_searchdir()).", NULL);
+ assert_null(ly_ctx_get_searchdirs(NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_searchdirs()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_unset_searchdir()).", NULL);
+
+ /* readable and executable, but not a directory */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utest_context"));
+ CHECK_LOG_CTX("Given search directory \""TESTS_BIN "/utest_context\" is not a directory.", NULL);
+ /* not existing */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, "/nonexistingfile"));
+ CHECK_LOG_CTX("Unable to use search directory \"/nonexistingfile\" (No such file or directory).", NULL);
+
+ /* ly_set_add() fails */
+ /* no change */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, NULL));
+
+ /* correct path */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utests"));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(TESTS_BIN "/utests", UTEST_LYCTX->search_paths.objs[0]);
+
+ /* duplicated paths */
+ assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utests"));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(TESTS_BIN "/utests", UTEST_LYCTX->search_paths.objs[0]);
+
+ /* another paths - add 8 to fill the initial buffer of the searchpaths list */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/CMakeFiles"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../src"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../CMakeModules"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../doc"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN));
+ assert_int_equal(7, UTEST_LYCTX->search_paths.count);
+
+ /* get searchpaths */
+ list = ly_ctx_get_searchdirs(UTEST_LYCTX);
+ assert_non_null(list);
+ assert_string_equal(TESTS_BIN "/utests", list[0]);
+ assert_string_equal(TESTS_BIN "/CMakeFiles", list[1]);
+ assert_string_equal(TESTS_SRC, list[5]);
+ assert_string_equal(TESTS_BIN, list[6]);
+ assert_null(list[7]);
+
+ /* removing searchpaths */
+ /* nonexisting */
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile"));
+ CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL);
+ /* first */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_BIN "/utests"));
+ assert_string_not_equal(TESTS_BIN "/utests", list[0]);
+ assert_int_equal(6, UTEST_LYCTX->search_paths.count);
+ /* middle */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_SRC));
+ assert_int_equal(5, UTEST_LYCTX->search_paths.count);
+ /* last */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_BIN));
+ assert_int_equal(4, UTEST_LYCTX->search_paths.count);
+ /* all */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, NULL));
+ assert_int_equal(0, UTEST_LYCTX->search_paths.count);
+
+ /* again - no change */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, NULL));
+
+ /* cleanup */
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* test searchdir list in ly_ctx_new() */
+ assert_int_equal(LY_EINVAL, ly_ctx_new("/nonexistingfile", 0, &UTEST_LYCTX));
+ CHECK_LOG("Unable to use search directory \"/nonexistingfile\" (No such file or directory).", NULL);
+ assert_int_equal(LY_SUCCESS,
+ ly_ctx_new(TESTS_SRC PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_SRC,
+ LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX));
+ assert_int_equal(2, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(TESTS_SRC, UTEST_LYCTX->search_paths.objs[0]);
+ assert_string_equal(TESTS_BIN, UTEST_LYCTX->search_paths.objs[1]);
+}
+
+#endif
+
+static void
+test_options(void **state)
+{
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0xffff, &UTEST_LYCTX));
+
+ /* invalid arguments */
+ assert_int_equal(0, ly_ctx_get_options(NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_options()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_ctx_set_options(NULL, 0));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_set_options()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_options(NULL, 0));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_unset_options()).", NULL);
+
+ /* unset */
+ /* LY_CTX_ALL_IMPLEMENTED */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_ALL_IMPLEMENTED));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED);
+
+ /* LY_CTX_REF_IMPLEMENTED */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED);
+
+ /* LY_CTX_DISABLE_SEARCHDIRS */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS);
+
+ /* LY_CTX_DISABLE_SEARCHDIR_CWD */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIR_CWD));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD);
+
+ /* LY_CTX_PREFER_SEARCHDIRS */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS);
+
+ assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX));
+
+ /* set back */
+ /* LY_CTX_ALL_IMPLEMENTED */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_ALL_IMPLEMENTED));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED);
+
+ /* LY_CTX_REF_IMPLEMENTED */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED);
+
+ /* LY_CTX_DISABLE_SEARCHDIRS */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS);
+
+ /* LY_CTX_DISABLE_SEARCHDIR_CWD */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIR_CWD));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD);
+
+ /* LY_CTX_PREFER_SEARCHDIRS */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS);
+
+ assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX));
+}
+
+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))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_models(void **state)
+{
+ struct ly_in *in;
+ const char *str;
+ struct lys_module *mod1, *mod2;
+ struct lys_glob_unres unres = {0};
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* invalid arguments */
+ assert_int_equal(0, ly_ctx_get_change_count(NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_change_count()).", NULL);
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX));
+ 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));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Invalid schema input format.", NULL);
+
+ /* import callback */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)(str = "test"));
+ assert_ptr_equal(test_imp_clb, UTEST_LYCTX->imp_clb);
+ assert_ptr_equal(str, UTEST_LYCTX->imp_clb_data);
+ assert_ptr_equal(test_imp_clb, ly_ctx_get_module_imp_clb(UTEST_LYCTX, (void **)&str));
+ assert_string_equal("test", str);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL);
+ assert_null(UTEST_LYCTX->imp_clb);
+ assert_null(UTEST_LYCTX->imp_clb_data);
+
+ /* 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));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL,
+ "Name collision between module and submodule of name \"y\".", "Line number 1.");
+
+ 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));
+ 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));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL,
+ "Name collision between module and submodule of name \"y\".", "Line number 1.");
+
+ 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));
+ lys_unres_glob_revert(UTEST_LYCTX, &unres);
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Parsing module \"b\" failed.", NULL,
+ "Including \"y\" submodule into \"b\" failed.", NULL,
+ "Parsing submodule failed.", NULL,
+ "Name collision between submodules of name \"y\".", "Line number 1.");
+
+ /* selecting correct revision of the submodules */
+ ly_ctx_reset_latests(UTEST_LYCTX);
+ 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));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date);
+
+ /* reloading module in case only the compiled module resists in the context */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module w {namespace urn:w;prefix w;revision 2018-10-24;}", &in));
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod1));
+ ly_in_free(in, 0);
+ assert_non_null(mod1->compiled);
+ assert_non_null(mod1->parsed);
+
+#if 0
+ /* TODO in case we are able to remove the parsed schema, here we will test how it will handle missing import parsed schema */
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in));
+ /* mod1->parsed is necessary to compile mod2 because of possible groupings, typedefs, ... */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL);
+ assert_int_equal(LY_ENOTFOUND, lys_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, &mod2));
+ /*logbuf_assert("Unable to reload \"w\" module to import it into \"z\", source data not found.");*/
+ CHECK_LOG_CTX("Recompilation of module \"w\" failed.", NULL);
+ assert_null(mod2);
+ ly_in_free(in, 0);
+#endif
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod2));
+ ly_in_free(in, 0);
+ assert_non_null(mod2);
+ assert_non_null(mod1->parsed);
+ assert_string_equal("w", mod1->name);
+}
+
+static void
+test_imports(void **state)
+{
+ struct lys_module *mod1, *mod2, *mod3, *import;
+ char *str;
+ uint16_t ctx_options;
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+ ctx_options = LY_CTX_DISABLE_SEARCHDIRS | LY_CTX_NO_YANGLIBRARY;
+
+ /* Import callback provides newer revision of module 'a',
+ * however the older revision is implemented soon and therefore it is preferred. */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-17;}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-16;}",
+ LYS_IN_YANG, &mod1));
+ assert_true(LYS_MOD_LATEST_REV & mod1->latest_revision);
+ assert_int_equal(1, mod1->implemented);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}",
+ LYS_IN_YANG, &mod2));
+ assert_ptr_equal(mod1, mod2->parsed->imports[0].module);
+ assert_true((LYS_MOD_LATEST_REV | LYS_MOD_IMPORTED_REV) & mod1->latest_revision);
+ assert_string_equal("2019-09-16", mod1->revision);
+ assert_int_equal(1, mod1->implemented);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16"));
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* Import callback provides older revision of module 'a' and it is
+ * imported by another module, so it is preferred even if newer
+ * revision is implemented later. */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-16;}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}",
+ LYS_IN_YANG, &mod2));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-17;}",
+ LYS_IN_YANG, &mod1));
+ ly_log_level(LY_LLVRB);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;import a {prefix a;}}",
+ LYS_IN_YANG, &mod3));
+ CHECK_LOG("Implemented module \"a@2019-09-17\" is not used for import, revision \"2019-09-16\" is imported instead.", NULL);
+ ly_log_level(LY_LLWRN);
+ assert_true(LYS_MOD_LATEST_SEARCHDIRS & mod1->latest_revision);
+ assert_int_equal(1, mod1->implemented);
+ import = mod2->parsed->imports[0].module;
+ assert_true(LYS_MOD_IMPORTED_REV & import->latest_revision);
+ assert_string_equal("2019-09-16", import->revision);
+ assert_int_equal(0, import->implemented);
+ import = mod3->parsed->imports[0].module;
+ assert_string_equal("2019-09-16", import->revision);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16"));
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-17"));
+ assert_string_equal("2019-09-17", ly_ctx_get_module_implemented(UTEST_LYCTX, "a")->revision);
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* check of circular dependency */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX));
+ str = "module a {namespace urn:a; prefix a;"
+ "import b {prefix b;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b { yang-version 1.1; namespace urn:b; prefix b;"
+ "import a {prefix a;}"
+ "}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+}
+
+static void
+test_get_models(void **state)
+{
+ struct lys_module *mod, *mod2;
+ const char *str0 = "module a {namespace urn:a;prefix a;}";
+ const char *str1 = "module a {namespace urn:a;prefix a;revision 2018-10-23;}";
+ const char *str2 = "module a {namespace urn:a;prefix a;revision 2018-10-23;revision 2018-10-24;}";
+ struct ly_in *in0, *in1, *in2;
+ struct lys_glob_unres unres = {0};
+
+ unsigned int index = 0;
+ const char *names[] = {
+ "ietf-yang-metadata", "yang", "ietf-inet-types", "ietf-yang-types", "ietf-yang-schema-mount",
+ "ietf-yang-structure-ext", "ietf-datastores", "ietf-yang-library", "a", "a", "a"
+ };
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str0, &in0));
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1));
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str2, &in2));
+
+ /* invalid arguments */
+ assert_ptr_equal(NULL, ly_ctx_get_module(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_module()).", NULL);
+ assert_ptr_equal(NULL, ly_ctx_get_module(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument name (ly_ctx_get_module()).", NULL);
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_module_ns()).", NULL);
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument ns (ly_ctx_get_module_ns()).", NULL);
+ assert_null(ly_ctx_get_module(UTEST_LYCTX, "nonsence", NULL));
+
+ /* internal modules */
+ assert_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-types"));
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang");
+ assert_non_null(mod);
+ assert_non_null(mod->parsed);
+ assert_string_equal("yang", mod->name);
+ mod2 = ly_ctx_get_module_implemented_ns(UTEST_LYCTX, mod->ns);
+ assert_ptr_equal(mod, mod2);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-yang-metadata", "2016-08-05"));
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-yang-types", "2013-07-15"));
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-inet-types", "2013-07-15"));
+ assert_non_null(ly_ctx_get_module_ns(UTEST_LYCTX, "urn:ietf:params:xml:ns:yang:ietf-datastores", "2018-02-14"));
+
+ /* 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_EDENIED, lys_implement(mod2, NULL, &unres));
+ CHECK_LOG_CTX("Module \"a@2018-10-24\" is already implemented in revision \"2018-10-23\".", NULL);
+ 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));
+ /* 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));
+ lys_unres_glob_erase(&unres);
+ assert_non_null(mod2);
+ assert_ptr_not_equal(mod, mod2);
+ mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a");
+ assert_ptr_equal(mod, mod2);
+ 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));
+ 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"));
+
+ 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));
+ CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL);
+ lys_unres_glob_erase(&unres);
+
+ while ((mod = (struct lys_module *)ly_ctx_get_module_iter(UTEST_LYCTX, &index))) {
+ assert_string_equal(names[index - 1], mod->name);
+ }
+ assert_int_equal(11, index);
+
+ /* cleanup */
+ ly_in_free(in0, 0);
+ ly_in_free(in1, 0);
+ ly_in_free(in2, 0);
+}
+
+static void
+test_ylmem(void **state)
+{
+#define DATA_YANG_LIBRARY_START "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\
+ " <module-set>\n"\
+ " <name>complete</name>\n"\
+ " <module>\n"\
+ " <name>yang</name>\n"\
+ " <revision>2022-06-16</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-library</name>\n"\
+ " <revision>2019-01-04</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\
+ " </module>\n"
+
+#define DATA_YANG_BASE_IMPORTS " <import-only-module>\n"\
+ " <name>ietf-yang-metadata</name>\n"\
+ " <revision>2016-08-05</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\
+ " </import-only-module>\n"\
+ " <import-only-module>\n"\
+ " <name>ietf-inet-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\
+ " </import-only-module>\n"\
+ " <import-only-module>\n"\
+ " <name>ietf-yang-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\
+ " </import-only-module>\n"\
+ " <import-only-module>\n"\
+ " <name>ietf-datastores</name>\n"\
+ " <revision>2018-02-14</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\
+ " </import-only-module>\n"
+
+#define DATA_YANG_SCHEMA_MODULE_STATE " </module-set>\n"\
+ " <schema>\n"\
+ " <name>complete</name>\n"\
+ " <module-set>complete</module-set>\n"\
+ " </schema>\n"\
+ " <content-id>9</content-id>\n"\
+ "</yang-library>\n"\
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\
+ " <module-set-id>12</module-set-id>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-metadata</name>\n"\
+ " <revision>2016-08-05</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>yang</name>\n"\
+ " <revision>2022-06-16</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\
+ " <conformance-type>implement</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-inet-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-library</name>\n"\
+ " <revision>2019-01-04</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\
+ " <conformance-type>implement</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-datastores</name>\n"\
+ " <revision>2018-02-14</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"
+
+ const char *yanglibrary_only =
+ DATA_YANG_LIBRARY_START
+ DATA_YANG_BASE_IMPORTS
+ DATA_YANG_SCHEMA_MODULE_STATE
+ "</modules-state>\n";
+
+ const char *with_netconf =
+ DATA_YANG_LIBRARY_START
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " </module>\n"
+ DATA_YANG_BASE_IMPORTS
+ " <import-only-module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " </import-only-module>\n"
+ DATA_YANG_SCHEMA_MODULE_STATE
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " <conformance-type>import</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>";
+
+ char *with_netconf_features = malloc(8096);
+
+ strcpy(with_netconf_features,
+ DATA_YANG_LIBRARY_START
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <feature>writable-running</feature>\n"
+ " <feature>candidate</feature>\n"
+ " <feature>confirmed-commit</feature>\n"
+ " <feature>rollback-on-error</feature>\n"
+ " <feature>validate</feature>\n"
+ " <feature>startup</feature>\n"
+ " <feature>url</feature>\n"
+ " <feature>xpath</feature>\n"
+ " </module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-yang-metadata</name>\n"
+ " <revision>2016-08-05</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-inet-types</name>\n"
+ " <revision>2013-07-15</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-yang-types</name>\n"
+ " <revision>2013-07-15</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-datastores</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " </import-only-module>\n");
+ strcpy(with_netconf_features + strlen(with_netconf_features),
+ DATA_YANG_SCHEMA_MODULE_STATE
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <feature>writable-running</feature>\n"
+ " <feature>candidate</feature>\n"
+ " <feature>confirmed-commit</feature>\n"
+ " <feature>rollback-on-error</feature>\n"
+ " <feature>validate</feature>\n"
+ " <feature>startup</feature>\n"
+ " <feature>url</feature>\n"
+ " <feature>xpath</feature>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " <conformance-type>import</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>");
+
+ const char *garbage_revision =
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module-set>\n"
+ " <name>complete</name>\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2022-06-16</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-yang-library</name>\n"
+ " <revision>2019-01-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"
+ " </module>\n"
+ DATA_YANG_BASE_IMPORTS
+ DATA_YANG_SCHEMA_MODULE_STATE
+ "</modules-state>\n";
+
+ const char *no_yanglibrary =
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module-set>\n"
+ " <name>complete</name>\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2022-06-16</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"
+ " </module>\n"
+ DATA_YANG_BASE_IMPORTS
+ DATA_YANG_SCHEMA_MODULE_STATE
+ "</modules-state>\n";
+
+ (void) state;
+ /* seperate context to avoid double free during teadown */
+ struct ly_ctx *ctx_test = NULL;
+
+ /* test invalid parameters */
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, NULL, LYD_XML, 0, &ctx_test));
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, TESTS_SRC, LYD_XML, 0, NULL));
+ assert_int_equal(LY_ESYS, ly_ctx_new_ylpath(NULL, TESTS_SRC "garbage", LYD_XML, 0, &ctx_test));
+
+ /* basic test with ietf-yang-library-only */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", yanglibrary_only, LYD_XML, 0, &ctx_test));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04"));
+ assert_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"));
+ ly_ctx_destroy(ctx_test);
+ ctx_test = NULL;
+
+ /* test loading module, should also import other module */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf, LYD_XML, 0, &ctx_test));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"));
+ assert_int_equal(1, ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")->implemented);
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14"));
+ assert_int_equal(0, ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")->implemented);
+ assert_int_equal(LY_ENOT, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url"));
+ ly_ctx_destroy(ctx_test);
+ ctx_test = NULL;
+
+ /* test loading module with feature if they are present */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, 0, &ctx_test));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14"));
+ assert_int_equal(LY_SUCCESS, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url"));
+ ly_ctx_destroy(ctx_test);
+ ctx_test = NULL;
+
+ /* test with not matching revision */
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", garbage_revision, LYD_XML, 0, &ctx_test));
+
+ /* test data containing ietf-yang-library which conflicts with the option */
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test));
+
+ /* test creating without ietf-yang-library */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", no_yanglibrary, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test));
+ assert_int_equal(NULL, ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04"));
+ ly_ctx_destroy(ctx_test);
+ free(with_netconf_features);
+}
+
+static LY_ERR
+check_node_priv_parsed_is_set(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue))
+{
+ const struct lysp_node *pnode;
+ const char ***iter;
+
+ pnode = (const struct lysp_node *)node->priv;
+ CHECK_POINTER(pnode, 1);
+ iter = (const char ***)data;
+ CHECK_POINTER(**iter, 1);
+ CHECK_STRING(pnode->name, **iter);
+ (*iter)++;
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+check_node_priv_parsed_not_set(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue))
+{
+ CHECK_POINTER(node->priv, 0);
+ return LY_SUCCESS;
+}
+
+static void
+check_ext_instance_priv_parsed_is_set(struct lysc_ext_instance *ext)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysc_ext_substmt *substmts;
+ struct lysc_node *cnode;
+ const char **iter;
+ const char *check[] = {
+ "tmp_cont", "lf", NULL
+ };
+
+ LY_ARRAY_FOR(ext, u) {
+ substmts = ext[u].substmts;
+ LY_ARRAY_FOR(substmts, v) {
+ if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) {
+ cnode = *(struct lysc_node **)substmts[v].storage;
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_tree_dfs_full(cnode, check_node_priv_parsed_is_set, &iter));
+ }
+ }
+ }
+}
+
+static void
+check_ext_instance_priv_parsed_not_set(struct lysc_ext_instance *ext)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysc_ext_substmt *substmts;
+ struct lysc_node *cnode;
+
+ LY_ARRAY_FOR(ext, u) {
+ substmts = ext[u].substmts;
+ LY_ARRAY_FOR(substmts, v) {
+ if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) {
+ cnode = *(struct lysc_node **)substmts[v].storage;
+ if (cnode) {
+ CHECK_POINTER((struct lysp_node *)cnode->priv, 0);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * @brief Testing of LY_CTX_SET_PRIV_PARSED.
+ */
+static void
+test_set_priv_parsed(void **state)
+{
+ struct lys_module *mod;
+ const char *schema_a;
+ const char **iter;
+ const char *check[] = {
+ "cont", "contnotif", "contx", "grpleaf", "augleaf", "l1",
+ "l1a", "l1b", "l1c", "foo1", "ll", "any", "l2",
+ "l2c", "l2cx", "ch", "cas", "casx", "oper",
+ "input", "inparam", "output", "outparam", "n1", NULL
+ };
+
+ /* each node must have a unique name. */
+ schema_a = "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;yang-version 1.1;\n"
+ "\n"
+ " import ietf-restconf {\n"
+ " prefix rc;\n"
+ " revision-date 2017-01-26;\n"
+ " }\n"
+ "\n"
+ " rc:yang-data \"tmp\" {\n"
+ " container tmp_cont {\n"
+ " leaf lf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container cont {\n"
+ " notification contnotif;\n"
+ " leaf-list contx {\n"
+ " type string;\n"
+ " }\n"
+ " uses grp;\n"
+ " }\n"
+ " list l1 {\n"
+ " key \"l1a l1b\";\n"
+ " leaf l1a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l1b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l1c {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " feature f1;\n"
+ " feature f2;\n"
+ " leaf foo1 {\n"
+ " type uint16;\n"
+ " if-feature f1;\n"
+ " }\n"
+ " leaf foo2 {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf foo3 {\n"
+ " type uint16;\n"
+ " if-feature f2;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ " anydata any {\n"
+ " config false;\n"
+ " }\n"
+ " list l2 {\n"
+ " config false;\n"
+ " container l2c {\n"
+ " leaf l2cx {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " choice ch {\n"
+ " case cas {\n"
+ " leaf casx {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " rpc oper {\n"
+ " input {\n"
+ " leaf inparam {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf outparam {\n"
+ " type int8;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " notification n1;\n"
+ " grouping grp {\n"
+ " leaf grpleaf {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ " augment /cont {\n"
+ " leaf augleaf {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ " deviation /a:foo2 {\n"
+ " deviate not-supported;\n"
+ " }\n"
+ "}\n";
+
+ /* use own context with extra flags */
+ 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_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);
+
+ print_message("[ ] create context\n");
+ mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL);
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter));
+ check_ext_instance_priv_parsed_is_set(mod->compiled->exts);
+
+ print_message("[ ] unset option\n");
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED));
+ mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL);
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_not_set, &iter));
+ check_ext_instance_priv_parsed_not_set(mod->compiled->exts);
+
+ print_message("[ ] set option\n");
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED));
+ mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL);
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter));
+ check_ext_instance_priv_parsed_is_set(mod->compiled->exts);
+}
+
+static void
+test_explicit_compile(void **state)
+{
+ uint32_t i;
+ struct lys_module *mod;
+ const char *schema_a = "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;yang-version 1.1;\n"
+ " feature f1;\n"
+ " feature f2;\n"
+ " leaf foo1 {\n"
+ " type uint16;\n"
+ " if-feature f1;\n"
+ " }\n"
+ " leaf foo2 {\n"
+ " type uint16;\n"
+ " }\n"
+ " container cont {\n"
+ " leaf foo3 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ const char *schema_b = "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;yang-version 1.1;\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ " augment /a:cont {\n"
+ " leaf augleaf {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ const char *schema_c = "module c {\n"
+ " namespace urn:tests:c;\n"
+ " prefix c;yang-version 1.1;\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ " deviation /a:foo2 {\n"
+ " deviate not-supported;\n"
+ " }\n"
+ "}\n";
+
+ /* use own context with extra flags */
+ 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));
+ 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);
+ assert_int_equal(LY_SUCCESS, lys_set_implemented((struct lys_module *)mod, feats));
+
+ /* none of the modules should be compiled */
+ i = 0;
+ while ((mod = ly_ctx_get_module_iter(UTEST_LYCTX, &i))) {
+ assert_null(mod->compiled);
+ }
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_compile(UTEST_LYCTX));
+
+ /* check internal modules */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-datastores");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-library");
+ assert_non_null(mod);
+
+ /* check test modules */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c");
+ assert_non_null(mod);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_searchdirs),
+ UTEST(test_options),
+ UTEST(test_models),
+ UTEST(test_imports),
+ UTEST(test_get_models),
+ UTEST(test_ylmem),
+ UTEST(test_set_priv_parsed),
+ UTEST(test_explicit_compile),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_hash_table.c b/tests/utests/basic/test_hash_table.c
new file mode 100644
index 0000000..25b595a
--- /dev/null
+++ b/tests/utests/basic/test_hash_table.c
@@ -0,0 +1,271 @@
+/*
+ * @file test_hash_table.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from hash_table.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdlib.h>
+
+#include "common.h"
+#include "hash_table.h"
+
+struct ht_rec *lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx);
+
+static void
+test_invalid_arguments(void **state)
+{
+ assert_int_equal(LY_EINVAL, lydict_insert(NULL, NULL, 0, NULL));
+ CHECK_LOG("Invalid argument ctx (lydict_insert()).", NULL);
+
+ assert_int_equal(LY_EINVAL, lydict_insert_zc(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (lydict_insert_zc()).", NULL);
+ assert_int_equal(LY_EINVAL, lydict_insert_zc(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument str_p (lydict_insert_zc()).", NULL);
+}
+
+static void
+test_dict_hit(void **state)
+{
+ const char *str1, *str2, *str3;
+
+ /* insert 2 strings, one of them repeatedly */
+ assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "test1", 0, &str1));
+ assert_non_null(str1);
+ /* via zerocopy we have to get the same pointer as provided */
+ assert_non_null(str2 = strdup("test2"));
+ assert_int_equal(LY_SUCCESS, lydict_insert_zc(UTEST_LYCTX, (char *)str2, &str3));
+ assert_ptr_equal(str2, str3);
+ /* here we get the same pointer as in case the string was inserted first time */
+ assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "test1", 0, &str2));
+ assert_non_null(str2);
+ assert_ptr_equal(str1, str2);
+
+ /* remove strings, but the repeatedly inserted only once */
+ lydict_remove(UTEST_LYCTX, "test1");
+ lydict_remove(UTEST_LYCTX, "test2");
+
+ /* destroy dictionary - should raise warning about data presence */
+ ly_ctx_destroy(UTEST_LYCTX);
+ UTEST_LYCTX = NULL;
+ CHECK_LOG("String \"test1\" not freed from the dictionary, refcount 1", NULL);
+
+#ifndef NDEBUG
+ /* cleanup */
+ free((char *)str1);
+#endif
+}
+
+static uint8_t
+ht_equal_clb(void *val1, void *val2, uint8_t mod, void *cb_data)
+{
+ int *v1, *v2;
+
+ (void)mod;
+ (void)cb_data;
+
+ v1 = (int *)val1;
+ v2 = (int *)val2;
+
+ return *v1 == *v2;
+}
+
+static void
+test_ht_basic(void **state)
+{
+ uint32_t i;
+ struct hash_table *ht;
+
+ assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 0));
+
+ i = 2;
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL));
+ assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
+ CHECK_LOG("Invalid argument hash (lyht_remove_with_resize_cb()).", NULL);
+
+ lyht_free(ht);
+}
+
+static void
+test_ht_resize(void **state)
+{
+ uint32_t i;
+ struct ht_rec *rec;
+ struct hash_table *ht;
+
+ assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
+ assert_int_equal(8, ht->size);
+
+ /* insert records into indexes 2-7 */
+ for (i = 2; i < 8; ++i) {
+ assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
+ }
+ /* check that table resized */
+ assert_int_equal(16, ht->size);
+
+ /* check expected content of the table */
+ for (i = 0; i < 16; ++i) {
+ if ((i >= 2) && (i < 8)) {
+ /* inserted data on indexes 2-7 */
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(1, rec->hits);
+ assert_int_equal(i, rec->hash);
+ } else {
+ /* nothing otherwise */
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(0, rec->hits);
+ }
+ }
+
+ /* removing not present data should fail */
+ for (i = 0; i < 2; ++i) {
+ UTEST_LOG_CLEAN;
+ assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
+ CHECK_LOG("Invalid argument hash (lyht_remove_with_resize_cb()).", NULL);
+ }
+ /* removing present data, resize should happened
+ * when we are below 25% of the table filled, so with 3 records left */
+ for ( ; i < 5; ++i) {
+ assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
+ }
+ assert_int_equal(8, ht->size);
+
+ /* remove the rest */
+ for ( ; i < 8; ++i) {
+ assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
+ }
+
+ for (i = 0; i < 8; ++i) {
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ }
+
+ /* cleanup */
+ lyht_free(ht);
+}
+
+static void
+test_ht_collisions(void **UNUSED(state))
+{
+#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val))
+
+ uint32_t i;
+ struct ht_rec *rec;
+ struct hash_table *ht;
+
+ assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
+
+ for (i = 2; i < 6; ++i) {
+ assert_int_equal(lyht_insert(ht, &i, 2, NULL), 0);
+ }
+
+ /* check all records */
+ for (i = 0; i < 2; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 4);
+ assert_int_equal(GET_REC_INT(rec), i);
+ ++i;
+ for ( ; i < 6; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 1);
+ assert_int_equal(GET_REC_INT(rec), i);
+ }
+ for ( ; i < 8; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+
+ i = 4;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, -1);
+
+ i = 2;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ /* check all records */
+ for (i = 0; i < 2; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 2);
+ assert_int_equal(GET_REC_INT(rec), 5);
+ ++i;
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 1);
+ assert_int_equal(GET_REC_INT(rec), 3);
+ ++i;
+ for ( ; i < 6; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, -1);
+ }
+ for ( ; i < 8; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+
+ for (i = 0; i < 3; ++i) {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ }
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
+ ++i;
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ ++i;
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
+ ++i;
+ for ( ; i < 8; ++i) {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ }
+
+ i = 3;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+ i = 5;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ /* check all records */
+ for (i = 0; i < 2; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+ for ( ; i < 6; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, -1);
+ }
+ for ( ; i < 8; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+
+ lyht_free(ht);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_invalid_arguments),
+ UTEST(test_dict_hit),
+ UTEST(test_ht_basic),
+ UTEST(test_ht_resize),
+ UTEST(test_ht_collisions),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_inout.c b/tests/utests/basic/test_inout.c
new file mode 100644
index 0000000..be27510
--- /dev/null
+++ b/tests/utests/basic/test_inout.c
@@ -0,0 +1,401 @@
+/**
+ * @file test_inout.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for input and output handlers functions
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "in.h"
+#include "log.h"
+#include "out.h"
+
+#define TEST_INPUT_FILE TESTS_BIN "/libyang_test_input"
+#define TEST_OUTPUT_FILE TESTS_BIN "/libyang_test_output"
+#define TEST_OUTPUT_FILE2 TESTS_BIN "/libyang_test_output2"
+
+static int
+setup_files(void **state)
+{
+ int fd;
+
+ UTEST_SETUP;
+
+ /* create input */
+ fd = open(TEST_INPUT_FILE, O_CREAT | O_WRONLY, 00600);
+ if (fd == -1) {
+ return 1;
+ }
+
+ /* write something */
+ if (write(fd, "data", 4) != 4) {
+ return 1;
+ }
+ close(fd);
+
+ /* create output */
+ fd = open(TEST_OUTPUT_FILE, O_CREAT | O_RDONLY, 00600);
+ if (fd == -1) {
+ return 1;
+ }
+ close(fd);
+
+ /* create output2 */
+ fd = open(TEST_OUTPUT_FILE2, O_CREAT | O_RDONLY, 00600);
+ if (fd == -1) {
+ return 1;
+ }
+ close(fd);
+
+ return 0;
+}
+
+static int
+teardown_files(void **state)
+{
+ unlink(TEST_INPUT_FILE);
+ unlink(TEST_OUTPUT_FILE);
+ unlink(TEST_OUTPUT_FILE2);
+
+ UTEST_TEARDOWN;
+ return 0;
+}
+
+static void
+test_input_mem(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ char *str1 = "a", *str2 = "b";
+
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL));
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL));
+ assert_null(ly_in_memory(NULL, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in));
+ assert_int_equal(LY_IN_MEMORY, ly_in_type(in));
+ assert_ptr_equal(str1, ly_in_memory(in, str2));
+ assert_ptr_equal(str2, ly_in_memory(in, NULL));
+ assert_ptr_equal(str2, ly_in_memory(in, NULL));
+ ly_in_free(in, 0);
+}
+
+static void
+test_input_fd(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ int fd1, fd2;
+ struct stat statbuf;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(-1, NULL));
+ assert_int_equal(-1, ly_in_fd(NULL, -1));
+
+ assert_int_not_equal(-1, fd1 = open(TEST_INPUT_FILE, O_RDONLY));
+ assert_int_not_equal(-1, fd2 = open(TEST_INPUT_FILE, O_RDONLY));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in));
+ assert_int_equal(LY_IN_FD, ly_in_type(in));
+ assert_ptr_equal(fd1, ly_in_fd(in, fd2));
+ assert_ptr_equal(fd2, ly_in_fd(in, -1));
+ assert_ptr_equal(fd2, ly_in_fd(in, -1));
+ ly_in_free(in, 1);
+ /* fd1 is still open */
+ assert_int_equal(0, fstat(fd1, &statbuf));
+ close(fd1);
+#ifndef _WIN32
+ /* But fd2 was closed by ly_in_free(). This results in an "invalid handler" on Windows. */
+ errno = 0;
+ assert_int_equal(-1, fstat(fd2, &statbuf));
+ assert_int_equal(errno, EBADF);
+#endif
+}
+
+static void
+test_input_file(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ FILE *f1 = NULL, *f2 = NULL;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_file(NULL, NULL));
+ assert_null(ly_in_file(NULL, NULL));
+
+ assert_non_null(f1 = fopen(TEST_INPUT_FILE, "rb"));
+ assert_non_null(f2 = fopen(TEST_INPUT_FILE, "rb"));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_file(f1, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_file(f1, &in));
+ assert_int_equal(LY_IN_FILE, ly_in_type(in));
+ assert_ptr_equal(f1, ly_in_file(in, f2));
+ assert_ptr_equal(f2, ly_in_file(in, NULL));
+ assert_ptr_equal(f2, ly_in_file(in, NULL));
+ ly_in_free(in, 1);
+ /* f1 is still open */
+ assert_int_not_equal(-1, fileno(f1));
+ fclose(f1);
+ /* but f2 was closed by ly_in_free() */
+}
+
+static void
+test_input_filepath(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ const char *path1 = TEST_INPUT_FILE, *path2 = TEST_INPUT_FILE;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_filepath(NULL, 0, NULL));
+ assert_int_equal(LY_EINVAL, ly_in_new_filepath(path1, 0, NULL));
+ assert_ptr_equal(((void *)-1), ly_in_filepath(NULL, NULL, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_filepath(path1, 0, &in));
+ assert_int_equal(LY_IN_FILEPATH, ly_in_type(in));
+ assert_ptr_equal(NULL, ly_in_filepath(in, path2, 0));
+ assert_string_equal(path2, ly_in_filepath(in, NULL, 0));
+ ly_in_free(in, 0);
+}
+
+static void
+test_output_mem(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ char *buf1 = NULL, *buf2 = NULL;
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+ assert_int_equal(LY_OUT_MEMORY, ly_out_type(out));
+ ly_write(out, "test", 4);
+ assert_ptr_equal(buf1, ly_out_memory(out, &buf2, 0));
+ assert_ptr_equal(buf2, ly_out_memory(out, NULL, 0));
+ assert_ptr_equal(buf2, ly_out_memory(out, &buf1, strlen(buf1)));
+ ly_out_free(out, NULL, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, strlen(buf1), &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ assert_string_equal("test print", buf1);
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ assert_string_equal("rewrite", buf1);
+ ly_out_free(out, NULL, 1);
+}
+
+static void
+test_output_fd(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ assert_int_equal(LY_OUT_FD, ly_out_type(out));
+ assert_ptr_equal(fd1, ly_out_fd(out, fd2));
+ assert_ptr_equal(fd2, ly_out_fd(out, -1));
+ assert_ptr_equal(fd2, ly_out_fd(out, fd1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ /* truncate file to start with no data */
+ assert_int_equal(0, ftruncate(fd1, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_int_equal(10, read(fd2, buf, 30));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, lseek(fd2, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_int_equal(8, read(fd2, buf, 30));
+ assert_string_equal("rewrite", buf);
+
+ close(fd2);
+ ly_out_free(out, NULL, 1);
+}
+
+static void
+test_output_file(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ FILE *f1, *f2;
+ char buf[31] = {0};
+
+ assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb"));
+ assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "wb"));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ assert_int_equal(LY_OUT_FILE, ly_out_type(out));
+ assert_ptr_equal(f1, ly_out_file(out, f2));
+ assert_ptr_equal(f2, ly_out_file(out, NULL));
+ assert_ptr_equal(f2, ly_out_file(out, f1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, fclose(f2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb"));
+ assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "rb"));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f2));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, fseek(f2, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f2));
+ assert_string_equal("rewrite", buf);
+
+ fclose(f2);
+ ly_out_free(out, NULL, 1);
+}
+
+static void
+test_output_filepath(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ FILE *f1;
+ char buf[31] = {0};
+ const char *fp1 = TEST_OUTPUT_FILE;
+ const char *fp2 = TEST_OUTPUT_FILE2;
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ assert_int_equal(LY_OUT_FILEPATH, ly_out_type(out));
+ assert_ptr_equal(NULL, ly_out_filepath(out, fp2));
+ assert_string_equal(fp2, ly_out_filepath(out, NULL));
+ assert_ptr_equal(NULL, ly_out_filepath(out, fp1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_non_null(f1 = fopen(fp1, "rb"));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f1));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, fseek(f1, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f1));
+ assert_string_equal("rewrite", buf);
+
+ fclose(f1);
+ ly_out_free(out, NULL, 1);
+}
+
+static ssize_t
+write_clb(void *user_data, const void *buf, size_t count)
+{
+ return write((uintptr_t)user_data, buf, count);
+}
+
+void
+close_clb(void *arg)
+{
+ close((uintptr_t)arg);
+}
+
+static void
+test_output_clb(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out));
+ assert_int_equal(LY_OUT_CALLBACK, ly_out_type(out));
+ assert_ptr_equal(fd1, ly_out_clb_arg(out, (void *)(intptr_t)fd2));
+ assert_ptr_equal(fd2, ly_out_clb_arg(out, NULL));
+ assert_ptr_equal(fd2, ly_out_clb_arg(out, (void *)(intptr_t)fd1));
+ assert_ptr_equal(write_clb, ly_out_clb(out, write_clb));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out));
+ ly_out_free(out, close_clb, 0);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR));
+ /* truncate file to start with no data */
+ assert_int_equal(0, ftruncate(fd1, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ assert_int_equal(10, read(fd2, buf, 30));
+ assert_string_equal("test print", buf);
+
+ close(fd2);
+ ly_out_free(out, close_clb, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_input_mem),
+ UTEST(test_input_fd, setup_files, teardown_files),
+ UTEST(test_input_file, setup_files, teardown_files),
+ UTEST(test_input_filepath, setup_files, teardown_files),
+ UTEST(test_output_mem),
+ UTEST(test_output_fd, setup_files, teardown_files),
+ UTEST(test_output_file, setup_files, teardown_files),
+ UTEST(test_output_filepath, setup_files, teardown_files),
+ UTEST(test_output_clb, setup_files, teardown_files),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_json.c b/tests/utests/basic/test_json.c
new file mode 100644
index 0000000..1896b8a
--- /dev/null
+++ b/tests/utests/basic/test_json.c
@@ -0,0 +1,794 @@
+/*
+ * @file test_json.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for a generic JSON parser
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "context.h"
+#include "in_internal.h"
+#include "json.h"
+static void
+test_general(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = "";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ str = " \n\t \n";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* constant values */
+ str = "true";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ str = "false";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ str = "null";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_number(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* simple value */
+ str = "11";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("11", jsonctx->value);
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* fraction number */
+ str = "37.7668";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("37.7668", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* negative number */
+ str = "-122.3959";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-122.3959", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* integer, positive exponent */
+ str = "550E3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("550000", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-550E3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-550000", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* integer, negative exponent */
+ str = "1E-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.1", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "15E-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("1.5", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-15E-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-1.5", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "16E-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.16", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-16E-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-0.16", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "17E-3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.017", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-17E-3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-0.017", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "21000E-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("210", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "21000E-4";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("2.1", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "21000E-7";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.0021", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* decimal number, positive exponent */
+ str = "5.087E1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("50.87", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-5.087E1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-50.87", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "5.087E5";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("508700", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "59.1e+1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("591", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.05087", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.5087", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E6";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("5087", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.05087E6";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("50870", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E8";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("508700", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* decimal number, negative exponent */
+ str = "35.94e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("3.594", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-35.94e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("-3.594", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "35.94e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.3594", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "35.94e-3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.03594", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.3594e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.03594", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.03594e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.003594", jsonctx->value);
+ assert_int_equal(8, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.003594e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.0003594", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.3594e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.003594", jsonctx->value);
+ assert_int_equal(8, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.03594e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.0003594", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.003594e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.00003594", jsonctx->value);
+ assert_int_equal(10, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* zero */
+ str = "0";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_true(jsonctx->value[0] == '0');
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-0";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_true(jsonctx->value[0] == '-');
+ assert_true(jsonctx->value[1] == '0');
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "94E0";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_true(jsonctx->value[0] == '9');
+ assert_true(jsonctx->value[1] == '4');
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0E2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_true(jsonctx->value[0] == '0');
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-0E2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_true(jsonctx->value[0] == '-');
+ assert_true(jsonctx->value[1] == '0');
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "5.320e+2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("532", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "5.320e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("0.532", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* various invalid inputs */
+ str = "-x";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", "Line number 1.");
+
+ str = " -";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
+
+ str = "--1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"-\").", "Line number 1.");
+
+ str = "+1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character sequence \"+1\", expected a JSON value.", "Line number 1.");
+
+ str = " 1.x ";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", "Line number 1.");
+
+ str = "1.";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
+
+ str = " 1eo ";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"o\").", "Line number 1.");
+
+ str = "1e";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
+
+ str = "1E1000";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.", "Line number 1.");
+
+ str = "1e9999999999999999999";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1e9999999999999999999).", "Line number 1.");
+
+ str = "1.1e66000";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e66000).", "Line number 1.");
+
+ str = "1.1e-66000";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e-66000).", "Line number 1.");
+
+ str = "-2.1e0.";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Unexpected character \".\" after JSON number.", "Line number 1.");
+
+ ly_in_free(in, 0);
+}
+
+/* now string is tested in file ./tests/utests/types/string.c */
+static void
+test_string(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in = NULL;
+ const char *str;
+
+ str = "";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+
+#if 0
+ /* simple string */
+ str = "\"hello\"";
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_ptr_equal(&str[1], jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* 4-byte utf8 character */
+ str = "\"\\t𠜎\"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("\t𠜎", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* valid escape sequences - note that here it mixes valid JSON string characters (RFC 7159, sec. 7) and
+ * valid characters in YANG string type (RFC 7950, sec. 9.4). Since the latter is a subset of JSON string,
+ * the YANG string type's restrictions apply to the JSON escape sequences */
+ str = "\"\\\" \\\\ \\r \\/ \\n \\t \\u20ac\"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("\" \\ \r / \n \t €", jsonctx->value);
+ assert_int_equal(15, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* backspace and form feed are valid JSON escape sequences, but the control characters they represents are not allowed values for YANG string type */
+ str = "\"\\b\"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character reference \"\\b\" (0x00000008).", "Line number 1.");
+
+ str = "\"\\f\"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character reference \"\\f\" (0x0000000c).", "Line number 1.");
+#endif
+
+ /* unterminated string */
+ str = "\"unterminated string";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Missing quotation-mark at the end of a JSON string.", "Line number 1.");
+#if 0
+ /* invalid escape sequence */
+ str = "\"char \\x \"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character escape sequence \\x.", "Line number 1.");
+
+ /* new line is allowed only as escaped character in JSON */
+ str = "\"\n\"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON string \"\n\" (0x0000000a).", "Line number 1.");
+#endif
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_object(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = " { } ";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT_EMPTY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* simple value */
+ str = "{\"name\" : \"Radek\"}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_ptr_equal(&str[2], jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("\"Radek\"}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("Radek\"}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* two values */
+ str = "{\"smart\" : true,\"handsom\":false}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("smart\" : true,\"handsom\":false}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("true,\"handsom\":false}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal(",\"handsom\":false}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("handsom\":false}", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("false}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* inherited objects */
+ str = "{\"person\" : {\"name\":\"Radek\"}}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("person\" : {\"name\":\"Radek\"}}", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("{\"name\":\"Radek\"}}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("name\":\"Radek\"}}", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("\"Radek\"}}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("Radek\"}}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("}}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* new line is allowed only as escaped character in JSON */
+ str = "{ unquoted : \"data\"}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ CHECK_LOG_CTX("Invalid character sequence \"unquoted : \"data\"}\", expected a JSON object's member.", "Line number 1.");
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_array(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = " [ ] ";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY_EMPTY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* simple value */
+ str = "[ null]";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx, 0));
+ assert_null(jsonctx->value);
+ assert_int_equal(0, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("null]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* two values */
+ str = "[{\"a\":null},\"x\"]";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx, 0));
+ assert_null(jsonctx->value);
+ assert_int_equal(0, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("{\"a\":null},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("a\":null},\"x\"]", jsonctx->value);
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("null},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal(",\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("x\"]", jsonctx->value);
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* new line is allowed only as escaped character in JSON */
+ str = "[ , null]";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL));
+ CHECK_LOG_CTX("Invalid character sequence \", null]\", expected a JSON value.", "Line number 1.");
+ lyjson_ctx_free(jsonctx);
+
+ ly_in_free(in, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_general),
+ UTEST(test_number),
+ UTEST(test_string),
+ UTEST(test_object),
+ UTEST(test_array),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_plugins.c b/tests/utests/basic/test_plugins.c
new file mode 100644
index 0000000..7f3fe40
--- /dev/null
+++ b/tests/utests/basic/test_plugins.c
@@ -0,0 +1,109 @@
+/*
+ * @file test_plugins.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from set.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "plugins.h"
+#include "plugins_internal.h"
+
+const char *simple = "module libyang-plugins-simple {"
+ " namespace urn:libyang:tests:plugins:simple;"
+ " prefix s;"
+ " typedef note { type string; }"
+ " extension hint { argument value; }"
+ " leaf test {"
+ " type s:note {length 255;}"
+ " s:hint \"some hint here\";"
+ " }"
+ "}";
+
+static void
+test_add_invalid(void **state)
+{
+ (void)state;
+ assert_int_equal(LY_ESYS, lyplg_add(TESTS_BIN "/plugins/plugin_does_not_exist" LYPLG_SUFFIX));
+}
+
+static void
+test_add_simple(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_leaf *leaf;
+ struct lyplg_ext_record *record_e;
+ struct lyplg_type *plugin_t;
+
+ assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX));
+
+ UTEST_ADD_MODULE(simple, LYS_IN_YANG, NULL, &mod);
+
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+
+ assert_non_null(plugin_t = lyplg_type_plugin_find("libyang-plugins-simple", NULL, "note"));
+ assert_string_equal("ly2 simple test v1", plugin_t->id);
+ assert_ptr_equal(leaf->type->plugin, plugin_t);
+
+ assert_int_equal(1, LY_ARRAY_COUNT(leaf->exts));
+ assert_non_null(record_e = lyplg_ext_record_find("libyang-plugins-simple", NULL, "hint"));
+ assert_string_equal("ly2 simple test v1", record_e->plugin.id);
+ assert_ptr_equal(leaf->exts[0].def->plugin, &record_e->plugin);
+
+ /* the second loading of the same plugin - still success */
+ assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX));
+}
+
+static void
+test_not_implemented(void **state)
+{
+ struct lys_module *mod;
+ struct lyd_node *tree;
+ const char *schema = "module libyang-plugins-unknown {"
+ " namespace urn:libyang:tests:plugins:unknown;"
+ " prefix u;"
+ " extension myext;"
+ " typedef mytype { type string;}"
+ " leaf test {"
+ " u:myext;"
+ " type mytype;"
+ " }"
+ "}";
+ const char *data = "<test xmlns=\"urn:libyang:tests:plugins:unknown\">xxx</test>";
+ char *printed = NULL;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LOG_CTX(NULL, NULL);
+
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_add_invalid),
+ UTEST(test_add_simple),
+ UTEST(test_not_implemented),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_set.c b/tests/utests/basic/test_set.c
new file mode 100644
index 0000000..af39afa
--- /dev/null
+++ b/tests/utests/basic/test_set.c
@@ -0,0 +1,287 @@
+/*
+ * @file test_set.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from set.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _POSIX_C_SOURCE 200809L /* strdup */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "set.h"
+
+static void
+test_basics(void **UNUSED(state))
+{
+ struct ly_set *set;
+ char *str;
+ unsigned int u;
+ void *ptr;
+ uint32_t index;
+
+ /* creation - everything is empty */
+ assert_int_equal(LY_SUCCESS, ly_set_new(&set));
+ assert_non_null(set);
+ assert_int_equal(0, set->count);
+ assert_int_equal(0, set->size);
+ assert_null(set->objs);
+
+ /* add a testing object */
+ str = strdup("test string");
+ assert_non_null(str);
+
+ assert_int_equal(LY_SUCCESS, ly_set_add(set, str, 0, NULL));
+ assert_int_not_equal(0, set->size);
+ assert_int_equal(1, set->count);
+ assert_non_null(set->objs);
+ assert_non_null(set->objs[0]);
+
+ /* check the presence of the testing data */
+ assert_int_equal(1, ly_set_contains(set, str, &index));
+ assert_int_equal(0, index);
+ assert_int_equal(0, ly_set_contains(set, str - 1, NULL));
+
+ /* remove data, but keep the set */
+ u = set->size;
+ ptr = set->objs;
+ ly_set_clean(set, free);
+ assert_int_equal(0, set->count);
+ assert_int_equal(u, set->size);
+ assert_ptr_equal(ptr, set->objs);
+
+ /* remove buffer, but keep the set object */
+ ly_set_erase(set, NULL);
+ assert_int_equal(0, set->count);
+ assert_int_equal(0, set->size);
+ assert_ptr_equal(NULL, set->objs);
+
+ /* final cleanup */
+ ly_set_free(set, NULL);
+}
+
+static void
+test_inval(void **state)
+{
+ struct ly_set set;
+
+ memset(&set, 0, sizeof set);
+
+ ly_set_clean(NULL, NULL);
+ CHECK_LOG(NULL, NULL);
+
+ ly_set_erase(NULL, NULL);
+ CHECK_LOG(NULL, NULL);
+
+ ly_set_free(NULL, NULL);
+ CHECK_LOG(NULL, NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_dup(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_dup()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_add(NULL, NULL, 0, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_add()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_merge(NULL, NULL, 0, NULL));
+ CHECK_LOG("Invalid argument trg (ly_set_merge()).", NULL);
+ assert_int_equal(LY_SUCCESS, ly_set_merge(&set, NULL, 0, NULL));
+
+ assert_int_equal(LY_EINVAL, ly_set_rm_index(NULL, 0, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_rm_index()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_set_rm_index(&set, 1, NULL));
+ CHECK_LOG("Invalid argument index (ly_set_rm_index()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_rm(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_rm()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_set_rm(&set, NULL, NULL));
+ CHECK_LOG("Invalid argument object (ly_set_rm()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_set_rm(&set, &set, NULL));
+ CHECK_LOG("Invalid argument object (ly_set_rm()).", NULL);
+}
+
+static void
+test_duplication(void **UNUSED(state))
+{
+ struct ly_set *orig, *new;
+ char *str;
+ uint32_t index;
+
+ assert_int_equal(LY_SUCCESS, ly_set_new(&orig));
+ assert_non_null(orig);
+
+ /* add a testing object */
+ str = strdup("test string");
+ assert_non_null(str);
+ assert_int_equal(LY_SUCCESS, ly_set_add(orig, str, 0, &index));
+ assert_int_equal(0, index);
+
+ /* duplicate the set - without duplicator, so the new set will point to the same string */
+ assert_int_equal(LY_SUCCESS, ly_set_dup(orig, NULL, &new));
+ assert_non_null(new);
+ assert_ptr_not_equal(orig, new);
+ assert_int_equal(orig->count, new->count);
+ assert_ptr_equal(orig->objs[0], new->objs[0]);
+
+ ly_set_free(new, NULL);
+
+ /* duplicate the set - with duplicator, so the new set will point to a different buffer with the same content */
+ assert_int_equal(LY_SUCCESS, ly_set_dup(orig, (void *(*)(const void *))strdup, &new));
+ assert_non_null(new);
+ assert_ptr_not_equal(orig, new);
+ assert_int_equal(orig->count, new->count);
+ assert_ptr_not_equal(orig->objs[0], new->objs[0]);
+ assert_string_equal(orig->objs[0], new->objs[0]);
+
+ /* cleanup */
+ ly_set_free(new, free);
+ ly_set_free(orig, free);
+}
+
+static void
+test_add(void **UNUSED(state))
+{
+ uint32_t u, index;
+ char *str = "test string";
+ struct ly_set set;
+
+ memset(&set, 0, sizeof set);
+
+ /* add a testing object */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index));
+ assert_int_equal(0, index);
+
+ /* test avoiding data duplicities */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index));
+ assert_int_equal(0, index);
+ assert_int_equal(1, set.count);
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index));
+ assert_int_equal(1, index);
+ assert_int_equal(2, set.count);
+
+ /* test array resizing */
+ u = set.size;
+ for (uint32_t expected_index = 2; expected_index <= u; ++expected_index) {
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index));
+ assert_int_equal(expected_index, index);
+ }
+ assert_true(u != set.size);
+
+ /* cleanup */
+ ly_set_erase(&set, NULL);
+}
+
+static void
+test_merge(void **UNUSED(state))
+{
+ char *str1, *str2;
+ struct ly_set one, two;
+
+ memset(&one, 0, sizeof one);
+ memset(&two, 0, sizeof two);
+
+ str1 = strdup("string1");
+ str2 = strdup("string2");
+
+ /* fill first set
+ * - str1 is the same as in two, so it must not be freed! */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&one, str1, 0, NULL));
+
+ /* fill second set */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&two, str1, 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&two, str2, 0, NULL));
+
+ /* merge with checking duplicities - only one item is added into one;
+ * also without duplicating data, so it must not be freed at the end */
+ assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 0, NULL));
+ assert_int_equal(2, one.count);
+ assert_ptr_equal(one.objs[1], two.objs[1]);
+
+ /* clean and re-fill one (now duplicating str1, to allow testing duplicator) */
+ ly_set_clean(&one, NULL);
+ assert_int_equal(LY_SUCCESS, ly_set_add(&one, strdup(str1), 0, NULL));
+
+ /* merge without checking duplicities - two items are added into one;
+ * here also with duplicator */
+ assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 1, (void *(*)(const void *))strdup));
+ assert_int_equal(3, one.count);
+ assert_ptr_not_equal(one.objs[1], two.objs[0]);
+ assert_string_equal(one.objs[1], two.objs[0]);
+
+ /* cleanup */
+ ly_set_erase(&one, free);
+ ly_set_erase(&two, free);
+}
+
+static void
+test_rm(void **UNUSED(state))
+{
+ char *str1, *str2, *str3;
+ struct ly_set set;
+
+ memset(&set, 0, sizeof set);
+
+ /* fill the set */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string1", 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, strdup("string2"), 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string3", 0, NULL));
+
+ /* remove by index ... */
+ /* ... in the middle ... */
+ assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, free));
+ assert_int_equal(2, set.count);
+ assert_string_not_equal("string2", set.objs[0]);
+ assert_string_not_equal("string2", set.objs[1]);
+ /* ... last .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, NULL));
+ assert_int_equal(1, set.count);
+ assert_string_not_equal("string3", set.objs[0]);
+ /* ... first .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 0, NULL));
+ assert_int_equal(0, set.count);
+
+ /* fill the set */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str1 = "string1", 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str2 = "string2", 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str3 = strdup("string3"), 0, NULL));
+
+ /* remove by pointer ... */
+ /* ... in the middle ... */
+ assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str2, NULL));
+ assert_int_equal(2, set.count);
+ assert_string_not_equal("string2", set.objs[0]);
+ assert_string_not_equal("string2", set.objs[1]);
+ /* ... last (with destructor) .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str3, free));
+ assert_int_equal(1, set.count);
+ assert_string_not_equal("string3", set.objs[0]);
+ /* ... first .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str1, NULL));
+ assert_int_equal(0, set.count);
+
+ /* cleanup */
+ ly_set_erase(&set, NULL);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_basics),
+ UTEST(test_duplication),
+ UTEST(test_add),
+ UTEST(test_merge),
+ UTEST(test_rm),
+ UTEST(test_inval),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_xml.c b/tests/utests/basic/test_xml.c
new file mode 100644
index 0000000..668de4b
--- /dev/null
+++ b/tests/utests/basic/test_xml.c
@@ -0,0 +1,690 @@
+/*
+ * @file test_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from xml.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include <string.h>
+
+#include "context.h"
+#include "in_internal.h"
+#include "xml.h"
+
+LY_ERR lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri);
+
+static void
+test_element(void **state)
+{
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = "";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* end element */
+ str = "</element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Stray closing element tag (\"element\").", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* no element */
+ UTEST_LOG_CLEAN;
+ str = "no data present";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"no data present\", expected element tag start ('<').", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* not supported DOCTYPE */
+ str = "<!DOCTYPE greeting SYSTEM \"hello.dtd\"><greeting/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Document Type Declaration not supported.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* invalid XML */
+ str = "<!NONSENSE/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Unknown XML section \"<!NONSENSE/>\".", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* namespace ambiguity */
+ str = "<element xmlns=\"urn1\" xmlns=\"urn2\"/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Duplicate default XML namespaces \"urn1\" and \"urn2\".", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* prefix duplicate */
+ str = "<element xmlns:a=\"urn1\" xmlns:a=\"urn2\"/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Duplicate XML NS prefix \"a\" used for namespaces \"urn1\" and \"urn2\".", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* unqualified element */
+ str = " < element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_null(xmlctx->prefix);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_int_equal(1, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* element with attribute */
+ str = " < element attr=\'x\'/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+ assert_int_equal(1, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_true(!strncmp("attr", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_int_equal(1, xmlctx->elements.count);
+ assert_true(!strncmp("x", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_int_equal(0, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* headers and comments */
+ str = "<?xml version=\"1.0\"?> <!-- comment --> <?TEST xxx?> <element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+ assert_int_equal(1, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* separate opening and closing tags, neamespaced parsed internally */
+ str = "<element xmlns=\"urn\"></element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+ assert_int_equal(1, xmlctx->elements.count);
+ assert_int_equal(1, xmlctx->ns.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_int_equal(0, xmlctx->elements.count);
+ assert_int_equal(0, xmlctx->ns.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* qualified element */
+ str = " < yin:element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_true(!strncmp("yin", xmlctx->prefix, xmlctx->prefix_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* non-matching closing tag */
+ str = "<yin:element xmlns=\"urn\"></element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_true(!strncmp("yin", xmlctx->prefix, xmlctx->prefix_len));
+ assert_int_equal(1, xmlctx->elements.count);
+ assert_int_equal(1, xmlctx->ns.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Opening (\"yin:element\") and closing (\"element\") elements tag mismatch.", "Line number 1.");
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* invalid closing tag */
+ str = "<yin:element xmlns=\"urn\"></yin:element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"/>\", expected element tag termination ('>').", "Line number 1.");
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* UTF8 characters */
+ str = "<𠜎€𠜎Øn:𠜎€𠜎Øn/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_true(!strncmp("𠜎€𠜎Øn", xmlctx->name, xmlctx->name_len));
+ assert_true(!strncmp("𠜎€𠜎Øn", xmlctx->prefix, xmlctx->prefix_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* invalid UTF-8 characters */
+ str = "<¢:element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Identifier \"¢:element>\" starts with an invalid character.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ str = "<yin:câelement>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"âelement>\", expected element tag end ('>' or '/>') or an attribute.", "Line number 1.");
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* mixed content */
+ str = "<a>text <b>x</b></a>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("a", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("text ", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("b", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("x", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* tag mismatch */
+ str = "<a>text</b>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("a", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("text", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Opening (\"a\") and closing (\"b\") elements tag mismatch.", "Line number 1.");
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_attribute(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ struct lyxml_ns *ns;
+
+ /* not an attribute */
+ str = "<e unknown/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"/>\", expected '='.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ str = "<e xxx=/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"/>\", expected either single or double quotation mark.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ str = "<e xxx\n = yyy/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"yyy/>\", expected either single or double quotation mark.", "Line number 2.");
+ ly_in_free(in, 0);
+
+ /* valid attribute */
+ str = "<e attr=\"val\"";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_true(!strncmp("attr", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("val", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 0);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* valid namespace with prefix */
+ str = "<e xmlns:nc\n = \'urn\'/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_int_equal(1, xmlctx->ns.count);
+ ns = (struct lyxml_ns *)xmlctx->ns.objs[0];
+ assert_string_equal(ns->prefix, "nc");
+ assert_string_equal(ns->uri, "urn");
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_text(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+
+ /* empty attribute value */
+ str = "<e a=\"\"";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 1);
+ assert_int_equal(xmlctx->dynamic, 0);
+ ly_in_free(in, 0);
+
+ /* empty value but in single quotes */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 1);
+ assert_int_equal(xmlctx->dynamic, 0);
+ ly_in_free(in, 0);
+
+ /* empty element content - only formating before defining child */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(">\n <y>", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEMENT;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("\n ", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 1);
+ assert_int_equal(xmlctx->dynamic, 0);
+ ly_in_free(in, 0);
+
+ /* empty element content is invalid - missing content terminating character < */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("xxx", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"xxx\", expected element tag start ('<').", "Line number 1.");
+ ly_in_free(in, 0);
+
+ lyxml_ctx_free(xmlctx);
+ LOG_LOCBACK(0, 0, 0, 4);
+
+ /* valid strings */
+ str = "<a>€𠜎Øn \n&lt;&amp;&quot;&apos;&gt; &#82;&#x4f;&#x4B;</a>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("€𠜎Øn \n<&\"\'> ROK", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 1);
+ free((char *)xmlctx->value);
+ ly_in_free(in, 0);
+
+ /* test using n-bytes UTF8 hexadecimal code points */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#x0024;&#x00A2;&#x20ac;&#x10348;\'", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("$¢€ðˆ", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 1);
+ ly_in_free(in, 0);
+
+ /* CDATA value */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("> <![CDATA[ special non-escaped chars <>&\"' ]]> </a>", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTR_CONTENT;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp(" special non-escaped chars <>&\"' ", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 1);
+ free((char *)xmlctx->value);
+ ly_in_free(in, 0);
+
+ /* invalid characters in string */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#x52\'", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"'\", expected ;.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&#82\"", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"\"\", expected ;.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&nonsense;\"", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Entity reference \"&nonsense;\" not supported, only predefined references allowed.", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(">&#o122;", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEMENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#o122;\".", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#x06;\'", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#x06;\'\" (0x00000006).", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#xfdd0;\'", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#xfdd0;\'\" (0x0000fdd0).", "Line number 1.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#xffff;\'", &in));
+ xmlctx->in = in;
+ LOG_LOCSET(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#xffff;\'\" (0x0000ffff).", "Line number 1.");
+ ly_in_free(in, 0);
+
+ lyxml_ctx_free(xmlctx);
+ LOG_LOCBACK(0, 0, 0, 9);
+}
+
+static void
+test_ns(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ const struct lyxml_ns *ns;
+
+ /* opening element1 */
+ str = "<element1/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+
+ /* processing namespace definitions */
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default")));
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc1")));
+ /* simulate adding open element2 into context */
+ xmlctx->elements.count++;
+ /* processing namespace definitions */
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc2")));
+ assert_int_equal(3, xmlctx->ns.count);
+ assert_int_not_equal(0, xmlctx->ns.size);
+
+ ns = lyxml_ns_get(&xmlctx->ns, NULL, 0);
+ assert_non_null(ns);
+ assert_null(ns->prefix);
+ assert_string_equal("urn:default", ns->uri);
+
+ ns = lyxml_ns_get(&xmlctx->ns, "nc", 2);
+ assert_non_null(ns);
+ assert_string_equal("nc", ns->prefix);
+ assert_string_equal("urn:nc2", ns->uri);
+
+ /* simulate closing element2 */
+ xmlctx->elements.count--;
+ lyxml_ns_rm(xmlctx);
+ assert_int_equal(2, xmlctx->ns.count);
+
+ ns = lyxml_ns_get(&xmlctx->ns, "nc", 2);
+ assert_non_null(ns);
+ assert_string_equal("nc", ns->prefix);
+ assert_string_equal("urn:nc1", ns->uri);
+
+ /* close element1 */
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(0, xmlctx->ns.count);
+
+ assert_null(lyxml_ns_get(&xmlctx->ns, "nc", 2));
+ assert_null(lyxml_ns_get(&xmlctx->ns, NULL, 0));
+
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_ns2(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+
+ /* opening element1 */
+ str = "<element1/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+
+ /* default namespace defined in parent element1 */
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default")));
+ assert_int_equal(1, xmlctx->ns.count);
+ /* going into child element1 */
+ /* simulate adding open element1 into context */
+ xmlctx->elements.count++;
+ /* no namespace defined, going out (first, simulate closing of so far open element) */
+ xmlctx->elements.count--;
+ lyxml_ns_rm(xmlctx);
+ assert_int_equal(1, xmlctx->ns.count);
+
+ /* nothing else, going out of the parent element1 */
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(0, xmlctx->ns.count);
+
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_simple_xml(void **state)
+{
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ const char *test_input = "<elem1 attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>";
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(test_input, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "=\"value\"> <elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "> <elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "<elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, " /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "/> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, " </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "");
+
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_element),
+ UTEST(test_attribute),
+ UTEST(test_text),
+ UTEST(test_ns),
+ UTEST(test_ns2),
+ UTEST(test_simple_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_xpath.c b/tests/utests/basic/test_xpath.c
new file mode 100644
index 0000000..b388dc3
--- /dev/null
+++ b/tests/utests/basic/test_xpath.c
@@ -0,0 +1,1071 @@
+/**
+ * @file test_xpath.c
+ * @author: Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for XPath evaluation
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <string.h>
+
+#include "context.h"
+#include "parser_data.h"
+#include "set.h"
+#include "tests_config.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+const char *schema_a =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " identity id_a;\n"
+ " identity id_b {\n"
+ " base id_a;\n"
+ " }\n"
+ " identity id_c {\n"
+ " base id_b;\n"
+ " }\n"
+ "\n"
+ " list l1 {\n"
+ " key \"a b\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf foo {\n"
+ " type string;\n"
+ " }\n"
+ " leaf foo2 {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf foo3 {\n"
+ " type identityref {\n"
+ " base id_a;\n"
+ " }\n"
+ " }\n"
+ " leaf foo4 {\n"
+ " type decimal64 {\n"
+ " fraction-digits 5;\n"
+ " }\n"
+ " }\n"
+ " container c {\n"
+ " leaf x {\n"
+ " type string;\n"
+ " }\n"
+ " list ll {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " list ll {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " leaf-list ll2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}";
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL);
+ lys_parse_path(UTEST_LYCTX, TESTS_DIR_MODULES_YANG "/ietf-interfaces@2014-05-08.yang", LYS_IN_YANG, NULL);
+
+ return 0;
+}
+
+static void
+test_predicate(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ data =
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a1</a>"
+ " <b>b1</b>"
+ " <c>c1</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a2</a>"
+ " <b>b2</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a3</a>"
+ " <b>b3</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a4</a>"
+ " <b>b4</b>"
+ " <c>c4</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a5</a>"
+ " <b>b5</b>"
+ " <c>c5</c>"
+ "</l1>"
+ "<c xmlns=\"urn:tests:a\">"
+ " <x>key2</x>"
+ " <ll>"
+ " <a>key1</a>"
+ " <ll>"
+ " <a>key11</a>"
+ " <b>val11</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key12</a>"
+ " <b>val12</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key13</a>"
+ " <b>val13</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key2</a>"
+ " <ll>"
+ " <a>key21</a>"
+ " <b>val21</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key22</a>"
+ " <b>val22</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key3</a>"
+ " <ll>"
+ " <a>key31</a>"
+ " <b>val31</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key32</a>"
+ " <b>val32</b>"
+ " </ll>"
+ " </ll>"
+ "</c>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* predicate after number */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/foo2[4[3 = 3]]", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ /* reverse axis */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/child::ll[2]/preceding::ll[3]", &set));
+ assert_int_equal(1, set->count);
+ assert_string_equal("key11", lyd_get_value(lyd_child(set->dnodes[0])));
+ ly_set_free(set, NULL);
+
+ /* special predicate evaluated using hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a=concat('a', '1')][b=substring('ab1',2)]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=../x]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* cannot use hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=substring(ll/a,1,4)]", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* nested predicate */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/a:ll[a:a=string(/a:l1[a:a='foo']/a:a)]/a:a", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_union(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* Predicate for operand. */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1[c[../a = 'a1'] | c]/a", &set));
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_invalid(void **state)
+{
+ const char *data =
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:foo2[.=]", &set));
+ assert_null(set);
+
+ assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:", &set));
+ assert_null(set);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_hash(void **state)
+{
+ const char *data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>\n"
+ "<foo xmlns=\"urn:tests:a\">foo value</foo>\n"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <x>val</x>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " <b>val</b>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " <b>val</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_c</a>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll2>one</ll2>\n"
+ " <ll2>two</ll2>\n"
+ " <ll2>three</ll2>\n"
+ " <ll2>four</ll2>\n"
+ "</c>";
+ struct lyd_node *tree, *node;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set));
+ assert_int_equal(1, set->count);
+
+ node = set->objs[0];
+ assert_string_equal(node->schema->name, "l1");
+ node = lyd_child(node);
+ assert_string_equal(node->schema->name, "a");
+ assert_string_equal(lyd_get_value(node), "a3");
+
+ ly_set_free(set, NULL);
+
+ /* hashes should be used for both searches (well, there are not enough nested ll instances, so technically not true) */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_b']/ll[a='val_b']", &set));
+ assert_int_equal(1, set->count);
+
+ node = set->objs[0];
+ assert_string_equal(node->schema->name, "ll");
+ node = lyd_child(node);
+ assert_string_equal(node->schema->name, "a");
+ assert_string_equal(lyd_get_value(node), "val_b");
+ node = node->next;
+ assert_string_equal(node->schema->name, "b");
+ assert_null(node->next);
+
+ ly_set_free(set, NULL);
+
+ /* hashes are not used */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set));
+ assert_int_equal(4, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* hashes used even for leaf-lists */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set));
+ assert_int_equal(1, set->count);
+
+ node = set->objs[0];
+ assert_string_equal(node->schema->name, "ll2");
+ assert_string_equal(lyd_get_value(node), "three");
+
+ ly_set_free(set, NULL);
+
+ /* not found using hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set));
+ assert_int_equal(0, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* white-spaces are also ok */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set));
+ assert_int_equal(1, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_toplevel(void **state)
+{
+ const char *schema_b =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " list l2 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}";
+ const char *data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>\n"
+ "<foo xmlns=\"urn:tests:a\">foo value</foo>\n"
+ "<l2 xmlns=\"urn:tests:b\">\n"
+ " <a>1</a>\n"
+ " <b>1</b>\n"
+ "</l2>\n"
+ "<l2 xmlns=\"urn:tests:b\">\n"
+ " <a>2</a>\n"
+ " <b>1</b>\n"
+ "</l2>\n"
+ "<l2 xmlns=\"urn:tests:b\">\n"
+ " <a>3</a>\n"
+ " <b>1</b>\n"
+ "</l2>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* all top-level nodes from one module (default container as well) */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set));
+ assert_int_equal(5, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all top-level nodes from all modules */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set));
+ assert_int_equal(8, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all nodes from one module */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set));
+ assert_int_equal(13, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all nodes from all modules */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set));
+ assert_int_equal(22, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all nodes from all modules #2 */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set));
+ assert_int_equal(22, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_atomize(void **state)
+{
+ struct ly_set *set;
+ const struct lys_module *mod;
+
+ mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a");
+ assert_non_null(mod);
+
+ /* some random paths just making sure the API function works */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*", 0, &set));
+ assert_int_equal(6, set->count);
+ ly_set_free(set, NULL);
+
+ /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//.", 0, &set));
+ assert_in_range(set->count, 17, UINT32_MAX);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:c/ll[a='val1']/ll[a='val2']/b", 0, &set));
+ assert_int_equal(6, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/ietf-interfaces:interfaces/*", 0, &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/*", 0, &set));
+ assert_int_equal(13, set->count);
+ ly_set_free(set, NULL);
+
+ /*
+ * axes
+ */
+
+ /* ancestor */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/a/ancestor::node()", 0, &set));
+ assert_int_equal(6, set->count);
+ ly_set_free(set, NULL);
+
+ /* ancestor-or-self */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/ancestor-or-self::ll", 0, &set));
+ assert_int_equal(5, set->count);
+ ly_set_free(set, NULL);
+
+ /* attribute */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/l1/attribute::key", 0, &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* child */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::l1/child::a", 0, &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/descendant::c/descendant::b", 0, &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant-or-self */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*/descendant-or-self::c", 0, &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ /* following */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following::a", 0, &set));
+ assert_int_equal(4, set->count);
+ ly_set_free(set, NULL);
+
+ /* following-sibling */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following-sibling::ll", 0, &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* parent */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:*/c/parent::l1", 0, &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:c//..", 0, &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/preceding::a", 0, &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding-sibling */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/ll/preceding-sibling::node()", 0, &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* self */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/self::c/ll/ll/b/self::b", 0, &set));
+ assert_int_equal(4, set->count);
+ ly_set_free(set, NULL);
+}
+
+static void
+test_canonize(void **state)
+{
+ const char *data =
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>"
+ "<foo3 xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\">a:id_b</foo3>"
+ "<foo4 xmlns=\"urn:tests:a\">250.5</foo4>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* integer */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo2[.='050']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* identityref */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[.='id_b']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* decimal64 */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo4[.='0250.500']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_derived_from(void **state)
+{
+ const char *data =
+ "<foo3 xmlns=\"urn:tests:a\">id_c</foo3>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_b')]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_a')]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_augment(void **state)
+{
+ const char *schema_b =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ "\n"
+ " augment /a:c {\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}";
+ const char *data =
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <x>value</x>\n"
+ " <ll>\n"
+ " <a>key</a>\n"
+ " </ll>\n"
+ " <a xmlns=\"urn:tests:b\">25</a>\n"
+ " <ll2>c1</ll2>\n"
+ "</c>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* get all children ignoring their module */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/*", &set));
+ assert_int_equal(4, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_variables(void **state)
+{
+ struct lyd_node *tree, *node;
+ struct ly_set *set;
+ const char *data;
+ struct lyxp_var *vars = NULL;
+
+#define LOCAL_SETUP(DATA, TREE) \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, DATA, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &TREE)); \
+ assert_non_null(TREE);
+
+#define SET_NODE(NODE, SET, INDEX) \
+ assert_non_null(SET); \
+ assert_true(INDEX < SET->count); \
+ NODE = SET->objs[INDEX];
+
+#define LOCAL_TEARDOWN(SET, TREE, VARS) \
+ ly_set_free(SET, NULL); \
+ lyd_free_all(TREE); \
+ lyxp_vars_free(VARS); \
+ vars = NULL;
+
+ /* Eval variable to number. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Eval variable to string. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"mstr\""));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "mstr");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Eval variable to set of nodes. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable in union expr. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c[../a = 'a3']"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[c[../a = 'a1'] | $var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ SET_NODE(node, set, 1);
+ assert_string_equal(lyd_get_value(node), "a3");
+ assert_int_equal(set->count, 2);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Predicate after variable. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var[../a = 'a1']]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable in variable. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "$var2"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"mstr\""));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "mstr");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Compare two variables. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"str\""));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"str\""));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[$var1 = $var2]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "mstr");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Arithmetic operation with variable. */
+ data =
+ "<foo2 xmlns=\"urn:tests:a\">4</foo2>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "2"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo2[.= ($var1 * 2)]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "4");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable as function parameter. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "./c"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[count($var) = 1]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable in path expr. */
+ /* NOTE: The variable can only be at the beginning of the expression path. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "/l1"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var/a]", vars, &set));
+ assert_int_equal(set->count, 2);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable as function. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "position()"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var = 2]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Dynamic change of value. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "1"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ ly_set_free(set, NULL);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable not defined. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"mstr\""));
+ assert_int_equal(LY_ENOTFOUND, lyd_find_xpath2(tree, "/foo[text() = $var55]", vars, &set));
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Syntax error in value. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
+ assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$var]", vars, &set));
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Prefix is not supported. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
+ assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$pref:var]", vars, &set));
+ assert_string_equal("Variable with prefix is not supported.", _UC->err_msg);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+#undef LOCAL_SETUP
+#undef LOCAL_TEARDOWN
+}
+
+static void
+test_axes(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ data =
+ "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n"
+ " <a>a1</a>\n"
+ " <b yang:operation=\"replace\">b1</b>\n"
+ " <c yang:operation=\"none\">c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n"
+ " <a yang:operation=\"none\" yang:key=\"[no-key='no-value']\">a3</a>\n"
+ " <b>b3</b>\n"
+ " <c yang:value=\"no-val\">c3</c>\n"
+ "</l1>"
+ "<c xmlns=\"urn:tests:a\">"
+ " <x>val</x>"
+ " <ll>"
+ " <a>key1</a>"
+ " <ll>"
+ " <a>key11</a>"
+ " <b>val11</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key12</a>"
+ " <b>val12</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key13</a>"
+ " <b>val13</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key2</a>"
+ " <ll>"
+ " <a>key21</a>"
+ " <b>val21</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key22</a>"
+ " <b>val22</b>"
+ " </ll>"
+ " </ll>"
+ "</c>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* ancestor */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/a/ancestor::node()", &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ /* ancestor-or-self */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/ancestor-or-self::ll", &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ /* attribute */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/@operation", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/attribute::key", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ /* child */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::l1/child::a", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant::c/descendant::b", &set));
+ assert_int_equal(5, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant-or-self */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//c", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant-or-self::node()/c", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* following */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following::a", &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ /* following-sibling */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following-sibling::ll", &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* parent */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::*/c/parent::l1", &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::c//..", &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/preceding::a", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding-sibling */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/ll/preceding-sibling::node()", &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* self */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/self::c/ll/ll/b/self::b", &set));
+ assert_int_equal(5, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_predicate, setup),
+ UTEST(test_union, setup),
+ UTEST(test_invalid, setup),
+ UTEST(test_hash, setup),
+ UTEST(test_toplevel, setup),
+ UTEST(test_atomize, setup),
+ UTEST(test_canonize, setup),
+ UTEST(test_derived_from, setup),
+ UTEST(test_augment, setup),
+ UTEST(test_variables, setup),
+ UTEST(test_axes, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_yanglib.c b/tests/utests/basic/test_yanglib.c
new file mode 100644
index 0000000..0349157
--- /dev/null
+++ b/tests/utests/basic/test_yanglib.c
@@ -0,0 +1,144 @@
+/**
+ * @file test_yanglib.c
+ * @author: Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for ietf-yang-library data
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <string.h>
+
+#include "context.h"
+#include "in.h"
+#include "log.h"
+#include "set.h"
+#include "tests_config.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+const char *schema_a =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " include a_sub;\n"
+ "\n"
+ " list l2 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}";
+const char *schema_b =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ "\n"
+ " deviation /a:l2 {\n"
+ " deviate add {\n"
+ " max-elements 40;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf foo {\n"
+ " type string;\n"
+ " }\n"
+ "}";
+
+static LY_ERR
+test_imp_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
+ LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ const char *schema_a_sub =
+ "submodule a_sub {\n"
+ " belongs-to a {\n"
+ " prefix a;\n"
+ " }\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " feature feat1;\n"
+ "\n"
+ " list l3 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ assert_string_equal(mod_name, "a");
+ assert_null(mod_rev);
+ if (!submod_name) {
+ return LY_ENOTFOUND;
+ }
+ assert_string_equal(submod_name, "a_sub");
+ assert_null(sub_rev);
+ assert_null(user_data);
+
+ *format = LYS_IN_YANG;
+ *module_data = schema_a_sub;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_yanglib(void **state)
+{
+ const char *feats[] = {"feat1", NULL};
+ struct lyd_node *tree;
+ struct ly_set *set;
+ LY_ERR ret;
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, NULL);
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL);
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "<<%u>>", ly_ctx_get_change_count(UTEST_LYCTX)));
+ lyd_free_all(tree);
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", -10));
+ lyd_free_all(tree);
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, ""));
+ lyd_free_all(tree);
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", ly_ctx_get_change_count(UTEST_LYCTX)));
+
+ /* make sure there is "a" with a submodule and deviation */
+ ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set/module[name='a'][submodule/name='a_sub']"
+ "[feature='feat1'][deviation='b']", &set);
+ assert_int_equal(ret, LY_SUCCESS);
+
+ assert_int_equal(set->count, 1);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_yanglib),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c
new file mode 100644
index 0000000..1b7592a
--- /dev/null
+++ b/tests/utests/data/test_diff.c
@@ -0,0 +1,1221 @@
+/**
+ * @file test_diff.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief tests for lyd_diff()
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+#define CHECK_PARSE_LYD(INPUT, MODEL) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, MODEL)
+
+#define CHECK_LYD_STRING(IN_MODEL, TEXT) \
+ CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS)
+
+#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OUT_MODEL) \
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_MODEL));\
+ assert_non_null(OUT_MODEL)
+
+#define TEST_DIFF_3(XML1, XML2, XML3, DIFF1, DIFF2, MERGE) \
+ { \
+ struct lyd_node *data1;\
+ struct lyd_node *data2;\
+ struct lyd_node *data3;\
+ /*create*/\
+ CHECK_PARSE_LYD(XML1, data1);\
+ CHECK_PARSE_LYD(XML2, data2);\
+ CHECK_PARSE_LYD(XML3, data3);\
+ /* diff1 */ \
+ struct lyd_node *diff1;\
+ CHECK_PARSE_LYD_DIFF(data1, data2, 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_LYD_STRING(diff2, DIFF2); \
+ assert_int_equal(lyd_diff_apply_all(&data2, diff2), LY_SUCCESS);\
+ CHECK_LYD(data2, data3);\
+ /* merge */ \
+ assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS);\
+ CHECK_LYD_STRING(diff1, MERGE); \
+ /* cleanup */ \
+ lyd_free_all(data1);\
+ lyd_free_all(data2);\
+ lyd_free_all(data3);\
+ lyd_free_all(diff1);\
+ lyd_free_all(diff2);\
+ }
+
+const char *schema1 =
+ "module defaults {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:libyang:tests:defaults\";\n"
+ " prefix df;\n"
+ ""
+ " feature unhide;\n"
+ ""
+ " typedef defint32 {\n"
+ " type int32;\n"
+ " default \"42\";\n"
+ " }\n"
+ ""
+ " leaf hiddenleaf {\n"
+ " if-feature \"unhide\";\n"
+ " type int32;\n"
+ " default \"42\";\n"
+ " }\n"
+ ""
+ " container df {\n"
+ " leaf foo {\n"
+ " type defint32;\n"
+ " }\n"
+ ""
+ " 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"
+ " }\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";
+
+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;
+}
+
+static void
+test_invalid(void **state)
+{
+ (void) state;
+ const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\"><foo>42</foo></df>";
+
+ struct lyd_node *model_1;
+
+ CHECK_PARSE_LYD(xml, model_1);
+
+ struct lyd_node *diff = NULL;
+
+ assert_int_equal(lyd_diff_siblings(model_1, lyd_child(model_1), 0, &diff), LY_EINVAL);
+ assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, NULL), LY_EINVAL);
+
+ lyd_free_all(model_1);
+ lyd_free_all(diff);
+}
+
+static void
+test_same(void **state)
+{
+ (void) state;
+ const char *xml =
+ "<nacm xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-acm\">\n"
+ " <enable-nacm>true</enable-nacm>\n"
+ " <read-default>permit</read-default>\n"
+ " <write-default>deny</write-default>\n"
+ " <exec-default>permit</exec-default>\n"
+ " <enable-external-groups>true</enable-external-groups>\n"
+ "</nacm><df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo><b1_1>42</b1_1>\n"
+ "</df><hidden xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo><baz>42</baz></hidden>\n";
+
+ struct lyd_node *model_1;
+ struct lyd_node *model_2;
+
+ 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-netconf-acm", "2018-02-14", NULL));
+
+ CHECK_PARSE_LYD(xml, model_1);
+ CHECK_PARSE_LYD(xml, model_2);
+
+ struct lyd_node *diff = NULL;
+
+ assert_int_equal(lyd_diff_siblings(model_1, model_2, 0, &diff), LY_SUCCESS);
+ assert_null(diff);
+ assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS);
+ CHECK_LYD(model_1, model_2);
+
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+}
+
+static void
+test_empty1(void **state)
+{
+ (void) state;
+ const char *xml_in =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ " <b1_1>42</b1_1>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ " <baz>42</baz>\n"
+ "</hidden>\n";
+
+ struct lyd_node *model_1 = NULL;
+ struct lyd_node *model_2;
+
+ CHECK_PARSE_LYD(xml_in, model_2);
+
+ struct lyd_node *diff;
+
+ CHECK_PARSE_LYD_DIFF(model_1, model_2, diff);
+ CHECK_LYD_STRING(diff,
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n"
+ " <foo>42</foo>\n"
+ " <b1_1>42</b1_1>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n"
+ " <foo>42</foo>\n"
+ " <baz>42</baz>\n"
+ "</hidden>\n");
+ assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS);
+ CHECK_LYD(model_1, model_2);
+
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+}
+
+static void
+test_empty2(void **state)
+{
+ (void) state;
+ const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ " <b1_1>42</b1_1>\n"
+ "</df><hidden xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ " <baz>42</baz>\n"
+ "</hidden>\n";
+
+ struct lyd_node *model_1;
+
+ CHECK_PARSE_LYD(xml, model_1);
+
+ struct lyd_node *diff;
+
+ CHECK_PARSE_LYD_DIFF(model_1, NULL, diff);
+ CHECK_LYD_STRING(diff,
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n"
+ " <foo>42</foo>\n"
+ " <b1_1>42</b1_1>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n"
+ " <foo>42</foo>\n"
+ " <baz>42</baz>\n"
+ "</hidden>\n");
+
+ assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS);
+ assert_ptr_equal(model_1, NULL);
+
+ lyd_free_all(diff);
+ lyd_free_all(model_1);
+}
+
+static void
+test_empty_nested(void **state)
+{
+ (void) state;
+ const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\"><foo>42</foo></df>";
+
+ struct lyd_node *model_1;
+
+ CHECK_PARSE_LYD(xml, model_1);
+
+ struct lyd_node *diff = NULL;
+
+ assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, &diff), LY_SUCCESS);
+ assert_null(diff);
+
+ struct lyd_node *diff1;
+
+ CHECK_PARSE_LYD_DIFF(NULL, lyd_child(model_1), diff1);
+ CHECK_LYD_STRING(diff1,
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"create\">42</foo>\n"
+ "</df>\n");
+
+ struct lyd_node *diff2;
+
+ CHECK_PARSE_LYD_DIFF(lyd_child(model_1), NULL, diff2);
+ CHECK_LYD_STRING(diff2,
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"delete\">42</foo>\n"
+ "</df>\n");
+
+ lyd_free_all(model_1);
+ lyd_free_all(diff1);
+ lyd_free_all(diff2);
+}
+
+static void
+test_delete_merge(void **state)
+{
+ (void) state;
+ struct lyd_node *diff1, *diff2;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list>\n"
+ " <name>a</name>\n"
+ " <list2 yang:operation=\"delete\">\n"
+ " <name2>a</name2>\n"
+ " </list2>\n"
+ " </list>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>a</name>\n"
+ " </list>\n"
+ "</df>\n";
+ const char *xml_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>a</name>\n"
+ " <list2 yang:operation=\"delete\">\n"
+ " <name2>a</name2>\n"
+ " </list2>\n"
+ " </list>\n"
+ "</df>\n";
+
+ CHECK_PARSE_LYD(xml1, diff1);
+ CHECK_PARSE_LYD(xml2, diff2);
+
+ assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS);
+ CHECK_LYD_STRING(diff1, xml_merge);
+
+ lyd_free_all(diff1);
+ lyd_free_all(diff2);
+}
+
+static void
+test_leaf(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ " <baz>42</baz>\n"
+ "</hidden>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>41</foo>\n"
+ " <b1_1>42</b1_1>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>40</foo>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>40</foo>\n"
+ "</hidden>\n";
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"42\">41</foo>\n"
+ " <b1_1 yang:operation=\"create\">42</b1_1>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n"
+ " <foo>42</foo>\n"
+ " <baz>42</baz>\n"
+ "</hidden>\n";
+
+ const char *out_diff_2 = "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"41\">40</foo>\n"
+ " <b1_1 yang:operation=\"delete\">42</b1_1>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n"
+ " <foo>40</foo>\n"
+ "</hidden>\n";
+
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"42\">40</foo>\n"
+ "</df>\n"
+ "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"replace\" yang:orig-value=\"42\" yang:orig-default=\"false\">40</foo>\n"
+ " <baz yang:operation=\"delete\">42</baz>\n"
+ "</hidden>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_list(void **state)
+{
+ (void) state;
+ const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <list>\n"
+ " <name>a</name>\n"
+ " <value>1</value>\n"
+ " </list>\n"
+ " <list>\n"
+ " <name>b</name>\n"
+ " <value>2</value>\n"
+ " </list>\n"
+ "</df>\n";
+ const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <list>\n"
+ " <name>b</name>\n"
+ " <value>-2</value>\n"
+ " </list>\n"
+ " <list>\n"
+ " <name>c</name>\n"
+ " <value>3</value>\n"
+ " </list>\n"
+ "</df>\n";
+ const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <list>\n"
+ " <name>b</name>\n"
+ " <value>-2</value>\n"
+ " </list>\n"
+ " <list>\n"
+ " <name>a</name>\n"
+ " <value>2</value>\n"
+ " </list>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>a</name>\n"
+ " <value>1</value>\n"
+ " </list>\n"
+ " <list yang:operation=\"none\">\n"
+ " <name>b</name>\n"
+ " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\">-2</value>\n"
+ " </list>\n"
+ " <list yang:operation=\"create\">\n"
+ " <name>c</name>\n"
+ " <value>3</value>\n"
+ " </list>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>c</name>\n"
+ " <value>3</value>\n"
+ " </list>\n"
+ " <list yang:operation=\"create\">\n"
+ " <name>a</name>\n"
+ " <value>2</value>\n"
+ " </list>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list yang:operation=\"none\">\n"
+ " <name>a</name>\n"
+ " <value yang:operation=\"replace\" yang:orig-value=\"1\" yang:orig-default=\"false\">2</value>\n"
+ " </list>\n"
+ " <list yang:operation=\"none\">\n"
+ " <name>b</name>\n"
+ " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\">-2</value>\n"
+ " </list>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_userord_llist(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>1</llist>\n"
+ " <llist>2</llist>\n"
+ " <llist>3</llist>\n"
+ " <llist>4</llist>\n"
+ " <llist>5</llist>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>1</llist>\n"
+ " <llist>4</llist>\n"
+ " <llist>3</llist>\n"
+ " <llist>2</llist>\n"
+ " <llist>5</llist>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>5</llist>\n"
+ " <llist>4</llist>\n"
+ " <llist>3</llist>\n"
+ " <llist>2</llist>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"1\">4</llist>\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"4\">3</llist>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"delete\" yang:orig-value=\"\">1</llist>\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"\">5</llist>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"1\">4</llist>\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"4\">3</llist>\n"
+ " <llist yang:orig-value=\"\" yang:operation=\"delete\">1</llist>\n"
+ " <llist yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"\" yang:operation=\"replace\">5</llist>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_userord_llist2(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>1</llist>\n"
+ " <list><name>a</name><value>1</value></list>\n"
+ " <llist>2</llist>\n"
+ " <llist>3</llist>\n"
+ " <llist>4</llist>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>1</llist>\n"
+ " <list><name>a</name><value>1</value></list>\n"
+ " <llist>2</llist>\n"
+ " <llist>4</llist>\n"
+ " <llist>3</llist>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>4</llist>\n"
+ " <llist>1</llist>\n"
+ " <list><name>a</name><value>1</value></list>\n"
+ " <llist>3</llist>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"2\">4</llist>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">4</llist>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\">4</llist>\n"
+ " <llist yang:orig-value=\"1\" yang:operation=\"delete\">2</llist>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_userord_mix(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>1</llist>\n"
+ " <llist>2</llist>\n"
+ " <llist>3</llist>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>3</llist>\n"
+ " <llist>1</llist>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <llist>1</llist>\n"
+ " <llist>4</llist>\n"
+ " <llist>3</llist>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">3</llist>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\">1</llist>\n"
+ " <llist yang:operation=\"create\" yang:value=\"1\">4</llist>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n"
+ " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">3</llist>\n"
+ " <llist yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\" yang:operation=\"replace\">1</llist>\n"
+ " <llist yang:value=\"1\" yang:operation=\"create\">4</llist>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_userord_list(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>11</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>33</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\">11</l2>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"delete\" yang:orig-key=\"[l1='a']\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"delete\" yang:orig-key=\"\">\n"
+ " <l1>a</l1>\n"
+ " <l2>11</l2>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"none\">\n"
+ " <l1>c</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\">33</l2>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"create\" yang:key=\"[l1='c']\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"delete\">\n"
+ " <l1>a</l1>\n"
+ " <l2 yang:operation=\"delete\">1</l2>\n"
+ " </ul>\n"
+ " <ul yang:orig-key=\"[l1='a']\" yang:operation=\"replace\" yang:key=\"[l1='c']\">\n"
+ " <l1>b</l1>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"none\">\n"
+ " <l1>c</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\">33</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_userord_list2(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>4</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>4</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>4</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"create\" yang:key=\"\">\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"create\" yang:key=\"\">\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"create\" yang:key=\"[l1='a']\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"create\" yang:key=\"\">\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ " <ul yang:key=\"\" yang:operation=\"create\">\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul yang:key=\"[l1='a']\" yang:operation=\"create\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_keyless_list(void **state)
+{
+ (void) state;
+ const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <kl>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </kl>\n"
+ "</df>\n";
+ const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <kl>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ "</df>\n";
+ const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <kl>\n"
+ " <l1>c</l1>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l2>4</l2>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l1>e</l1>\n"
+ " <l2>5</l2>\n"
+ " </kl>\n"
+ " <kl>\n"
+ " <l1>f</l1>\n"
+ " <l2>6</l2>\n"
+ " </kl>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <kl yang:operation=\"delete\" yang:orig-position=\"2\">\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"replace\" yang:position=\"\" yang:orig-position=\"1\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"create\" yang:position=\"2\">\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"create\" yang:position=\"\">\n"
+ " <l1>c</l1>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"create\" yang:position=\"1\">\n"
+ " <l2>4</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"create\" yang:position=\"2\">\n"
+ " <l1>e</l1>\n"
+ " <l2>5</l2>\n"
+ " </kl>\n"
+ " <kl yang:operation=\"create\" yang:position=\"3\">\n"
+ " <l1>f</l1>\n"
+ " <l2>6</l2>\n"
+ " </kl>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <kl yang:operation=\"delete\" yang:orig-position=\"2\">\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </kl>\n"
+ " <kl yang:orig-position=\"1\" yang:operation=\"delete\">\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </kl>\n"
+ " <kl yang:orig-position=\"\" yang:operation=\"delete\">\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </kl>\n"
+ " <kl yang:position=\"\" yang:operation=\"create\">\n"
+ " <l1>c</l1>\n"
+ " </kl>\n"
+ " <kl yang:position=\"1\" yang:operation=\"create\">\n"
+ " <l2>4</l2>\n"
+ " </kl>\n"
+ " <kl yang:position=\"2\" yang:operation=\"create\">\n"
+ " <l1>e</l1>\n"
+ " <l2>5</l2>\n"
+ " </kl>\n"
+ " <kl yang:position=\"3\" yang:operation=\"create\">\n"
+ " <l1>f</l1>\n"
+ " <l2>6</l2>\n"
+ " </kl>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_state_llist(void **state)
+{
+ (void) state;
+ const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <kll>a</kll>\n"
+ " <kll>b</kll>\n"
+ " <kll>c</kll>\n"
+ "</df>\n";
+ const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <kll>b</kll>\n"
+ " <kll>c</kll>\n"
+ " <kll>a</kll>\n"
+ " <kll>a</kll>\n"
+ " <kll>a</kll>\n"
+ "</df>\n";
+ const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <kll>a</kll>\n"
+ " <kll>d</kll>\n"
+ " <kll>a</kll>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <kll yang:operation=\"replace\" yang:orig-default=\"false\" yang:position=\"\" yang:orig-position=\"1\">b</kll>\n"
+ " <kll yang:operation=\"replace\" yang:orig-default=\"false\" yang:position=\"1\" yang:orig-position=\"2\">c</kll>\n"
+ " <kll yang:operation=\"create\" yang:position=\"3\">a</kll>\n"
+ " <kll yang:operation=\"create\" yang:position=\"4\">a</kll>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <kll yang:operation=\"delete\" yang:orig-position=\"\">b</kll>\n"
+ " <kll yang:operation=\"delete\" yang:orig-position=\"\">c</kll>\n"
+ " <kll yang:operation=\"delete\" yang:orig-position=\"2\">a</kll>\n"
+ " <kll yang:operation=\"create\" yang:position=\"1\">d</kll>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <kll yang:orig-default=\"false\" yang:orig-position=\"1\" yang:operation=\"delete\">b</kll>\n"
+ " <kll yang:orig-default=\"false\" yang:orig-position=\"2\" yang:operation=\"delete\">c</kll>\n"
+ " <kll yang:operation=\"create\" yang:position=\"4\">a</kll>\n"
+ " <kll yang:position=\"1\" yang:operation=\"create\">d</kll>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
+test_wd(void **state)
+{
+ (void) state;
+ const struct lys_module *mod;
+ const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>41</foo>\n"
+ " <dllist>4</dllist>\n"
+ "</df>\n";
+ const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <foo>42</foo>\n"
+ " <dllist>4</dllist>\n"
+ " <dllist>1</dllist>\n"
+ "</df>\n";
+
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "defaults");
+ assert_non_null(mod);
+
+ struct lyd_node *model_1 = NULL;
+
+ assert_int_equal(lyd_validate_module(&model_1, mod, 0, NULL), LY_SUCCESS);
+ assert_ptr_not_equal(model_1, NULL);
+
+ struct lyd_node *model_2;
+ struct lyd_node *model_3;
+
+ CHECK_PARSE_LYD_PARAM(xml2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ CHECK_PARSE_LYD_PARAM(xml3, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_3);
+
+ /* diff1 */
+ struct lyd_node *diff1 = NULL;
+
+ assert_int_equal(lyd_diff_siblings(model_1, model_2, LYD_DIFF_DEFAULTS, &diff1), LY_SUCCESS);
+ assert_non_null(diff1);
+
+ const char *diff1_out_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"replace\" yang:orig-default=\"true\" yang:orig-value=\"42\">41</foo>\n"
+ " <dllist yang:operation=\"delete\">1</dllist>\n"
+ " <dllist yang:operation=\"delete\">2</dllist>\n"
+ " <dllist yang:operation=\"delete\">3</dllist>\n"
+ " <dllist yang:operation=\"create\">4</dllist>\n"
+ "</df>\n";
+
+ CHECK_LYD_STRING_PARAM(diff1, diff1_out_1, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
+ assert_int_equal(lyd_diff_apply_all(&model_1, diff1), LY_SUCCESS);
+ CHECK_LYD(model_1, model_2);
+
+ /* diff2 */
+ struct lyd_node *diff2;
+
+ assert_int_equal(lyd_diff_siblings(model_2, model_3, LYD_DIFF_DEFAULTS, &diff2), LY_SUCCESS);
+ assert_non_null(diff2);
+ CHECK_LYD_STRING(diff2,
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"41\">42</foo>\n"
+ " <dllist yang:operation=\"create\">1</dllist>\n"
+ "</df>\n");
+
+ assert_int_equal(lyd_diff_apply_all(&model_2, diff2), LY_SUCCESS);
+ CHECK_LYD(model_2, model_3);
+
+ /* merge */
+ assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS);
+
+ const char *diff1_out_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <foo yang:orig-default=\"true\" yang:operation=\"none\">42</foo>\n"
+ " <dllist yang:operation=\"none\" yang:orig-default=\"true\">1</dllist>\n"
+ " <dllist yang:operation=\"delete\">2</dllist>\n"
+ " <dllist yang:operation=\"delete\">3</dllist>\n"
+ " <dllist yang:operation=\"create\">4</dllist>\n"
+ "</df>\n";
+
+ CHECK_LYD_STRING_PARAM(diff1, diff1_out_2, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
+
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(model_3);
+ lyd_free_all(diff1);
+ lyd_free_all(diff2);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_invalid, setup),
+ UTEST(test_same, setup),
+ UTEST(test_empty1, setup),
+ UTEST(test_empty2, setup),
+ UTEST(test_empty_nested, setup),
+ UTEST(test_delete_merge, setup),
+ UTEST(test_leaf, setup),
+ UTEST(test_list, setup),
+ UTEST(test_userord_llist, setup),
+ UTEST(test_userord_llist2, setup),
+ UTEST(test_userord_mix, setup),
+ UTEST(test_userord_list, setup),
+ UTEST(test_userord_list2, setup),
+ UTEST(test_keyless_list, setup),
+ UTEST(test_state_llist, setup),
+ UTEST(test_wd, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_lyb.c b/tests/utests/data/test_lyb.c
new file mode 100644
index 0000000..26f3e73
--- /dev/null
+++ b/tests/utests/data/test_lyb.c
@@ -0,0 +1,2841 @@
+/**
+ * @file test_lyb.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Cmocka tests for LYB binary data format.
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "hash_table.h"
+#include "libyang.h"
+
+#define CHECK_PARSE_LYD(INPUT, OUT_NODE) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, OUT_NODE)
+
+#define CHECK_LYD_STRING(MODEL, TEXT) \
+ CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)
+
+static void
+check_print_parse(void **state, const char *data_xml)
+{
+ struct lyd_node *tree_1;
+ struct lyd_node *tree_2;
+ char *lyb_out;
+
+ CHECK_PARSE_LYD(data_xml, tree_1);
+ assert_int_equal(lyd_print_mem(&lyb_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0);
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, lyb_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT,
+ 0, &tree_2));
+ assert_non_null(tree_2);
+ CHECK_LYD(tree_1, tree_2);
+
+ free(lyb_out);
+ lyd_free_all(tree_1);
+ lyd_free_all(tree_2);
+}
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+
+ return 0;
+}
+
+static void
+tests_leaflist(void **state)
+{
+ const char *mod;
+ const char *data_xml;
+
+ mod =
+ "module mod { namespace \"urn:test-leaflist\"; prefix m;"
+ " container cont {"
+ " presence \"\";"
+ " leaf-list ll {"
+ " type uint8;"
+ " }"
+ " }"
+ "}";
+ UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-leaflist\">\n"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-leaflist\">\n"
+ " <ll>1</ll>\n"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-leaflist\">\n"
+ " <ll>1</ll>\n"
+ " <ll>2</ll>\n"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+}
+
+static void
+tests_list(void **state)
+{
+ const char *mod;
+ const char *data_xml;
+
+ mod =
+ "module mod { namespace \"urn:test-list\"; prefix m;"
+ " container cont {"
+ " presence \"\";"
+ " list lst {"
+ " key \"lf\";"
+ " leaf lf {"
+ " type uint8;"
+ " }"
+ " }"
+ " }"
+ "}";
+ UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-list\">\n"
+ " <lst>"
+ " <lf>1</lf>"
+ " </lst>"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-list\">\n"
+ " <lst>"
+ " <lf>1</lf>"
+ " <lf>2</lf>"
+ " </lst>"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+}
+
+static void
+tests_any(void **state)
+{
+ const char *mod;
+ const char *data_xml;
+
+ mod =
+ "module mod { namespace \"urn:test-any\"; prefix m;"
+ " container cont {"
+ " presence \"\";"
+ " anyxml anxml;\n"
+ " }"
+ "}";
+ UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-any\">\n"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-any\">\n"
+ " <anxml><node>value</node></anxml>\n"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+
+ data_xml =
+ "<cont xmlns=\"urn:test-any\">\n"
+ " <anxml><node1>value</node1></anxml>\n"
+ " <anxml><node2>value</node2></anxml>\n"
+ "</cont>\n";
+ check_print_parse(state, data_xml);
+}
+
+static void
+test_ietf_interfaces(void **state)
+{
+ const char *data_xml =
+ "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>eth0</name>\n"
+ " <description>Ethernet 0</description>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <enabled>true</enabled>\n"
+ " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n"
+ " <enabled>true</enabled>\n"
+ " <mtu>1500</mtu>\n"
+ " <address>\n"
+ " <ip>192.168.2.100</ip>\n"
+ " <prefix-length>24</prefix-length>\n"
+ " </address>\n"
+ " </ipv4>\n"
+ " </interface>\n"
+ " <interface>\n"
+ " <name>eth1</name>\n"
+ " <description>Ethernet 1</description>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <enabled>true</enabled>\n"
+ " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n"
+ " <enabled>true</enabled>\n"
+ " <mtu>1500</mtu>\n"
+ " <address>\n"
+ " <ip>10.10.1.5</ip>\n"
+ " <prefix-length>16</prefix-length>\n"
+ " </address>\n"
+ " </ipv4>\n"
+ " </interface>\n"
+ " <interface>\n"
+ " <name>gigaeth0</name>\n"
+ " <description>GigabitEthernet 0</description>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <enabled>false</enabled>\n"
+ " </interface>\n"
+ "</interfaces>\n";
+
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-ip", NULL, NULL));
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL));
+
+ check_print_parse(state, data_xml);
+}
+
+static void
+test_origin(void **state)
+{
+ const char *origin_yang =
+ "module test-origin {"
+ " namespace \"urn:test-origin\";"
+ " prefix to;"
+ " import ietf-origin {"
+ " prefix or;"
+ " }"
+ ""
+ " container cont {"
+ " leaf leaf1 {"
+ " type string;"
+ " }"
+ " leaf leaf2 {"
+ " type string;"
+ " }"
+ " leaf leaf3 {"
+ " type uint8;"
+ " }"
+ " }"
+ "}";
+ const char *data_xml =
+ "<cont xmlns=\"urn:test-origin\">\n"
+ " <leaf1 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:default\">value1</leaf1>\n"
+ " <leaf2>value2</leaf2>\n"
+ " <leaf3 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:system\">125</leaf3>\n"
+ "</cont>\n";
+
+ UTEST_ADD_MODULE(origin_yang, LYS_IN_YANG, NULL, NULL);
+ assert_int_equal(LY_SUCCESS, lys_set_implemented(ly_ctx_get_module_latest(UTEST_LYCTX, "ietf-origin"), NULL));
+
+ check_print_parse(state, data_xml);
+}
+
+static void
+test_statements(void **state)
+{
+ const char *links_yang =
+ "module links {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:module2\";\n"
+ " prefix mod2;\n"
+ "\n"
+ " identity just-another-identity;\n"
+ "\n"
+ " leaf one-leaf {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " list list-for-augment {\n"
+ " key keyleaf;\n"
+ "\n"
+ " leaf keyleaf {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf just-leaf {\n"
+ " type int32;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf rleaf {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf-list llist {\n"
+ " type string;\n"
+ " min-elements 0;\n"
+ " max-elements 100;\n"
+ " ordered-by user;\n"
+ " }\n"
+ "\n"
+ " grouping rgroup {\n"
+ " leaf rg1 {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf rg2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *statements_yang =
+ "module statements {\n"
+ " namespace \"urn:module\";\n"
+ " prefix mod;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " import links {\n"
+ " prefix mod2;\n"
+ " }\n"
+ "\n"
+ " identity random-identity {\n"
+ " base \"mod2:just-another-identity\";\n"
+ " base \"another-identity\";\n"
+ " }\n"
+ "\n"
+ " identity another-identity {\n"
+ " base \"mod2:just-another-identity\";\n"
+ " }\n"
+ "\n"
+ " typedef percent {\n"
+ " type uint8 {\n"
+ " range \"0 .. 100\";\n"
+ " }\n"
+ " units percent;\n"
+ " }\n"
+ "\n"
+ " container ice-cream-shop {\n"
+ " container employees {\n"
+ " list employee {\n"
+ " config true;\n"
+ " key id;\n"
+ " unique name;\n"
+ " min-elements 0;\n"
+ " max-elements 100;\n"
+ "\n"
+ " leaf id {\n"
+ " type uint64;\n"
+ " mandatory true;\n"
+ " }\n"
+ "\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf age {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " container random {\n"
+ " choice switch {\n"
+ " case a {\n"
+ " leaf aleaf {\n"
+ " type string;\n"
+ " default aaa;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " case c {\n"
+ " leaf cleaf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " anyxml xml-data;\n"
+ " anydata any-data;\n"
+ " leaf-list leaflist {\n"
+ " type string;\n"
+ " min-elements 0;\n"
+ " max-elements 20;\n"
+ " ordered-by system;\n"
+ " }\n"
+ "\n"
+ " grouping group {\n"
+ " leaf g1 {\n"
+ " mandatory false;\n"
+ " type percent;\n"
+ " }\n"
+ "\n"
+ " leaf g2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " uses group;\n"
+ " uses mod2:rgroup;\n"
+ "\n"
+ " leaf lref {\n"
+ " type leafref {\n"
+ " path \"/mod2:one-leaf\";\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf iref {\n"
+ " type identityref {\n"
+ " base \"mod2:just-another-identity\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " notification notif;\n"
+ "\n"
+ " augment \"/random\" {\n"
+ " leaf aug-leaf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *data_xml =
+ "<one-leaf xmlns=\"urn:module2\">reference leaf</one-leaf>\n"
+ "<ice-cream-shop xmlns=\"urn:module\">\n"
+ " <employees>\n"
+ " <employee>\n"
+ " <id>0</id>\n"
+ " <name>John Doe</name>\n"
+ " <age>28</age>\n"
+ " </employee>\n"
+ " <employee>\n"
+ " <id>1</id>\n"
+ " <name>Dohn Joe</name>\n"
+ " <age>20</age>\n"
+ " </employee>\n"
+ " </employees>\n"
+ "</ice-cream-shop>\n"
+ "<random xmlns=\"urn:module\">\n"
+ " <aleaf>string</aleaf>\n"
+ " <xml-data><anyxml>data</anyxml></xml-data>\n"
+ " <any-data><notif/></any-data>\n"
+ " <leaflist>l0</leaflist>\n"
+ " <leaflist>l1</leaflist>\n"
+ " <leaflist>l2</leaflist>\n"
+ " <g1>40</g1>\n"
+ " <g2>string</g2>\n"
+ " <aug-leaf>string</aug-leaf>\n"
+ " <rg1>string</rg1>\n"
+ " <rg2>string</rg2>\n"
+ " <lref>reference leaf</lref>\n"
+ " <iref>random-identity</iref>\n"
+ "</random>\n";
+
+ UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, NULL);
+
+ check_print_parse(state, data_xml);
+}
+
+static void
+test_opaq(void **state)
+{
+ const char *nc_feats[] = {"writable-running", NULL};
+ const char *data_xml =
+ "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config>\n"
+ " <top xmlns=\"urn:ed\">\n"
+ " <first>TestFirst</first>\n"
+ " </top>\n"
+ " </config>\n"
+ "</edit-config>\n";
+ struct ly_in *in;
+ struct lyd_node *tree_1;
+ struct lyd_node *tree_2;
+ char *xml_out; /* tree_2 */
+ LY_ERR rc;
+
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", NULL, nc_feats));
+
+ ly_in_new_memory(data_xml, &in);
+ rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree_1, NULL);
+ ly_in_free(in, 0);
+ assert_int_equal(rc, LY_SUCCESS);
+
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0);
+
+ ly_in_new_memory(xml_out, &in);
+ rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_LYB, LYD_TYPE_RPC_YANG, &tree_2, NULL);
+ ly_in_free(in, 0);
+ assert_int_equal(rc, LY_SUCCESS);
+
+ /* compare models */
+ CHECK_LYD(tree_1, tree_2);
+
+ /* clean */
+ free(xml_out);
+ lyd_free_all(tree_1);
+ lyd_free_all(tree_2);
+}
+
+static void
+test_collisions(void **state)
+{
+ char *counters_yang, *data_xml;
+
+ counters_yang = malloc(32768);
+ strcpy(counters_yang,
+ "module counters {\n"
+ " namespace \"urn:counters\";\n"
+ " prefix c;\n"
+ "\n"
+ " container stats {\n");
+ strcat(counters_yang,
+ " leaf counter1 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter2 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter3 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter4 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter5 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter6 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter7 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter8 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter9 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter10 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter11 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter12 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter13 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter14 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter15 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter16 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter17 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter18 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter19 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter20 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter21 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter22 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter23 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter24 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter25 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter26 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter27 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter28 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter29 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter30 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter31 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter32 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter33 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter34 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter35 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter36 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter37 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter38 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter39 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter40 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter41 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter42 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter43 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter44 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter45 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter46 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter47 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter48 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter49 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter50 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter51 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter52 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter53 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter54 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter55 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter56 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter57 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter58 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter59 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter60 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter61 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter62 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter63 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter64 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter65 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter66 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter67 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter68 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter69 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter70 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter71 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter72 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter73 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter74 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter75 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter76 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter77 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter78 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter79 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter80 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter81 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter82 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter83 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter84 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter85 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter86 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter87 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter88 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter89 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter90 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter91 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter92 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter93 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter94 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter95 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter96 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter97 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter98 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter99 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter100 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter101 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter102 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter103 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter104 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter105 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter106 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter107 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter108 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter109 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter110 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter111 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter112 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter113 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter114 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter115 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter116 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter117 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter118 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter119 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter120 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter121 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter122 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter123 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter124 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter125 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter126 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter127 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter128 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter129 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter130 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter131 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter132 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter133 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter134 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter135 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter136 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter137 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter138 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter139 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter140 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter141 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter142 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter143 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter144 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter145 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter146 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter147 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter148 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter149 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter150 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter151 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter152 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter153 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter154 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter155 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter156 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter157 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter158 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter159 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter160 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter161 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter162 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter163 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter164 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter165 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter166 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter167 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter168 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter169 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter170 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter171 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter172 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter173 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter174 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter175 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter176 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter177 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter178 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter179 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter180 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter181 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter182 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter183 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter184 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter185 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter186 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter187 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter188 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter189 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter190 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter191 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter192 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter193 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter194 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter195 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter196 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter197 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter198 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter199 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter200 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter201 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter202 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter203 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter204 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter205 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter206 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter207 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter208 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter209 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter210 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter211 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter212 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter213 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter214 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter215 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter216 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter217 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter218 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter219 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter220 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter221 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter222 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter223 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter224 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter225 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter226 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter227 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter228 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter229 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter230 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter231 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter232 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter233 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter234 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter235 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter236 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter237 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter238 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter239 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter240 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter241 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter242 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter243 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter244 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter245 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter246 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter247 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter248 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter249 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter250 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter251 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter252 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter253 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter254 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter255 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter256 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter257 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter258 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter259 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter260 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter261 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter262 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter263 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter264 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter265 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter266 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter267 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter268 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter269 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter270 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter271 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter272 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter273 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter274 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter275 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter276 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter277 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter278 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter279 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter280 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter281 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter282 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter283 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter284 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter285 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter286 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter287 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter288 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter289 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter290 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter291 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter292 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter293 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter294 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter295 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter296 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter297 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter298 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter299 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter300 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter301 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter302 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter303 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter304 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter305 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter306 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter307 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter308 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter309 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter310 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter311 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter312 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter313 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter314 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter315 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter316 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter317 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter318 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter319 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter320 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter321 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter322 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter323 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter324 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter325 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter326 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter327 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter328 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter329 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter330 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter331 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter332 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter333 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter334 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter335 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter336 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter337 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter338 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter339 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter340 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter341 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter342 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter343 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter344 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter345 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter346 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter347 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter348 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter349 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter350 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter351 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter352 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter353 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter354 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter355 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter356 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter357 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter358 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter359 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter360 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter361 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter362 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter363 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter364 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter365 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter366 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter367 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter368 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter369 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter370 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter371 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter372 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter373 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter374 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter375 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter376 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter377 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter378 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter379 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter380 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter381 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter382 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter383 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter384 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter385 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter386 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter387 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter388 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter389 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter390 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter391 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter392 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter393 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter394 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter395 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter396 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter397 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter398 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter399 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter400 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter401 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter402 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter403 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter404 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter405 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter406 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter407 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter408 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter409 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter410 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter411 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter412 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter413 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter414 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter415 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter416 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter417 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter418 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter419 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter420 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter421 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter422 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter423 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter424 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter425 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter426 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter427 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter428 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter429 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter430 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter431 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter432 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter433 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter434 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter435 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter436 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter437 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter438 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter439 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter440 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter441 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter442 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter443 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter444 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter445 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter446 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter447 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter448 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter449 {\n"
+ " type uint64;\n"
+ " }\n");
+ strcat(counters_yang,
+ " leaf counter450 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter451 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter452 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter453 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter454 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter455 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter456 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter457 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter458 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter459 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter460 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter461 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter462 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter463 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter464 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter465 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter466 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter467 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter468 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter469 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter470 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter471 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter472 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter473 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter474 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter475 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter476 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter477 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter478 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter479 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter480 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter481 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter482 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter483 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter484 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter485 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter486 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter487 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter488 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter489 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter490 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter491 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter492 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter493 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter494 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter495 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter496 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter497 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter498 {\n"
+ " type uint64;\n"
+ " }\n"
+ " leaf counter499 {\n"
+ " type uint64;\n"
+ " }\n"
+ " }\n"
+ "}\n");
+
+ data_xml = malloc(16384);
+ strcpy(data_xml,
+ "<stats xmlns=\"urn:counters\">\n");
+ strcat(data_xml,
+ " <counter1>1</counter1>\n"
+ " <counter2>2</counter2>\n"
+ " <counter3>3</counter3>\n"
+ " <counter4>4</counter4>\n"
+ " <counter5>5</counter5>\n"
+ " <counter6>6</counter6>\n"
+ " <counter7>7</counter7>\n"
+ " <counter8>8</counter8>\n"
+ " <counter9>9</counter9>\n"
+ " <counter10>10</counter10>\n"
+ " <counter11>11</counter11>\n"
+ " <counter12>12</counter12>\n"
+ " <counter13>13</counter13>\n"
+ " <counter14>14</counter14>\n"
+ " <counter15>15</counter15>\n"
+ " <counter16>16</counter16>\n"
+ " <counter17>17</counter17>\n"
+ " <counter18>18</counter18>\n"
+ " <counter19>19</counter19>\n"
+ " <counter20>20</counter20>\n"
+ " <counter21>21</counter21>\n"
+ " <counter22>22</counter22>\n"
+ " <counter23>23</counter23>\n"
+ " <counter24>24</counter24>\n"
+ " <counter25>25</counter25>\n"
+ " <counter26>26</counter26>\n"
+ " <counter27>27</counter27>\n"
+ " <counter28>28</counter28>\n"
+ " <counter29>29</counter29>\n"
+ " <counter30>30</counter30>\n"
+ " <counter31>31</counter31>\n"
+ " <counter32>32</counter32>\n"
+ " <counter33>33</counter33>\n"
+ " <counter34>34</counter34>\n"
+ " <counter35>35</counter35>\n"
+ " <counter36>36</counter36>\n"
+ " <counter37>37</counter37>\n"
+ " <counter38>38</counter38>\n"
+ " <counter39>39</counter39>\n"
+ " <counter40>40</counter40>\n"
+ " <counter41>41</counter41>\n"
+ " <counter42>42</counter42>\n"
+ " <counter43>43</counter43>\n"
+ " <counter44>44</counter44>\n"
+ " <counter45>45</counter45>\n"
+ " <counter46>46</counter46>\n"
+ " <counter47>47</counter47>\n"
+ " <counter48>48</counter48>\n"
+ " <counter49>49</counter49>\n"
+ " <counter50>50</counter50>\n"
+ " <counter51>51</counter51>\n"
+ " <counter52>52</counter52>\n"
+ " <counter53>53</counter53>\n"
+ " <counter54>54</counter54>\n"
+ " <counter55>55</counter55>\n"
+ " <counter56>56</counter56>\n"
+ " <counter57>57</counter57>\n"
+ " <counter58>58</counter58>\n"
+ " <counter59>59</counter59>\n"
+ " <counter60>60</counter60>\n"
+ " <counter61>61</counter61>\n"
+ " <counter62>62</counter62>\n"
+ " <counter63>63</counter63>\n"
+ " <counter64>64</counter64>\n"
+ " <counter65>65</counter65>\n"
+ " <counter66>66</counter66>\n"
+ " <counter67>67</counter67>\n"
+ " <counter68>68</counter68>\n"
+ " <counter69>69</counter69>\n"
+ " <counter70>70</counter70>\n"
+ " <counter71>71</counter71>\n"
+ " <counter72>72</counter72>\n"
+ " <counter73>73</counter73>\n"
+ " <counter74>74</counter74>\n"
+ " <counter75>75</counter75>\n"
+ " <counter76>76</counter76>\n"
+ " <counter77>77</counter77>\n"
+ " <counter78>78</counter78>\n"
+ " <counter79>79</counter79>\n"
+ " <counter80>80</counter80>\n"
+ " <counter81>81</counter81>\n"
+ " <counter82>82</counter82>\n"
+ " <counter83>83</counter83>\n"
+ " <counter84>84</counter84>\n"
+ " <counter85>85</counter85>\n"
+ " <counter86>86</counter86>\n"
+ " <counter87>87</counter87>\n"
+ " <counter88>88</counter88>\n"
+ " <counter89>89</counter89>\n"
+ " <counter90>90</counter90>\n"
+ " <counter91>91</counter91>\n"
+ " <counter92>92</counter92>\n"
+ " <counter93>93</counter93>\n"
+ " <counter94>94</counter94>\n"
+ " <counter95>95</counter95>\n"
+ " <counter96>96</counter96>\n"
+ " <counter97>97</counter97>\n"
+ " <counter98>98</counter98>\n"
+ " <counter99>99</counter99>\n");
+ strcat(data_xml,
+ " <counter100>100</counter100>\n"
+ " <counter101>101</counter101>\n"
+ " <counter102>102</counter102>\n"
+ " <counter103>103</counter103>\n"
+ " <counter104>104</counter104>\n"
+ " <counter105>105</counter105>\n"
+ " <counter106>106</counter106>\n"
+ " <counter107>107</counter107>\n"
+ " <counter108>108</counter108>\n"
+ " <counter109>109</counter109>\n"
+ " <counter110>110</counter110>\n"
+ " <counter111>111</counter111>\n"
+ " <counter112>112</counter112>\n"
+ " <counter113>113</counter113>\n"
+ " <counter114>114</counter114>\n"
+ " <counter115>115</counter115>\n"
+ " <counter116>116</counter116>\n"
+ " <counter117>117</counter117>\n"
+ " <counter118>118</counter118>\n"
+ " <counter119>119</counter119>\n"
+ " <counter120>120</counter120>\n"
+ " <counter121>121</counter121>\n"
+ " <counter122>122</counter122>\n"
+ " <counter123>123</counter123>\n"
+ " <counter124>124</counter124>\n"
+ " <counter125>125</counter125>\n"
+ " <counter126>126</counter126>\n"
+ " <counter127>127</counter127>\n"
+ " <counter128>128</counter128>\n"
+ " <counter129>129</counter129>\n"
+ " <counter130>130</counter130>\n"
+ " <counter131>131</counter131>\n"
+ " <counter132>132</counter132>\n"
+ " <counter133>133</counter133>\n"
+ " <counter134>134</counter134>\n"
+ " <counter135>135</counter135>\n"
+ " <counter136>136</counter136>\n"
+ " <counter137>137</counter137>\n"
+ " <counter138>138</counter138>\n"
+ " <counter139>139</counter139>\n"
+ " <counter140>140</counter140>\n"
+ " <counter141>141</counter141>\n"
+ " <counter142>142</counter142>\n"
+ " <counter143>143</counter143>\n"
+ " <counter144>144</counter144>\n"
+ " <counter145>145</counter145>\n"
+ " <counter146>146</counter146>\n"
+ " <counter147>147</counter147>\n"
+ " <counter148>148</counter148>\n"
+ " <counter149>149</counter149>\n"
+ " <counter150>150</counter150>\n"
+ " <counter151>151</counter151>\n"
+ " <counter152>152</counter152>\n"
+ " <counter153>153</counter153>\n"
+ " <counter154>154</counter154>\n"
+ " <counter155>155</counter155>\n"
+ " <counter156>156</counter156>\n"
+ " <counter157>157</counter157>\n"
+ " <counter158>158</counter158>\n"
+ " <counter159>159</counter159>\n"
+ " <counter160>160</counter160>\n"
+ " <counter161>161</counter161>\n"
+ " <counter162>162</counter162>\n"
+ " <counter163>163</counter163>\n"
+ " <counter164>164</counter164>\n"
+ " <counter165>165</counter165>\n"
+ " <counter166>166</counter166>\n"
+ " <counter167>167</counter167>\n"
+ " <counter168>168</counter168>\n"
+ " <counter169>169</counter169>\n"
+ " <counter170>170</counter170>\n"
+ " <counter171>171</counter171>\n"
+ " <counter172>172</counter172>\n"
+ " <counter173>173</counter173>\n"
+ " <counter174>174</counter174>\n"
+ " <counter175>175</counter175>\n"
+ " <counter176>176</counter176>\n"
+ " <counter177>177</counter177>\n"
+ " <counter178>178</counter178>\n"
+ " <counter179>179</counter179>\n"
+ " <counter180>180</counter180>\n"
+ " <counter181>181</counter181>\n"
+ " <counter182>182</counter182>\n"
+ " <counter183>183</counter183>\n"
+ " <counter184>184</counter184>\n"
+ " <counter185>185</counter185>\n"
+ " <counter186>186</counter186>\n"
+ " <counter187>187</counter187>\n"
+ " <counter188>188</counter188>\n"
+ " <counter189>189</counter189>\n"
+ " <counter190>190</counter190>\n"
+ " <counter191>191</counter191>\n"
+ " <counter192>192</counter192>\n"
+ " <counter193>193</counter193>\n"
+ " <counter194>194</counter194>\n"
+ " <counter195>195</counter195>\n"
+ " <counter196>196</counter196>\n"
+ " <counter197>197</counter197>\n"
+ " <counter198>198</counter198>\n"
+ " <counter199>199</counter199>\n");
+ strcat(data_xml,
+ " <counter200>200</counter200>\n"
+ " <counter201>201</counter201>\n"
+ " <counter202>202</counter202>\n"
+ " <counter203>203</counter203>\n"
+ " <counter204>204</counter204>\n"
+ " <counter205>205</counter205>\n"
+ " <counter206>206</counter206>\n"
+ " <counter207>207</counter207>\n"
+ " <counter208>208</counter208>\n"
+ " <counter209>209</counter209>\n"
+ " <counter210>210</counter210>\n"
+ " <counter211>211</counter211>\n"
+ " <counter212>212</counter212>\n"
+ " <counter213>213</counter213>\n"
+ " <counter214>214</counter214>\n"
+ " <counter215>215</counter215>\n"
+ " <counter216>216</counter216>\n"
+ " <counter217>217</counter217>\n"
+ " <counter218>218</counter218>\n"
+ " <counter219>219</counter219>\n"
+ " <counter220>220</counter220>\n"
+ " <counter221>221</counter221>\n"
+ " <counter222>222</counter222>\n"
+ " <counter223>223</counter223>\n"
+ " <counter224>224</counter224>\n"
+ " <counter225>225</counter225>\n"
+ " <counter226>226</counter226>\n"
+ " <counter227>227</counter227>\n"
+ " <counter228>228</counter228>\n"
+ " <counter229>229</counter229>\n"
+ " <counter230>230</counter230>\n"
+ " <counter231>231</counter231>\n"
+ " <counter232>232</counter232>\n"
+ " <counter233>233</counter233>\n"
+ " <counter234>234</counter234>\n"
+ " <counter235>235</counter235>\n"
+ " <counter236>236</counter236>\n"
+ " <counter237>237</counter237>\n"
+ " <counter238>238</counter238>\n"
+ " <counter239>239</counter239>\n"
+ " <counter240>240</counter240>\n"
+ " <counter241>241</counter241>\n"
+ " <counter242>242</counter242>\n"
+ " <counter243>243</counter243>\n"
+ " <counter244>244</counter244>\n"
+ " <counter245>245</counter245>\n"
+ " <counter246>246</counter246>\n"
+ " <counter247>247</counter247>\n"
+ " <counter248>248</counter248>\n"
+ " <counter249>249</counter249>\n"
+ " <counter250>250</counter250>\n"
+ " <counter251>251</counter251>\n"
+ " <counter252>252</counter252>\n"
+ " <counter253>253</counter253>\n"
+ " <counter254>254</counter254>\n"
+ " <counter255>255</counter255>\n"
+ " <counter256>256</counter256>\n"
+ " <counter257>257</counter257>\n"
+ " <counter258>258</counter258>\n"
+ " <counter259>259</counter259>\n"
+ " <counter260>260</counter260>\n"
+ " <counter261>261</counter261>\n"
+ " <counter262>262</counter262>\n"
+ " <counter263>263</counter263>\n"
+ " <counter264>264</counter264>\n"
+ " <counter265>265</counter265>\n"
+ " <counter266>266</counter266>\n"
+ " <counter267>267</counter267>\n"
+ " <counter268>268</counter268>\n"
+ " <counter269>269</counter269>\n"
+ " <counter270>270</counter270>\n"
+ " <counter271>271</counter271>\n"
+ " <counter272>272</counter272>\n"
+ " <counter273>273</counter273>\n"
+ " <counter274>274</counter274>\n"
+ " <counter275>275</counter275>\n"
+ " <counter276>276</counter276>\n"
+ " <counter277>277</counter277>\n"
+ " <counter278>278</counter278>\n"
+ " <counter279>279</counter279>\n"
+ " <counter280>280</counter280>\n"
+ " <counter281>281</counter281>\n"
+ " <counter282>282</counter282>\n"
+ " <counter283>283</counter283>\n"
+ " <counter284>284</counter284>\n"
+ " <counter285>285</counter285>\n"
+ " <counter286>286</counter286>\n"
+ " <counter287>287</counter287>\n"
+ " <counter288>288</counter288>\n"
+ " <counter289>289</counter289>\n"
+ " <counter290>290</counter290>\n"
+ " <counter291>291</counter291>\n"
+ " <counter292>292</counter292>\n"
+ " <counter293>293</counter293>\n"
+ " <counter294>294</counter294>\n"
+ " <counter295>295</counter295>\n"
+ " <counter296>296</counter296>\n"
+ " <counter297>297</counter297>\n"
+ " <counter298>298</counter298>\n"
+ " <counter299>299</counter299>\n");
+ strcat(data_xml,
+ " <counter300>300</counter300>\n"
+ " <counter301>301</counter301>\n"
+ " <counter302>302</counter302>\n"
+ " <counter303>303</counter303>\n"
+ " <counter304>304</counter304>\n"
+ " <counter305>305</counter305>\n"
+ " <counter306>306</counter306>\n"
+ " <counter307>307</counter307>\n"
+ " <counter308>308</counter308>\n"
+ " <counter309>309</counter309>\n"
+ " <counter310>310</counter310>\n"
+ " <counter311>311</counter311>\n"
+ " <counter312>312</counter312>\n"
+ " <counter313>313</counter313>\n"
+ " <counter314>314</counter314>\n"
+ " <counter315>315</counter315>\n"
+ " <counter316>316</counter316>\n"
+ " <counter317>317</counter317>\n"
+ " <counter318>318</counter318>\n"
+ " <counter319>319</counter319>\n"
+ " <counter320>320</counter320>\n"
+ " <counter321>321</counter321>\n"
+ " <counter322>322</counter322>\n"
+ " <counter323>323</counter323>\n"
+ " <counter324>324</counter324>\n"
+ " <counter325>325</counter325>\n"
+ " <counter326>326</counter326>\n"
+ " <counter327>327</counter327>\n"
+ " <counter328>328</counter328>\n"
+ " <counter329>329</counter329>\n"
+ " <counter330>330</counter330>\n"
+ " <counter331>331</counter331>\n"
+ " <counter332>332</counter332>\n"
+ " <counter333>333</counter333>\n"
+ " <counter334>334</counter334>\n"
+ " <counter335>335</counter335>\n"
+ " <counter336>336</counter336>\n"
+ " <counter337>337</counter337>\n"
+ " <counter338>338</counter338>\n"
+ " <counter339>339</counter339>\n"
+ " <counter340>340</counter340>\n"
+ " <counter341>341</counter341>\n"
+ " <counter342>342</counter342>\n"
+ " <counter343>343</counter343>\n"
+ " <counter344>344</counter344>\n"
+ " <counter345>345</counter345>\n"
+ " <counter346>346</counter346>\n"
+ " <counter347>347</counter347>\n"
+ " <counter348>348</counter348>\n"
+ " <counter349>349</counter349>\n"
+ " <counter350>350</counter350>\n"
+ " <counter351>351</counter351>\n"
+ " <counter352>352</counter352>\n"
+ " <counter353>353</counter353>\n"
+ " <counter354>354</counter354>\n"
+ " <counter355>355</counter355>\n"
+ " <counter356>356</counter356>\n"
+ " <counter357>357</counter357>\n"
+ " <counter358>358</counter358>\n"
+ " <counter359>359</counter359>\n"
+ " <counter360>360</counter360>\n"
+ " <counter361>361</counter361>\n"
+ " <counter362>362</counter362>\n"
+ " <counter363>363</counter363>\n"
+ " <counter364>364</counter364>\n"
+ " <counter365>365</counter365>\n"
+ " <counter366>366</counter366>\n"
+ " <counter367>367</counter367>\n"
+ " <counter368>368</counter368>\n"
+ " <counter369>369</counter369>\n"
+ " <counter370>370</counter370>\n"
+ " <counter371>371</counter371>\n"
+ " <counter372>372</counter372>\n"
+ " <counter373>373</counter373>\n"
+ " <counter374>374</counter374>\n"
+ " <counter375>375</counter375>\n"
+ " <counter376>376</counter376>\n"
+ " <counter377>377</counter377>\n"
+ " <counter378>378</counter378>\n"
+ " <counter379>379</counter379>\n"
+ " <counter380>380</counter380>\n"
+ " <counter381>381</counter381>\n"
+ " <counter382>382</counter382>\n"
+ " <counter383>383</counter383>\n"
+ " <counter384>384</counter384>\n"
+ " <counter385>385</counter385>\n"
+ " <counter386>386</counter386>\n"
+ " <counter387>387</counter387>\n"
+ " <counter388>388</counter388>\n"
+ " <counter389>389</counter389>\n"
+ " <counter390>390</counter390>\n"
+ " <counter391>391</counter391>\n"
+ " <counter392>392</counter392>\n"
+ " <counter393>393</counter393>\n"
+ " <counter394>394</counter394>\n"
+ " <counter395>395</counter395>\n"
+ " <counter396>396</counter396>\n"
+ " <counter397>397</counter397>\n"
+ " <counter398>398</counter398>\n"
+ " <counter399>399</counter399>\n");
+ strcat(data_xml,
+ " <counter400>400</counter400>\n"
+ " <counter401>401</counter401>\n"
+ " <counter402>402</counter402>\n"
+ " <counter403>403</counter403>\n"
+ " <counter404>404</counter404>\n"
+ " <counter405>405</counter405>\n"
+ " <counter406>406</counter406>\n"
+ " <counter407>407</counter407>\n"
+ " <counter408>408</counter408>\n"
+ " <counter409>409</counter409>\n"
+ " <counter410>410</counter410>\n"
+ " <counter411>411</counter411>\n"
+ " <counter412>412</counter412>\n"
+ " <counter413>413</counter413>\n"
+ " <counter414>414</counter414>\n"
+ " <counter415>415</counter415>\n"
+ " <counter416>416</counter416>\n"
+ " <counter417>417</counter417>\n"
+ " <counter418>418</counter418>\n"
+ " <counter419>419</counter419>\n"
+ " <counter420>420</counter420>\n"
+ " <counter421>421</counter421>\n"
+ " <counter422>422</counter422>\n"
+ " <counter423>423</counter423>\n"
+ " <counter424>424</counter424>\n"
+ " <counter425>425</counter425>\n"
+ " <counter426>426</counter426>\n"
+ " <counter427>427</counter427>\n"
+ " <counter428>428</counter428>\n"
+ " <counter429>429</counter429>\n"
+ " <counter430>430</counter430>\n"
+ " <counter431>431</counter431>\n"
+ " <counter432>432</counter432>\n"
+ " <counter433>433</counter433>\n"
+ " <counter434>434</counter434>\n"
+ " <counter435>435</counter435>\n"
+ " <counter436>436</counter436>\n"
+ " <counter437>437</counter437>\n"
+ " <counter438>438</counter438>\n"
+ " <counter439>439</counter439>\n"
+ " <counter440>440</counter440>\n"
+ " <counter441>441</counter441>\n"
+ " <counter442>442</counter442>\n"
+ " <counter443>443</counter443>\n"
+ " <counter444>444</counter444>\n"
+ " <counter445>445</counter445>\n"
+ " <counter446>446</counter446>\n"
+ " <counter447>447</counter447>\n"
+ " <counter448>448</counter448>\n"
+ " <counter449>449</counter449>\n"
+ " <counter450>450</counter450>\n"
+ " <counter451>451</counter451>\n"
+ " <counter452>452</counter452>\n"
+ " <counter453>453</counter453>\n"
+ " <counter454>454</counter454>\n"
+ " <counter455>455</counter455>\n"
+ " <counter456>456</counter456>\n"
+ " <counter457>457</counter457>\n"
+ " <counter458>458</counter458>\n"
+ " <counter459>459</counter459>\n"
+ " <counter460>460</counter460>\n"
+ " <counter461>461</counter461>\n"
+ " <counter462>462</counter462>\n"
+ " <counter463>463</counter463>\n"
+ " <counter464>464</counter464>\n"
+ " <counter465>465</counter465>\n"
+ " <counter466>466</counter466>\n"
+ " <counter467>467</counter467>\n"
+ " <counter468>468</counter468>\n"
+ " <counter469>469</counter469>\n"
+ " <counter470>470</counter470>\n"
+ " <counter471>471</counter471>\n"
+ " <counter472>472</counter472>\n"
+ " <counter473>473</counter473>\n"
+ " <counter474>474</counter474>\n"
+ " <counter475>475</counter475>\n"
+ " <counter476>476</counter476>\n"
+ " <counter477>477</counter477>\n"
+ " <counter478>478</counter478>\n"
+ " <counter479>479</counter479>\n"
+ " <counter480>480</counter480>\n"
+ " <counter481>481</counter481>\n"
+ " <counter482>482</counter482>\n"
+ " <counter483>483</counter483>\n"
+ " <counter484>484</counter484>\n"
+ " <counter485>485</counter485>\n"
+ " <counter486>486</counter486>\n"
+ " <counter487>487</counter487>\n"
+ " <counter488>488</counter488>\n"
+ " <counter489>489</counter489>\n"
+ " <counter490>490</counter490>\n"
+ " <counter491>491</counter491>\n"
+ " <counter492>492</counter492>\n"
+ " <counter493>493</counter493>\n"
+ " <counter494>494</counter494>\n"
+ " <counter495>495</counter495>\n"
+ " <counter496>496</counter496>\n"
+ " <counter497>497</counter497>\n"
+ " <counter498>498</counter498>\n"
+ " <counter499>499</counter499>\n"
+ "</stats>\n");
+
+ UTEST_ADD_MODULE(counters_yang, LYS_IN_YANG, NULL, NULL);
+
+ check_print_parse(state, data_xml);
+
+ free(counters_yang);
+ free(data_xml);
+}
+
+#if 0
+
+static void
+test_types(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "types", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/types.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_annotations(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/annotations.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_similar_annot_names(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/similar-annot-names.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_many_child_annot(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/many-childs-annot.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_union(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "union", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_union2(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "statements", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union2.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_collisions(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/collisions.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_anydata(void **state)
+{
+ struct state *st = (*state);
+ const struct lys_module *mod;
+ int ret;
+ const char *test_anydata =
+ "module test-anydata {"
+ " namespace \"urn:test-anydata\";"
+ " prefix ya;"
+ ""
+ " container cont {"
+ " anydata ntf;"
+ " }"
+ "}";
+
+ assert_non_null(ly_ctx_load_module(st->ctx, "ietf-netconf-notifications", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ / *get notification in LYB format to set as anydata content * /
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ lyd_free_withsiblings(st->dt1);
+ st->dt1 = NULL;
+
+ / *now comes the real test, test anydata * /
+ mod = lys_parse_mem(st->ctx, test_anydata, LYS_YANG);
+ assert_non_null(mod);
+
+ st->dt1 = lyd_new(NULL, mod, "cont");
+ assert_non_null(st->dt1);
+
+ assert_non_null(lyd_new_anydata(st->dt1, NULL, "ntf", st->mem, LYD_ANYDATA_LYBD));
+ st->mem = NULL;
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ ret = lyd_validate(&st->dt1, LYD_OPT_CONFIG, NULL);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+
+ /* and also test the embedded notification itself */
+ free(st->mem);
+ ret = lyd_lyb_data_length(((struct lyd_node_anydata *)st->dt1->child)->value.mem);
+ st->mem = malloc(ret);
+ memcpy(st->mem, ((struct lyd_node_anydata *)st->dt1->child)->value.mem, ret);
+
+ lyd_free_withsiblings(st->dt2);
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_NOTIF | LYD_OPT_STRICT | LYD_OPT_NOEXTDEPS, NULL);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ /* parse the JSON again for this comparison */
+ lyd_free_withsiblings(st->dt1);
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_submodule_feature(void **state)
+{
+ struct state *st = (*state);
+ const struct lys_module *mod;
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ mod = ly_ctx_load_module(st->ctx, "feature-submodule-main", NULL);
+ assert_non_null(mod);
+ assert_int_equal(lys_features_enable(mod, "test-submodule-feature"), 0);
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/test-submodule-feature.json", LYD_JSON, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_coliding_augments(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "augment-target", NULL));
+ assert_non_null(ly_ctx_load_module(st->ctx, "augment0", NULL));
+ assert_non_null(ly_ctx_load_module(st->ctx, "augment1", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/augment.xml", LYD_XML, LYD_OPT_CONFIG);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+static void
+test_leafrefs(void **state)
+{
+ struct state *st = (*state);
+ int ret;
+
+ ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
+ assert_non_null(ly_ctx_load_module(st->ctx, "leafrefs2", NULL));
+
+ st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/leafrefs2.json", LYD_JSON, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt1, NULL);
+
+ ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
+ assert_int_equal(ret, 0);
+
+ st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
+ assert_ptr_not_equal(st->dt2, NULL);
+
+ check_data_tree(st->dt1, st->dt2);
+}
+
+#endif
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(tests_leaflist),
+ UTEST(tests_list),
+ UTEST(tests_any),
+ UTEST(test_ietf_interfaces, setup),
+ UTEST(test_origin, setup),
+ UTEST(test_statements, setup),
+ UTEST(test_opaq, setup),
+ UTEST(test_collisions, setup),
+#if 0
+ cmocka_unit_test_setup_teardown(test_types, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_annotations, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_similar_annot_names, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_many_child_annot, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_union, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_union2, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_collisions, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_anydata, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_submodule_feature, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_coliding_augments, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_leafrefs, setup_f, teardown_f),
+#endif
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_merge.c b/tests/utests/data/test_merge.c
new file mode 100644
index 0000000..3e7b772
--- /dev/null
+++ b/tests/utests/data/test_merge.c
@@ -0,0 +1,756 @@
+/**
+ * @file test_merge.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief tests for complex data merges.
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+#define LYD_TREE_CREATE(INPUT, MODEL) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL)
+
+#define CONTEXT_CREATE \
+ CONTEXT_CREATE_PATH(NULL)
+
+#define LYD_TREE_CHECK_CHAR(MODEL, TEXT, PARAMS) \
+ CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | PARAMS)
+
+static void
+test_batch(void **state)
+{
+ const char *start =
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2016-02-11</revision>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n";
+ const char *data[] = {
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-yang-library</name>\n"
+ " <revision>2016-02-01</revision>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2012-02-22</revision>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf-monitoring</name>\n"
+ " <revision>2010-10-04</revision>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf-with-defaults</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2016-02-11</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-yang-library</name>\n"
+ " <revision>2016-02-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2012-02-22</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <feature>writable-running</feature>\n"
+ " <feature>candidate</feature>\n"
+ " <feature>rollback-on-error</feature>\n"
+ " <feature>validate</feature>\n"
+ " <feature>startup</feature>\n"
+ " <feature>xpath</feature>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf-monitoring</name>\n"
+ " <revision>2010-10-04</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n",
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>ietf-netconf-with-defaults</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n"
+ };
+ const char *output_template =
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2016-02-11</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-yang-library</name>\n"
+ " <revision>2016-02-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2012-02-22</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <feature>writable-running</feature>\n"
+ " <feature>candidate</feature>\n"
+ " <feature>rollback-on-error</feature>\n"
+ " <feature>validate</feature>\n"
+ " <feature>startup</feature>\n"
+ " <feature>xpath</feature>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-monitoring</name>\n"
+ " <revision>2010-10-04</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-with-defaults</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>\n";
+
+ struct lyd_node *target;
+
+ CHECK_PARSE_LYD_PARAM(start, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, target);
+
+ for (int32_t i = 0; i < 11; ++i) {
+ struct lyd_node *source;
+
+ CHECK_PARSE_LYD_PARAM(data[i], LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, source);
+ assert_int_equal(LY_SUCCESS, lyd_merge_siblings(&target, source, LYD_MERGE_DESTRUCT));
+ }
+
+ LYD_TREE_CHECK_CHAR(target, output_template, 0);
+
+ lyd_free_all(target);
+}
+
+static void
+test_leaf(void **state)
+{
+ const char *sch = "module x {"
+ " namespace urn:x;"
+ " prefix x;"
+ " container A {"
+ " leaf f1 {type string;}"
+ " container B {"
+ " leaf f2 {type string;}"
+ " }"
+ " }"
+ " }";
+ const char *trg = "<A xmlns=\"urn:x\"> <f1>block</f1> </A>";
+ const char *src = "<A xmlns=\"urn:x\"> <f1>aa</f1> <B> <f2>bb</f2> </B> </A>";
+ const char *result = "<A xmlns=\"urn:x\"><f1>aa</f1><B><f2>bb</f2></B></A>";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, LYD_PRINT_SHRINK);
+
+ lyd_free_all(target);
+ lyd_free_all(source);
+}
+
+static void
+test_container(void **state)
+{
+ const char *sch =
+ "module A {\n"
+ " namespace \"aa:A\";\n"
+ " prefix A;\n"
+ " container A {\n"
+ " leaf f1 {type string;}\n"
+ " container B {\n"
+ " leaf f2 {type string;}\n"
+ " }\n"
+ " container C {\n"
+ " leaf f3 {type string;}\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *trg = "<A xmlns=\"aa:A\"> <B> <f2>aaa</f2> </B> </A>";
+ const char *src = "<A xmlns=\"aa:A\"> <C> <f3>bbb</f3> </C> </A>";
+ const char *result = "<A xmlns=\"aa:A\"><B><f2>aaa</f2></B><C><f3>bbb</f3></C></A>";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, LYD_PRINT_SHRINK);
+
+ /* destroy */
+ lyd_free_all(source);
+ lyd_free_all(target);
+}
+
+static void
+test_list(void **state)
+{
+ const char *sch =
+ "module merge {\n"
+ " namespace \"http://test/merge\";\n"
+ " prefix merge;\n"
+ "\n"
+ " container inner1 {\n"
+ " list b-list1 {\n"
+ " key p1;\n"
+ " leaf p1 {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf p2 {\n"
+ " type string;\n"
+ " }\n"
+ " leaf p3 {\n"
+ " type boolean;\n"
+ " default false;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *trg =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>a</p2>\n"
+ " <p3>true</p3>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ const char *src =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ const char *result =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " <p3>true</p3>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, 0);
+
+ lyd_free_all(target);
+ lyd_free_all(source);
+}
+
+static void
+test_list2(void **state)
+{
+ const char *sch =
+ "module merge {\n"
+ " namespace \"http://test/merge\";\n"
+ " prefix merge;\n"
+ "\n"
+ " container inner1 {\n"
+ " list b-list1 {\n"
+ " key p1;\n"
+ " leaf p1 {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf p2 {\n"
+ " type string;\n"
+ " }\n"
+ " container inner2 {\n"
+ " leaf p3 {\n"
+ " type boolean;\n"
+ " default false;\n"
+ " }\n"
+ " leaf p4 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *trg =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>a</p2>\n"
+ " <inner2>\n"
+ " <p4>val</p4>\n"
+ " </inner2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ const char *src =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ const char *result =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " <inner2>\n"
+ " <p4>val</p4>\n"
+ " </inner2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, 0);
+
+ lyd_free_all(source);
+ lyd_free_all(target);
+}
+
+static void
+test_dup_inst_list(void **state)
+{
+ const char *sch =
+ "module merge {\n"
+ " namespace \"http://test/merge\";\n"
+ " prefix merge;\n"
+ "\n"
+ " container inner1 {\n"
+ " config false;\n"
+ " list b-list1 {\n"
+ " leaf p1 {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf p2 {\n"
+ " type string;\n"
+ " }\n"
+ " container inner2 {\n"
+ " leaf p4 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *trg =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " </b-list1>\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>a</p2>\n"
+ " <inner2>\n"
+ " <p4>val</p4>\n"
+ " </inner2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ const char *src =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " </b-list1>\n"
+ " <b-list1>\n"
+ " <p1>2</p1>\n"
+ " <p2>a</p2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ const char *result =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>b</p2>\n"
+ " </b-list1>\n"
+ " <b-list1>\n"
+ " <p1>1</p1>\n"
+ " <p2>a</p2>\n"
+ " <inner2>\n"
+ " <p4>val</p4>\n"
+ " </inner2>\n"
+ " </b-list1>\n"
+ " <b-list1>\n"
+ " <p1>2</p1>\n"
+ " <p2>a</p2>\n"
+ " </b-list1>\n"
+ "</inner1>\n";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, 0);
+
+ lyd_free_all(source);
+ lyd_free_all(target);
+}
+
+static void
+test_dup_inst_llist(void **state)
+{
+ const char *sch =
+ "module merge {\n"
+ " namespace \"http://test/merge\";\n"
+ " prefix merge;\n"
+ "\n"
+ " container inner1 {\n"
+ " config false;\n"
+ " leaf-list b-llist1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *trg =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>b</b-llist1>\n"
+ " <b-llist1>c</b-llist1>\n"
+ " <b-llist1>d</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>b</b-llist1>\n"
+ " <b-llist1>c</b-llist1>\n"
+ " <b-llist1>d</b-llist1>\n"
+ "</inner1>\n";
+ const char *src =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-llist1>d</b-llist1>\n"
+ " <b-llist1>c</b-llist1>\n"
+ " <b-llist1>b</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>f</b-llist1>\n"
+ " <b-llist1>f</b-llist1>\n"
+ "</inner1>\n";
+ const char *result =
+ "<inner1 xmlns=\"http://test/merge\">\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>b</b-llist1>\n"
+ " <b-llist1>c</b-llist1>\n"
+ " <b-llist1>d</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>b</b-llist1>\n"
+ " <b-llist1>c</b-llist1>\n"
+ " <b-llist1>d</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>a</b-llist1>\n"
+ " <b-llist1>f</b-llist1>\n"
+ " <b-llist1>f</b-llist1>\n"
+ "</inner1>\n";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, 0);
+
+ lyd_free_all(source);
+ lyd_free_all(target);
+}
+
+static void
+test_case(void **state)
+{
+ const char *sch =
+ "module merge {\n"
+ " namespace \"http://test/merge\";\n"
+ " prefix merge;\n"
+ " container cont {\n"
+ " choice ch {\n"
+ " container inner {\n"
+ " leaf p1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case c2 {\n"
+ " leaf p1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ const char *trg =
+ "<cont xmlns=\"http://test/merge\">\n"
+ " <inner>\n"
+ " <p1>1</p1>\n"
+ " </inner>\n"
+ "</cont>\n";
+ const char *src =
+ "<cont xmlns=\"http://test/merge\">\n"
+ " <p1>1</p1>\n"
+ "</cont>\n";
+ const char *result =
+ "<cont xmlns=\"http://test/merge\">\n"
+ " <p1>1</p1>\n"
+ "</cont>\n";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ /* merge them */
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ /* check the result */
+ LYD_TREE_CHECK_CHAR(target, result, 0);
+
+ lyd_free_all(source);
+ lyd_free_all(target);
+}
+
+static void
+test_dflt(void **state)
+{
+ const char *sch =
+ "module merge-dflt {\n"
+ " namespace \"urn:merge-dflt\";\n"
+ " prefix md;\n"
+ " container top {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " default \"c_dflt\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ struct lyd_node *target = NULL;
+ struct lyd_node *source = NULL;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/c", "c_dflt", 0, &target), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/a", "a_val", 0, &source), LY_SUCCESS);
+ assert_int_equal(lyd_new_path(source, UTEST_LYCTX, "/merge-dflt:top/b", "b_val", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&source, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ assert_int_equal(lyd_merge_siblings(&target, source, LYD_MERGE_DESTRUCT | LYD_MERGE_DEFAULTS), LY_SUCCESS);
+ source = NULL;
+
+ /* c should be replaced and now be default */
+ assert_string_equal(lyd_child(target)->prev->schema->name, "c");
+ assert_true(lyd_child(target)->prev->flags & LYD_DEFAULT);
+
+ lyd_free_all(target);
+ lyd_free_all(source);
+}
+
+static void
+test_dflt2(void **state)
+{
+ const char *sch =
+ "module merge-dflt {\n"
+ " namespace \"urn:merge-dflt\";\n"
+ " prefix md;\n"
+ " container top {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " default \"c_dflt\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ struct lyd_node *target;
+ struct lyd_node *source;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/c", "c_dflt", 0, &target), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/a", "a_val", 0, &source), LY_SUCCESS);
+ assert_int_equal(lyd_new_path(source, UTEST_LYCTX, "/merge-dflt:top/b", "b_val", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&source, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
+
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+
+ /* c should not be replaced, so c remains not default */
+ assert_false(lyd_child(target)->flags & LYD_DEFAULT);
+
+ lyd_free_all(target);
+ lyd_free_all(source);
+}
+
+static void
+test_leafrefs(void **state)
+{
+ const char *sch = "module x {"
+ " namespace urn:x;"
+ " prefix x;"
+ " list l {"
+ " key n;"
+ " leaf n { type string; }"
+ " leaf t { type string; }"
+ " leaf r { type leafref { path '/l/n'; } }}}";
+ const char *trg = "<l xmlns=\"urn:x\"><n>a</n></l>"
+ "<l xmlns=\"urn:x\"><n>b</n><r>a</r></l>";
+ const char *src = "<l xmlns=\"urn:x\"><n>c</n><r>a</r></l>"
+ "<l xmlns=\"urn:x\"><n>a</n><t>*</t></l>";
+ const char *res = "<l xmlns=\"urn:x\"><n>a</n><t>*</t></l>"
+ "<l xmlns=\"urn:x\"><n>b</n><r>a</r></l>"
+ "<l xmlns=\"urn:x\"><n>c</n><r>a</r></l>";
+ struct lyd_node *source, *target;
+
+ UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE(src, source);
+ LYD_TREE_CREATE(trg, target);
+
+ assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS);
+
+ LYD_TREE_CHECK_CHAR(target, res, LYD_PRINT_SHRINK);
+
+ lyd_free_all(source);
+ lyd_free_all(target);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_batch),
+ UTEST(test_leaf),
+ UTEST(test_container),
+ UTEST(test_list),
+ UTEST(test_list2),
+ UTEST(test_dup_inst_list),
+ UTEST(test_dup_inst_llist),
+ UTEST(test_case),
+ UTEST(test_dflt),
+ UTEST(test_dflt2),
+ UTEST(test_leafrefs),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c
new file mode 100644
index 0000000..7642960
--- /dev/null
+++ b/tests/utests/data/test_new.c
@@ -0,0 +1,446 @@
+/**
+ * @file test_new.c
+ * @author: Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for functions for creating data
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+/* common module for the tests */
+const char *schema_a = "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;yang-version 1.1;\n"
+ " list l1 {\n"
+ " key \"a b\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " list l11 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint32;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " leaf foo {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ " container c {\n"
+ " leaf-list x {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " anydata any {\n"
+ " config false;\n"
+ " }\n"
+ " leaf-list ll2 {\n"
+ " config false;\n"
+ " type string;\n"
+ " }\n"
+ " list l2 {\n"
+ " config false;\n"
+ " container c {\n"
+ " leaf x {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container c2 {\n"
+ " config false;\n"
+ " list l3 {\n"
+ " leaf x {\n"
+ " type string;\n"
+ " }\n"
+ " leaf y {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " rpc oper {\n"
+ " input {\n"
+ " leaf param {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf param {\n"
+ " type int8;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+static void
+test_top_level(void **state)
+{
+ struct lys_module *mod;
+ struct lyd_node *node, *rpc;
+
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod);
+
+ /* list */
+ assert_int_equal(lyd_new_list(NULL, mod, "l1", 0, &node, "val_a", "val_b"), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[]", 0, &node), LY_EVALID);
+ CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]\").", "Schema location \"/a:l1\".");
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[key1='a'][key2='b']", 0, &node), LY_ENOTFOUND);
+ CHECK_LOG_CTX("Not found node \"key1\" in path.", "Schema location \"/a:l1\".");
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID);
+ CHECK_LOG_CTX("Key expected instead of leaf \"c\" in path.", "Schema location \"/a:l1\".");
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "c", "[a='a'][b='b']", 0, &node), LY_ENOTFOUND);
+ CHECK_LOG_CTX("List node \"c\" not found.", NULL);
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a='a'][b='b']", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a=''][b='']", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a:a='a'][a:b='b']", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a= 'a']\n[b =\t'b']", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ /* leaf */
+ assert_int_equal(lyd_new_term(NULL, mod, "foo", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID);
+ CHECK_LOG_CTX("Invalid type uint16 value \"[a='a'][b='b'][c='c']\".", "Schema location \"/a:foo\".");
+
+ assert_int_equal(lyd_new_term(NULL, mod, "c", "value", 0, &node), LY_ENOTFOUND);
+ CHECK_LOG_CTX("Term node \"c\" not found.", NULL);
+
+ assert_int_equal(lyd_new_term(NULL, mod, "foo", "256", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ /* leaf-list */
+ assert_int_equal(lyd_new_term(NULL, mod, "ll", "ahoy", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ /* container */
+ assert_int_equal(lyd_new_inner(NULL, mod, "c", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_inner(NULL, mod, "l1", 0, &node), LY_ENOTFOUND);
+ CHECK_LOG_CTX("Inner node (container, notif, RPC, or action) \"l1\" not found.", NULL);
+
+ assert_int_equal(lyd_new_inner(NULL, mod, "l2", 0, &node), LY_ENOTFOUND);
+ CHECK_LOG_CTX("Inner node (container, notif, RPC, or action) \"l2\" not found.", NULL);
+
+ /* anydata */
+ assert_int_equal(lyd_new_any(NULL, mod, "any", "{\"node\":\"val\"}", 0, LYD_ANYDATA_STRING, 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+ assert_int_equal(lyd_new_any(NULL, mod, "any", "<node>val</node>", 0, LYD_ANYDATA_STRING, 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ /* key-less list */
+ assert_int_equal(lyd_new_list2(NULL, mod, "l2", "[a='a'][b='b']", 0, &node), LY_EVALID);
+ CHECK_LOG_CTX("List predicate defined for keyless list \"l2\" in path.", "Schema location \"/a:l2\".");
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l2", "", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_list2(NULL, mod, "l2", NULL, 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ assert_int_equal(lyd_new_list(NULL, mod, "l2", 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ /* RPC */
+ assert_int_equal(lyd_new_inner(NULL, mod, "oper", 0, &rpc), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(rpc, mod, "param", "22", 0, &node), LY_SUCCESS);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_node_leaf *)node->schema)->type->basetype);
+ assert_int_equal(lyd_new_term(rpc, mod, "param", "22", 1, &node), LY_SUCCESS);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_node_leaf *)node->schema)->type->basetype);
+ lyd_free_tree(rpc);
+}
+
+static void
+test_opaq(void **state)
+{
+ struct lyd_node *root, *node;
+ struct lyd_node_opaq *opq;
+
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(lyd_new_opaq(NULL, UTEST_LYCTX, "node1", NULL, NULL, "my-module", &root), LY_SUCCESS);
+ assert_null(root->schema);
+ opq = (struct lyd_node_opaq *)root;
+ assert_string_equal(opq->name.name, "node1");
+ assert_string_equal(opq->name.module_name, "my-module");
+ assert_string_equal(opq->value, "");
+
+ assert_int_equal(lyd_new_opaq(root, NULL, "node2", "value", NULL, "my-module2", &node), LY_SUCCESS);
+ assert_null(node->schema);
+ opq = (struct lyd_node_opaq *)node;
+ assert_string_equal(opq->name.name, "node2");
+ assert_string_equal(opq->name.module_name, "my-module2");
+ assert_string_equal(opq->value, "value");
+ assert_ptr_equal(opq->parent, root);
+
+ lyd_free_tree(root);
+}
+
+static void
+test_path(void **state)
+{
+ LY_ERR ret;
+ struct lyd_node *root, *node, *parent;
+ struct lys_module *mod;
+ char *str;
+
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod);
+
+ /* create 2 nodes */
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:c/x[.='val']", "vvv", 0, 0, 0, &root, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(root->schema->name, "c");
+ assert_non_null(node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val", lyd_get_value(node));
+
+ /* append another */
+ ret = lyd_new_path2(root, NULL, "/a:c/x", "val2", 0, 0, 0, &parent, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_ptr_equal(parent, node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val2", lyd_get_value(node));
+
+ /* and a last one */
+ ret = lyd_new_path2(root, NULL, "x", "val3", 0, 0, 0, &parent, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_ptr_equal(parent, node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val3", lyd_get_value(node));
+
+ lyd_free_tree(root);
+
+ /* try LYD_NEWOPT_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\".", "Schema location \"/a:l1\".");
+
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:l1", NULL, 0, 0, LYD_NEW_PATH_OPAQ, NULL, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_null(root->schema);
+
+ lyd_free_tree(root);
+
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, 0, NULL, NULL);
+ assert_int_equal(ret, LY_EVALID);
+ CHECK_LOG_CTX("Invalid type uint16 empty value.", "Schema location \"/a:foo\".");
+
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, LYD_NEW_PATH_OPAQ, NULL, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_null(root->schema);
+
+ lyd_free_tree(root);
+
+ ret = lyd_new_path(NULL, UTEST_LYCTX, "/a:l11", NULL, LYD_NEW_PATH_OPAQ, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_null(root->schema);
+
+ ret = lyd_new_path(root, NULL, "a", NULL, LYD_NEW_PATH_OPAQ, NULL);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(lyd_child(root));
+ assert_null(lyd_child(root)->schema);
+
+ ret = lyd_new_path(root, NULL, "b", NULL, LYD_NEW_PATH_OPAQ, NULL);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(lyd_child(root)->next);
+ assert_null(lyd_child(root)->next->schema);
+
+ lyd_free_tree(root);
+
+ /* key-less list */
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:c2/l3/x", "val1", 0, 0, 0, &root, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val1", lyd_get_value(node));
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3[1]", NULL, 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_EEXIST);
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3[2]/x", "val2", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3/x", "val3", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3[4]/x", "empty", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3[4]/x", "val4", 0, 0, LYD_NEW_PATH_UPDATE, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3[5]/x", "val5", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ ret = lyd_new_path2(root, NULL, "/a:c2/l3[6]/x", "val6", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "<c2 xmlns=\"urn:tests:a\">\n"
+ " <l3>\n"
+ " <x>val1</x>\n"
+ " </l3>\n"
+ " <l3>\n"
+ " <x>val2</x>\n"
+ " </l3>\n"
+ " <l3>\n"
+ " <x>val3</x>\n"
+ " </l3>\n"
+ " <l3>\n"
+ " <x>val4</x>\n"
+ " </l3>\n"
+ " <l3>\n"
+ " <x>val5</x>\n"
+ " </l3>\n"
+ " <l3>\n"
+ " <x>val6</x>\n"
+ " </l3>\n"
+ "</c2>\n");
+ free(str);
+ lyd_free_siblings(root);
+
+ /* state leaf-list */
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:ll2", "val_first", 0, 0, 0, &root, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(node->schema->name, "ll2");
+ assert_string_equal("val_first", lyd_get_value(node));
+
+ ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_EEXIST);
+
+ ret = lyd_new_path2(root, NULL, "/a:ll2[2]", "val2", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+
+ ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "val", 0, 0, LYD_NEW_PATH_UPDATE, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ ret = lyd_new_path2(root, UTEST_LYCTX, "/a:ll2", "val3", 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+
+ ret = lyd_new_path2(root, NULL, "/a:ll2[3][.='val3']", NULL, 0, 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_EVALID);
+
+ lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "<ll2 xmlns=\"urn:tests:a\">val</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:a\">val2</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:a\">val3</ll2>\n");
+ free(str);
+ lyd_free_siblings(root);
+
+ /* anydata */
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", "<elem>val</elem>", 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,
+ "<any xmlns=\"urn:tests:a\">\n"
+ " <elem>val</elem>\n"
+ "</any>\n");
+ free(str);
+ lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "{\n"
+ " \"a:any\": {\n"
+ " \"elem\": \"val\"\n"
+ " }\n"
+ "}\n");
+ free(str);
+ lyd_free_siblings(root);
+}
+
+static void
+test_path_ext(void **state)
+{
+ LY_ERR ret;
+ struct lyd_node *root, *node;
+ struct lys_module *mod;
+ const char *mod_str = "module ext {yang-version 1.1; namespace urn:tests:extensions:ext; prefix e;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template {container c {leaf x {type string;} leaf y {type string;} leaf z {type string;}}}}";
+
+ 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(mod_str, LYS_IN_YANG, NULL, &mod);
+
+ /* create x */
+ ret = lyd_new_ext_path(NULL, &mod->compiled->exts[0], "/ext:c/x", "xxx", 0, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(root->schema->name, "c");
+ assert_non_null(node = lyd_child(root));
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("xxx", lyd_get_value(node));
+
+ /* append y */
+ ret = lyd_new_ext_path(root, &mod->compiled->exts[0], "/ext:c/y", "yyy", 0, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_string_equal(node->schema->name, "y");
+ assert_string_equal("yyy", lyd_get_value(node));
+
+ /* append z */
+ ret = lyd_new_path(root, NULL, "ext:z", "zzz", 0, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_string_equal(node->schema->name, "z");
+ assert_string_equal("zzz", lyd_get_value(node));
+
+ lyd_free_tree(root);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_top_level),
+ UTEST(test_opaq),
+ UTEST(test_path),
+ UTEST(test_path_ext),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
new file mode 100644
index 0000000..d341e31
--- /dev/null
+++ b/tests/utests/data/test_parser_json.c
@@ -0,0 +1,793 @@
+/*
+ * @file test_parser_json.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from parser_xml.c
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "context.h"
+#include "in.h"
+#include "out.h"
+#include "parser_data.h"
+#include "printer_data.h"
+#include "tests_config.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+
+static int
+setup(void **state)
+{
+ const char *schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1; import ietf-yang-metadata {prefix md;}"
+ "md:annotation hint { type int8;}"
+ "list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;}"
+ " leaf d {type string;}"
+ " container cont {leaf e {type boolean;}}"
+ "}"
+ "leaf foo { type string;}"
+ "container c {"
+ " leaf x {type string;}"
+ " action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }"
+ " notification n1 { leaf nl {type string;} }"
+ "}"
+ "container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}"
+ "anydata any {config false;}"
+ "anyxml axml;"
+ "leaf-list ll1 { type uint8; }"
+ "leaf foo2 { type string; default \"default-val\"; }"
+ "leaf foo3 { type uint32; }"
+ "notification n2;}";
+
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+
+ return 0;
+}
+
+#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_JSON, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE)
+
+#define PARSER_CHECK_ERROR(INPUT, PARSE_OPTION, VALIDATE_OPTION, MODEL, RET_VAL, ERR_MESSAGE, ERR_PATH) \
+ assert_int_equal(RET_VAL, lyd_parse_data_mem(UTEST_LYCTX, INPUT, LYD_JSON, PARSE_OPTION, VALIDATE_OPTION, &MODEL));\
+ CHECK_LOG_CTX(ERR_MESSAGE, ERR_PATH);\
+ assert_null(MODEL)
+
+#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \
+ CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_JSON, PRINT_OPTION)
+
+static void
+test_leaf(void **state)
+{
+ struct lyd_node *tree;
+ struct lyd_node_term *leaf;
+ const char *data;
+
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL));
+
+ data = "{\"a:foo\":\"foo value\"}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "foo value");
+
+ CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2",
+ 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree->next->next;
+
+ CHECK_LYD_VALUE(leaf->value, STRING, "default-val");
+ assert_true(leaf->flags & LYD_DEFAULT);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* make foo2 explicit */
+ data = "{\"a:foo2\":\"default-val\"}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2",
+ 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "default-val");
+ assert_false(leaf->flags & LYD_DEFAULT);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* parse foo2 but make it implicit */
+ data = "{\"a:foo2\":\"default-val\",\"@a:foo2\":{\"ietf-netconf-with-defaults:default\":true}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2",
+ 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "default-val");
+ assert_true(leaf->flags & LYD_DEFAULT);
+
+ /* print default values */
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG, data);
+ lyd_free_all(tree);
+
+ /* skip leaf */
+ data = "{\"a:cp\":{\"x\":\"val\",\"y\":\"valy\",\"z\":5}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:cp\":{\"y\":\"valy\",\"z\":5}}");
+ lyd_free_all(tree);
+
+ /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */
+ data = "{\"@a:foo\":{\"a:hint\":1,\"a:hint\":2,\"x:xxx\":{\"value\":\"/x:no/x:yes\"}},\"a:foo\":\"xxx\"}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0);
+ CHECK_LYD_META(tree->meta, 1, "hint", 1, 1, INT8, "1", 1);
+ CHECK_LYD_META(tree->meta->next, 1, "hint", 0, 1, INT8, "2", 2);
+ assert_null(tree->meta->next->next);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS,
+ "{\"a:foo\":\"xxx\",\"@a:foo\":{\"a:hint\":1,\"a:hint\":2}}");
+ lyd_free_all(tree);
+
+ PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Unknown (or not implemented) YANG module \"x\" of metadata \"x:xxx\".", "Data location \"/@a:foo\", line number 1.");
+
+ /* missing referenced metadata node */
+ PARSER_CHECK_ERROR("{\"@a:foo\" : { \"a:hint\" : 1 }}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Missing JSON data instance to be coupled with @a:foo metadata.", "Data location \"/@a:foo\", line number 1.");
+
+ /* missing namespace for meatadata*/
+ PARSER_CHECK_ERROR("{\"a:foo\" : \"value\", \"@a:foo\" : { \"hint\" : 1 }}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Metadata in JSON must be namespace-qualified, missing prefix for \"hint\".",
+ "Schema location \"/a:foo\", line number 1.");
+
+ /* invalid JSON type */
+ data = "{\"a:l1\" : [{ \"a\" : \"val-a\", \"b\" : \"val-b\", \"c\" : 1, \"cont\" : { \"e\" : \"0\" } }]}";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid non-boolean-encoded boolean value \"0\".",
+ "Data location \"/a:l1[a='val-a'][b='val-b'][c='1']/cont/e\", line number 1.");
+
+ /* reverse solidus in JSON object member name */
+ data = "{\"@a:foo\":{\"a:hi\\nt\":1},\"a:foo\":\"xxx\"}";
+ assert_int_equal(LY_EINVAL, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+}
+
+static void
+test_leaflist(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct lyd_node_term *ll;
+
+ data = "{\"a:ll1\":[10,11]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(ll->value, UINT8, "10", 10);
+
+ assert_non_null(tree->next);
+ CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree->next;
+ CHECK_LYD_VALUE(ll->value, UINT8, "11", 11);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* accept empty */
+ data = "{\"a:ll1\":[]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_null(tree);
+
+ /* simple metadata */
+ data = "{\"a:ll1\":[10,11],\"@a:ll1\":[null,{\"a:hint\":2}]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(ll->value, UINT8, "10", 10);
+ assert_null(ll->meta);
+
+ assert_non_null(tree->next);
+ CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree->next;
+ CHECK_LYD_VALUE(ll->value, UINT8, "11", 11);
+ CHECK_LYD_META(ll->meta, 1, "hint", 0, 1, INT8, "2", 2);
+ assert_null(ll->meta->next);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */
+ data = "{\"@a:ll1\" : [{\"a:hint\" : 1, \"x:xxx\" : { \"value\" : \"/x:no/x:yes\" }, "
+ "\"a:hint\" : 10},null,{\"a:hint\" : 3}], \"a:ll1\" : [1,2,3]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(ll->value, UINT8, "1", 1);
+ CHECK_LYD_META(ll->meta, 1, "hint", 1, 1, INT8, "1", 1);
+ CHECK_LYD_META(ll->meta->next, 1, "hint", 0, 1, INT8, "10", 10);
+
+ assert_non_null(tree->next);
+ CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree->next;
+ CHECK_LYD_VALUE(ll->value, UINT8, "2", 2);
+ assert_null(ll->meta);
+
+ assert_non_null(tree->next->next);
+ CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1",
+ 1, LYS_LEAFLIST, 0, 0, NULL, 0);
+ ll = (struct lyd_node_term *)tree->next->next;
+ CHECK_LYD_VALUE(ll->value, UINT8, "3", 3);
+ CHECK_LYD_META(ll->meta, 1, "hint", 0, 1, INT8, "3", 3);
+ assert_null(ll->meta->next);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS,
+ "{\"a:ll1\":[1,2,3],\"@a:ll1\":[{\"a:hint\":1,\"a:hint\":10},null,{\"a:hint\":3}]}");
+ lyd_free_all(tree);
+
+ /* missing referenced metadata node */
+ PARSER_CHECK_ERROR("{\"@a:ll1\":[{\"a:hint\":1}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Missing JSON data instance to be coupled with @a:ll1 metadata.", "Data location \"/@a:ll1\", line number 1.");
+
+ PARSER_CHECK_ERROR("{\"a:ll1\":[1],\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Missing JSON data instance #2 of a:ll1 to be coupled with metadata.", "Schema location \"/a:ll1\", line number 1.");
+
+ PARSER_CHECK_ERROR("{\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2},{\"a:hint\":3}],\"a:ll1\" : [1, 2]}", 0, LYD_VALIDATE_PRESENT,
+ tree, LY_EVALID, "Missing JSON data instance #3 to be coupled with @a:ll1 metadata.",
+ "Data location \"/@a:ll1\", line number 1.");
+}
+
+static void
+test_anydata(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ data = "{\"a:any\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any",
+ 1, LYS_ANYDATA, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:any\":{}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any",
+ 1, LYS_ANYDATA, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:any\":{\"node\":20}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any",
+ 1, LYS_ANYDATA, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+}
+
+static void
+test_anyxml(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ data = "{\"a:axml\":\"some-value in string\"}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":\"\"}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":55}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":false}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":null}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":[null,true,false]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":[null,true,{\"name\":[25,40, false]}]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* same as anydata tests */
+ data = "{\"a:axml\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:axml\":{}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+}
+
+static void
+test_list(void **state)
+{
+ const char *data;
+ struct lyd_node *tree, *iter;
+ struct lyd_node_inner *list;
+ struct lyd_node_term *leaf;
+
+ /* check hashes */
+ data = "{\"a:l1\":[{\"a\":\"one\",\"b\":\"one\",\"c\":1}]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1",
+ 1, LYS_LIST, 0, 0, NULL, 0);
+ list = (struct lyd_node_inner *)tree;
+ LY_LIST_FOR(list->child, iter) {
+ assert_int_not_equal(0, iter->hash);
+ }
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* accept empty */
+ data = "{\"a:l1\":[]}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_null(tree);
+
+ /* missing keys */
+ PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\"}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"a\".",
+ "Data location \"/a:l1[b='b'][c='1']\", line number 1.");
+
+ PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"a\" : \"a\"}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"b\".", "Data location \"/a:l1[a='a']\", line number 1.");
+
+ PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"b\" : \"b\", \"a\" : \"a\"}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"c\".", "Data location \"/a:l1[a='a'][b='b']\", line number 1.");
+
+ /* key duplicate */
+ PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\", \"a\" : \"a\", \"c\" : 1}]}", 0, LYD_VALIDATE_PRESENT,
+ tree, LY_EVALID, "Duplicate instance of \"c\".",
+ "Data location \"/a:l1[a='a'][b='b'][c='1'][c='1']/c\", line number 1.");
+
+ /* keys order, in contrast to XML, JSON accepts keys in any order even in strict mode */
+ CHECK_PARSE_LYD("{ \"a:l1\": [ {\"d\" : \"d\", \"a\" : \"a\", \"c\" : 1, \"b\" : \"b\"}]}", 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1",
+ 1, LYS_LIST, 0, 0, NULL, 0);
+ list = (struct lyd_node_inner *)tree;
+ assert_non_null(leaf = (struct lyd_node_term *)list->child);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "d", 1, LYS_LEAF, 1, 0, NULL, 0);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS,
+ "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1,\"d\":\"d\"}]}");
+ lyd_free_all(tree);
+
+ /* */
+ CHECK_PARSE_LYD("{\"a:l1\":[{\"c\":1,\"b\":\"b\",\"a\":\"a\"}]}", LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1",
+ 1, LYS_LIST, 0, 0, NULL, 0);
+ list = (struct lyd_node_inner *)tree;
+ assert_non_null(leaf = (struct lyd_node_term *)list->child);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a",
+ 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b",
+ 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c",
+ 1, LYS_LEAF, 1, 0, NULL, 0);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS,
+ "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1}]}");
+ lyd_free_all(tree);
+
+ data = "{\"a:cp\":{\"@\":{\"a:hint\":1}}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ CHECK_LYD_META(tree->meta, 1, "hint", 0, 1, INT8, "1", 1);
+ assert_null(tree->meta->next);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+}
+
+static void
+test_container(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct lyd_node_inner *cont;
+
+ CHECK_PARSE_LYD("{\"a:c\":{}}", 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ cont = (struct lyd_node_inner *)tree;
+ assert_true(cont->flags & LYD_DEFAULT);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{}");
+ lyd_free_all(tree);
+
+ data = "{\"a:cp\":{}}";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ cont = (struct lyd_node_inner *)tree;
+ assert_false(cont->flags & LYD_DEFAULT);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* skip container */
+ CHECK_PARSE_LYD("{\"a:unknown\":{\"a\":\"val\",\"b\":5}}", 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{}");
+ lyd_free_all(tree);
+}
+
+static void
+test_opaq(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ /* invalid value, no flags */
+ data = "{\"a:foo3\":[null]}";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid non-number-encoded uint32 value \"\".", "Schema location \"/a:foo3\", line number 1.");
+
+ /* opaq flag */
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo3", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* missing key, no flags */
+ data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"d\":\"val_d\"}]}";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"c\".",
+ "Data location \"/a:l1[a='val_a'][b='val_b']\", line number 1.");
+
+ /* opaq flag */
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* invalid key, no flags */
+ data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid non-number-encoded int16 value \"val_c\".",
+ "Data location \"/a:l1[a='val_a'][b='val_b']/c\", line number 1.");
+
+ /* opaq flag */
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":{\"val\":\"val_c\"}}]}";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\"}]}";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* invalid metadata */
+ data = "{\"@a:foo\":\"str\",\"@a:foo3\":1,\"a:foo3\":2}";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Unknown module of node \"@a:foo\".", "Data location \"/@a:foo\".");
+
+ /* empty name */
+ PARSER_CHECK_ERROR("{\"@a:foo\":{\"\":0}}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "A JSON object member name cannot be a zero-length string.", "Line number 1.");
+
+ /* opaque data tree format print */
+ data =
+ "{\n"
+ " \"ietf-netconf-nmda:get-data\": {\n"
+ " \"data\": {\n"
+ " \"ietf-keystore:keystore\": {\n"
+ " \"asymmetric-keys\": {\n"
+ " \"asymmetric-key\": [\n"
+ " {\n"
+ " \"name\": \"genkey\",\n"
+ " \"algorithm\": \"rsa2048\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"ietf-netconf-server:netconf-server\": {\n"
+ " \"listen\": {\n"
+ " \"idle-timeout\": 3600,\n"
+ " \"endpoint\": [\n"
+ " {\n"
+ " \"name\": \"default-ssh\",\n"
+ " \"ssh\": {\n"
+ " \"tcp-server-parameters\": {\n"
+ " \"local-address\": \"0.0.0.0\",\n"
+ " \"local-port\": 830\n"
+ " },\n"
+ " \"ssh-server-parameters\": {\n"
+ " \"server-identity\": {\n"
+ " \"host-key\": [\n"
+ " {\n"
+ " \"name\": \"default-key\",\n"
+ " \"public-key\": {\n"
+ " \"keystore-reference\": \"genkey\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"client-authentication\": {\n"
+ " \"supported-authentication-methods\": {\n"
+ " \"publickey\": [null],\n"
+ " \"passsword\": [null],\n"
+ " \"other\": [\n"
+ " \"interactive\",\n"
+ " \"gssapi\"\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+}
+
+static void
+test_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+ const struct lyd_node *node;
+ const char *dsc = "Edit data in an NMDA datastore.\n"
+ "\n"
+ "If an error condition occurs such that an error severity\n"
+ "<rpc-error> element is generated, the server will stop\n"
+ "processing the <edit-data> operation and restore the\n"
+ "specified configuration to its complete state at\n"
+ "the start of this <edit-data> operation.";
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+ data = "{\"ietf-netconf-nmda:edit-data\":{"
+ "\"datastore\":\"ietf-datastores:running\","
+ "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+ "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+ "}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "edit-data", LYS_RPC,
+ 0, 0, 0, 0, 0, NULL, 0);
+
+ node = tree;
+ CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "edit-data", LYS_RPC,
+ 0, 0, 0, 0, 0, NULL, 0);
+ node = lyd_child(node)->next;
+ CHECK_LYSC_NODE(node->schema, "Inline config content.", 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config",
+ 0, LYS_ANYDATA, 1, 0, NULL, 0);
+
+ node = ((struct lyd_node_any *)node)->value.tree;
+ CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ node = lyd_child(node);
+ /* z has no value */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_JSON, "z", 0, 0, NULL, 0, "");
+ node = node->parent->next;
+ /* l1 key c has invalid value so it is at the end */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_action(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+
+ data = "{\"a:c\":{\"act\":{\"al\":\"value\"}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_notification(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *ntf;
+
+ data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_YANG, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ assert_non_null(ntf);
+ CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0);
+
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:n2\":{}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_YANG, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ assert_non_null(ntf);
+ CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 0, NULL, 0, 0x4, 1, 0, "n2", 0, 0, NULL, 0);
+
+ assert_non_null(tree);
+ assert_ptr_equal(ntf, tree);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_reply(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+ const struct lyd_node *node;
+
+ data = "{\"a:c\":{\"act\":{\"al\":25}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_REPLY_YANG, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+ node = lyd_child(op);
+ CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_STATUS_CURR | LYS_IS_OUTPUT, 1, "al", 0, LYS_LEAF, 1, 0, NULL, 0);
+
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ /* TODO print only rpc-reply node and then output subtree */
+ CHECK_LYD_STRING(lyd_child(op), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:al\":25}");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:c\":{\"act\":{\"al\":25}}}");
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_leaf, setup),
+ UTEST(test_leaflist, setup),
+ UTEST(test_anydata, setup),
+ UTEST(test_anyxml, setup),
+ UTEST(test_list, setup),
+ UTEST(test_container, setup),
+ UTEST(test_opaq, setup),
+ UTEST(test_rpc, setup),
+ UTEST(test_action, setup),
+ UTEST(test_notification, setup),
+ UTEST(test_reply, 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
new file mode 100644
index 0000000..7defd9c
--- /dev/null
+++ b/tests/utests/data/test_parser_xml.c
@@ -0,0 +1,836 @@
+/**
+ * @file test_parser_xml.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for functions from parser_xml.c
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "context.h"
+#include "in.h"
+#include "out.h"
+#include "parser_data.h"
+#include "printer_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+
+static int
+setup(void **state)
+{
+ const char *schema =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ " list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;}"
+ " leaf d {type string;}"
+ " container cont {leaf e {type boolean;}}"
+ " }"
+ " leaf foo { type string;}\n"
+ " container c {\n"
+ " leaf x {type string;}\n"
+ " action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }\n"
+ " notification n1 { leaf nl {type string;}}}\n"
+ " container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}\n"
+ " anydata any {config false;}\n"
+ " leaf foo2 { type string; default \"default-val\"; }\n"
+ " leaf foo3 { type uint32; }\n"
+ " notification n2;}";
+
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+
+ return 0;
+}
+
+#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE)
+
+#define PARSER_CHECK_ERROR(INPUT, PARSE_OPTION, VALIDATE_OPTION, MODEL, RET_VAL, ERR_MESSAGE, ERR_PATH) \
+ assert_int_equal(RET_VAL, lyd_parse_data_mem(UTEST_LYCTX, INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, &MODEL));\
+ CHECK_LOG_CTX(ERR_MESSAGE, ERR_PATH);\
+ assert_null(MODEL)
+
+#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \
+ CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, PRINT_OPTION)
+
+static void
+test_leaf(void **state)
+{
+ const char *data = "<foo xmlns=\"urn:tests:a\">foo value</foo>";
+ struct lyd_node *tree;
+ struct lyd_node_term *leaf;
+
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL));
+
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "foo value");
+
+ CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2",
+ 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree->next->next;
+ CHECK_LYD_VALUE(leaf->value, STRING, "default-val");
+ assert_true(leaf->flags & LYD_DEFAULT);
+ lyd_free_all(tree);
+
+ /* make foo2 explicit */
+ data = "<foo2 xmlns=\"urn:tests:a\">default-val</foo2>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2",
+ 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "default-val");
+ assert_false(leaf->flags & LYD_DEFAULT);
+ lyd_free_all(tree);
+
+ /* parse foo2 but make it implicit, skip metadata xxx from missing schema */
+ data = "<foo2 xmlns=\"urn:tests:a\" xmlns:wd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" "
+ "wd:default=\"true\" xmlns:x=\"urn:x\" x:xxx=\"false\">default-val</foo2>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2",
+ 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "default-val");
+ assert_true(leaf->flags & LYD_DEFAULT);
+ lyd_free_all(tree);
+
+ /* invalid value */
+ data = "<l1 xmlns=\"urn:tests:a\"><a>val-a</a><b>val-b</b><c>1</c><cont><e>0</e></cont></l1>";
+ PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid boolean value \"0\".",
+ "Data location \"/a:l1[a='val-a'][b='val-b'][c='1']/cont/e\", line number 1.");
+}
+
+static void
+test_anydata(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ data = "<any xmlns=\"urn:tests:a\">\n"
+ " <element1>\n"
+ " <x:element2 x:attr2=\"test\" xmlns:a=\"urn:tests:a\" xmlns:x=\"urn:x\">a:data</x:element2>\n"
+ " </element1>\n"
+ " <element1a/>\n"
+ "</any>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_SET_CONFIG, 1, "any",
+ 1, LYS_ANYDATA, 0, 0, NULL, 0);
+
+ const char *data_expected =
+ "<any xmlns=\"urn:tests:a\">\n"
+ " <element1>\n"
+ " <element2 xmlns=\"urn:x\" xmlns:x=\"urn:x\" x:attr2=\"test\" xmlns:a=\"urn:tests:a\">a:data</element2>\n"
+ " </element1>\n"
+ " <element1a/>\n"
+ "</any>\n";
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected);
+ lyd_free_all(tree);
+}
+
+static void
+test_list(void **state)
+{
+ const char *data;
+ struct lyd_node *tree, *iter;
+ struct lyd_node_inner *list;
+ struct lyd_node_term *leaf;
+
+ /* check hashes */
+ data = "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b><c>1</c></l1>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1",
+ 1, LYS_LIST, 0, 0, NULL, 0);
+ list = (struct lyd_node_inner *)tree;
+ LY_LIST_FOR(list->child, iter) {
+ assert_int_not_equal(0, iter->hash);
+ }
+ lyd_free_all(tree);
+
+ /* missing keys */
+ PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"a\".", "Data location \"/a:l1[b='b'][c='1']\", line number 1.");
+
+ PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"b\".", "Data location \"/a:l1[a='a']\", line number 1.");
+
+ PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><b>b</b><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"c\".", "Data location \"/a:l1[a='a'][b='b']\", line number 1.");
+
+ /* key duplicate */
+ PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a><c>1</c></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Duplicate instance of \"c\".",
+ "Data location \"/a:l1[a='a'][b='b'][c='1'][c='1']/c\", line number 1.");
+
+ /* keys order */
+ CHECK_PARSE_LYD("<l1 xmlns=\"urn:tests:a\"><d>d</d><a>a</a><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1",
+ 1, LYS_LIST, 0, 0, NULL, 0);
+ list = (struct lyd_node_inner *)tree;
+ assert_non_null(leaf = (struct lyd_node_term *)list->child);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "d", 1, LYS_LEAF, 1, 0, NULL, 0);
+ CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL);
+ lyd_free_all(tree);
+
+ data = "<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a></l1>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", 1, LYS_LIST, 0, 0, NULL, 0);
+ list = (struct lyd_node_inner *)tree;
+ assert_non_null(leaf = (struct lyd_node_term *)list->child);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
+ CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0);
+ CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL);
+ lyd_free_all(tree);
+
+ PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid position of the key \"b\" in a list.", "Data location \"/a:l1[c='1']/b\", line number 1.");
+}
+
+static void
+test_container(void **state)
+{
+ struct lyd_node *tree;
+ struct lyd_node_inner *cont;
+
+ CHECK_PARSE_LYD("<c xmlns=\"urn:tests:a\"/>", 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ cont = (struct lyd_node_inner *)tree;
+ assert_true(cont->flags & LYD_DEFAULT);
+ lyd_free_all(tree);
+
+ CHECK_PARSE_LYD("<cp xmlns=\"urn:tests:a\"/>", 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ cont = (struct lyd_node_inner *)tree;
+ assert_false(cont->flags & LYD_DEFAULT);
+ lyd_free_all(tree);
+}
+
+static void
+test_opaq(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ /* invalid value, no flags */
+ data = "<foo3 xmlns=\"urn:tests:a\"/>";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid type uint32 empty value.", "Schema location \"/a:foo3\", line number 1.");
+
+ /* opaq flag */
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "foo3", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<foo3 xmlns=\"urn:tests:a\"/>\n");
+ lyd_free_all(tree);
+
+ /* list, opaq flag */
+ data = "<l1 xmlns=\"urn:tests:a\"/>";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<l1 xmlns=\"urn:tests:a\"/>\n");
+ lyd_free_all(tree);
+
+ /* missing key, no flags */
+ data = "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <d>val_d</d>\n"
+ "</l1>\n";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "List instance is missing its key \"c\".",
+ "Data location \"/a:l1[a='val_a'][b='val_b']\", line number 5.");
+
+ /* opaq flag */
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* invalid key, no flags */
+ data = "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ "</l1>\n";
+ PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Invalid type int16 value \"val_c\".",
+ "Data location \"/a:l1[a='val_a'][b='val_b']/c\", line number 4.");
+
+ /* opaq flag */
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* opaq flag and fail */
+ assert_int_equal(LY_EVALID, lyd_parse_data_mem(UTEST_LYCTX,
+ "<a xmlns=\"ns\">\n"
+ " <b>x</b>\n"
+ " <c xmld:id=\"D\">1</c>\n"
+ "</a>\n",
+ LYD_XML, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Line number 3.");
+}
+
+static void
+test_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+ const struct lyd_node *node;
+ const char *dsc = "The <edit-config> operation loads all or part of a specified\n"
+ "configuration to the specified target configuration.";
+ const char *ref = "RFC 6241, Section 7.2";
+ const char *feats[] = {"writable-running", NULL};
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats)));
+
+ data = "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ " </l1>\n"
+ " <cp xmlns=\"urn:tests:a\">\n"
+ " <z nc:operation=\"delete\"/>\n"
+ " </cp>\n"
+ " </config>\n"
+ "</edit-config>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "edit-config", LYS_RPC,
+ 0, 0, 0, 0, 0, ref, 0);
+
+ assert_non_null(tree);
+
+ node = tree;
+ CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "edit-config", LYS_RPC,
+ 0, 0, 0, 0, 0, ref, 0);
+ node = lyd_child(node)->next;
+ dsc = "Inline Config content.";
+ CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0);
+
+ node = ((struct lyd_node_any *)node)->value.tree;
+ CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ node = lyd_child(node);
+ /* z has no value */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 0, "");
+ node = node->parent->next;
+ /* l1 key c has invalid value so it is at the end */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config>\n"
+ " <cp xmlns=\"urn:tests:a\">\n"
+ " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n"
+ " </cp>\n"
+ " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ " </l1>\n"
+ " </config>\n"
+ "</edit-config>\n");
+
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_action(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+
+ data = "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>value</al>\n"
+ " </act>\n"
+ "</c>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>value</al>\n"
+ " </act>\n"
+ "</c>\n");
+
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_notification(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *ntf;
+
+ data = "<c xmlns=\"urn:tests:a\">\n"
+ " <n1>\n"
+ " <nl>value</nl>\n"
+ " </n1>\n"
+ "</c>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_YANG, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ assert_non_null(ntf);
+ CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0);
+
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* top-level notif without envelope */
+ data = "<n2 xmlns=\"urn:tests:a\"/>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_YANG, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ assert_non_null(ntf);
+ CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 0, NULL, 0, 0x4, 1, 0, "n2", 0, 0, NULL, 0);
+
+ assert_non_null(tree);
+ assert_ptr_equal(ntf, tree);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_reply(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+ const struct lyd_node *node;
+
+ data = "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>25</al>\n"
+ " </act>\n"
+ "</c>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+ node = lyd_child(op);
+ CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_STATUS_CURR | LYS_IS_OUTPUT, 1, "al", 0, LYS_LEAF, 1, 0, NULL, 0);
+
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ /* TODO print only rpc-reply node and then output subtree */
+ CHECK_LYD_STRING(lyd_child(op), LYD_PRINT_WITHSIBLINGS, "<al xmlns=\"urn:tests:a\">25</al>\n");
+ lyd_free_all(tree);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_netconf_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+ const struct lyd_node *node;
+ const char *dsc = "The <edit-config> operation loads all or part of a specified\n"
+ "configuration to the specified target configuration.";
+ const char *ref = "RFC 6241, Section 7.2";
+ const char *feats[] = {"writable-running", NULL};
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats)));
+
+ data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ "<edit-config>\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ " </l1>\n"
+ " <cp xmlns=\"urn:tests:a\">\n"
+ " <z nc:operation=\"delete\"/>\n"
+ " </cp>\n"
+ " </config>\n"
+ "</edit-config>\n"
+ "</rpc>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+
+ node = tree;
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 1, 0, LY_VALUE_XML, "rpc", 0, 0, 0, 0, "");
+
+ assert_non_null(tree);
+
+ node = op;
+ CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "edit-config", LYS_RPC,
+ 0, 0, 0, 0, 0, ref, 0);
+ node = lyd_child(node)->next;
+ dsc = "Inline Config content.";
+ CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0);
+
+ node = ((struct lyd_node_any *)node)->value.tree;
+ CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ node = lyd_child(node);
+ /* z has no value */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 0, "");
+ node = node->parent->next;
+ /* l1 key c has invalid value so it is at the end */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\"/>\n");
+ CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS,
+ "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config>\n"
+ " <cp xmlns=\"urn:tests:a\">\n"
+ " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n"
+ " </cp>\n"
+ " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ " </l1>\n"
+ " </config>\n"
+ "</edit-config>\n");
+
+ lyd_free_all(tree);
+ lyd_free_all(op);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_netconf_action(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+
+ data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>value</al>\n"
+ " </act>\n"
+ "</c>\n"
+ "</action>\n"
+ "</rpc>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc", 0, 0, 0, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "action", 0, 0, 0, 0, "");
+
+ assert_non_null(op);
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\">\n"
+ " <action xmlns=\"urn:ietf:params:xml:ns:yang:1\"/>\n"
+ "</rpc>\n");
+ CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS,
+ "<act xmlns=\"urn:tests:a\">\n"
+ " <al>value</al>\n"
+ "</act>\n");
+
+ lyd_free_all(tree);
+ lyd_free_all(op);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_netconf_reply_or_notification(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *action, *tree, *op, *op2;
+
+ /* parse the action */
+ data = "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>value</al>\n"
+ " </act>\n"
+ "</c>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &action, &op));
+ ly_in_free(in, 0);
+
+ /* parse notification first */
+ data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ "<eventTime>2010-12-06T08:00:01Z</eventTime>\n"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <n1>\n"
+ " <nl>value</nl>\n"
+ " </n1>\n"
+ "</c>\n"
+ "</notification>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_NETCONF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 1, LY_VALUE_XML, "notification", 0, 0, 0, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "eventTime", 0, 0, 0, 0,
+ "2010-12-06T08:00:01Z");
+
+ assert_non_null(op2);
+ CHECK_LYSC_NOTIF((struct lysc_node_notif *)op2->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ " <eventTime>2010-12-06T08:00:01Z</eventTime>\n"
+ "</notification>\n");
+ CHECK_LYD_STRING(op2, LYD_PRINT_WITHSIBLINGS,
+ "<n1 xmlns=\"urn:tests:a\">\n"
+ " <nl>value</nl>\n"
+ "</n1>\n");
+
+ lyd_free_all(tree);
+ lyd_free_all(op2);
+
+ /* parse a data reply */
+ data = "<rpc-reply message-id=\"55\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <al xmlns=\"urn:tests:a\">25</al>\n"
+ "</rpc-reply>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 0, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, "");
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\"/>\n");
+
+ lyd_free_all(tree);
+ /* it was connected to the action, do not free */
+
+ /* parse an ok reply */
+ data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n"
+ " <ok/>\n"
+ "</rpc-reply>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL));
+ 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, 0, LY_VALUE_XML, "ok", 0, 0, 0, 0, "");
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+
+ lyd_free_all(tree);
+
+ /* parse an error reply */
+ data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n"
+ " <rpc-error>\n"
+ " <error-type>rpc</error-type>\n"
+ " <error-tag>missing-attribute</error-tag>\n"
+ " <error-severity>error</error-severity>\n"
+ " <error-info>\n"
+ " <bad-attribute>message-id</bad-attribute>\n"
+ " <bad-element>rpc</bad-element>\n"
+ " </error-info>\n"
+ " </rpc-error>\n"
+ "</rpc-reply>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL));
+ 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);
+
+ lyd_free_all(action);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_filter_attributes(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree;
+ const struct lyd_node *node;
+ const char *dsc;
+ const char *ref = "RFC 6241, Section 7.7";
+ const char *feats[] = {"writable-running", NULL};
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats)));
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "notifications", "2008-07-14", NULL)));
+
+ data = "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <filter type=\"xpath\" select=\"/*\"/>\n"
+ "</get>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL));
+ ly_in_free(in, 0);
+ assert_non_null(tree);
+
+ node = tree;
+ dsc = "Retrieve running configuration and device state information.";
+ CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "get", LYS_RPC,
+ 1, 0, 0, 0, 0, ref, 0);
+ node = lyd_child(node);
+ dsc = "This parameter specifies the portion of the system\nconfiguration and state data to retrieve.";
+ CHECK_LYSC_NODE(node->schema, dsc, 1, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "filter", 0, LYS_ANYXML, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<create-subscription xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ " <filter type=\"subtree\">\n"
+ " <inner-node xmlns=\"my:urn\"/>\n"
+ " </filter>\n"
+ "</create-subscription>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL));
+ ly_in_free(in, 0);
+ assert_non_null(tree);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+}
+
+static void
+test_data_skip(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct lyd_node_term *leaf;
+
+ /* add invalid data to a module that is not implemented */
+ data = "<foo xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\"><u/></foo>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_null(tree);
+
+ /* add invalid data to a module that is implemented */
+ data = "<fooX xmlns=\"urn:tests:a\"><u/><list><value/></list></fooX>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_null(tree);
+
+ /* first invalid, next valid */
+ data = "<fooX xmlns=\"urn:tests:a\"><u/></fooX> <foo xmlns=\"urn:tests:a\">foo value</foo>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0);
+ leaf = (struct lyd_node_term *)tree;
+ CHECK_LYD_VALUE(leaf->value, STRING, "foo value");
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_leaf, setup),
+ UTEST(test_anydata, setup),
+ UTEST(test_list, setup),
+ UTEST(test_container, setup),
+ UTEST(test_opaq, setup),
+ UTEST(test_rpc, setup),
+ UTEST(test_action, setup),
+ UTEST(test_notification, setup),
+ UTEST(test_reply, setup),
+ UTEST(test_netconf_rpc, setup),
+ UTEST(test_netconf_action, setup),
+ UTEST(test_netconf_reply_or_notification, setup),
+ UTEST(test_filter_attributes, setup),
+ UTEST(test_data_skip, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_printer_xml.c b/tests/utests/data/test_printer_xml.c
new file mode 100644
index 0000000..d533c41
--- /dev/null
+++ b/tests/utests/data/test_printer_xml.c
@@ -0,0 +1,343 @@
+/*
+ * @file test_printer_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from printer_yang.c
+ *
+ * Copyright (c) 2019-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <string.h>
+
+#include "context.h"
+#include "out.h"
+#include "parser_data.h"
+#include "printer_data.h"
+#include "tests_config.h"
+#include "tree_schema.h"
+
+static int
+setup(void **state)
+{
+ const char *schema_defs = "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;"
+ "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;} identity fast-ethernet {base ethernet;}}";
+ const char *schema_types = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}"
+ "feature f; identity gigabit-ethernet { base defs:ethernet;}"
+ "container cont {leaf leaftarget {type empty;}"
+ " list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}"
+ " action test {input {leaf a {type string;}} output {leaf b {type string;}}}}"
+ " leaf-list leaflisttarget {type uint8; max-elements 5;}}"
+ "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}"
+ "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}"
+ "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}"
+ "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}"
+ "leaf-list leaflisttarget {type string;}"
+ "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}"
+ "leaf binary-norestr {type binary;}"
+ "leaf int8 {type int8 {range 10..20;}}"
+ "leaf uint8 {type uint8 {range 150..200;}}"
+ "leaf int16 {type int16 {range -20..-10;}}"
+ "leaf uint16 {type uint16 {range 150..200;}}"
+ "leaf int32 {type int32;}"
+ "leaf uint32 {type uint32;}"
+ "leaf int64 {type int64;}"
+ "leaf uint64 {type uint64;}"
+ "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}"
+ "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}"
+ "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}"
+ "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}"
+ "leaf str {type string {length 8..10; pattern '[a-z ]*';}}"
+ "leaf str-norestr {type string;}"
+ "leaf bool {type boolean;}"
+ "leaf empty {type empty;}"
+ "leaf ident {type identityref {base defs:interface-type;}}"
+ "leaf inst {type instance-identifier {require-instance true;}}"
+ "leaf inst-noreq {type instance-identifier {require-instance false;}}"
+ "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}"
+ "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}"
+ "leaf un1 {type union {"
+ " type leafref {path /int8; require-instance true;}"
+ " type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }"
+ " type string {length 1..20;}}}"
+ "anydata any;"
+ "rpc sum {input {leaf x {type uint8;} leaf y {type uint8;}} output {leaf result {type uint16;}}}}";
+ const char *schema_defaults =
+ "module defaults {\n"
+ " namespace \"urn:defaults\";\n"
+ " prefix d;\n"
+ " leaf a {\n"
+ " type union {\n"
+ " type instance-identifier;\n"
+ " type string;\n"
+ " }\n"
+ " default \"/d:b\";\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ "}";
+
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema_defs, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(schema_types, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(schema_defaults, LYS_IN_YANG, NULL, NULL);
+
+ return 0;
+}
+
+#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE)
+
+#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \
+ CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, PRINT_OPTION)
+
+static void
+test_anydata(void **state)
+{
+ struct lyd_node *tree;
+ const char *data;
+
+ data = "<any xmlns=\"urn:tests:types\"><somexml xmlns:x=\"url:x\" xmlns=\"example.com\"><x:x/></somexml></any>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ /* canonized */
+ data = "<any xmlns=\"urn:tests:types\"><somexml xmlns=\"example.com\"><x xmlns=\"url:x\"/></somexml></any>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<any xmlns=\"urn:tests:types\"/>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<any xmlns=\"urn:tests:types\">\n"
+ " <cont>\n"
+ " <defs:elem1 xmlns:defs=\"urn:tests:defs\">\n"
+ " <elem2 xmlns:defaults=\"urn:defaults\" defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\">\n"
+ " </elem2>\n"
+ " </defs:elem1>\n"
+ " </cont>\n"
+ "</any>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ /* cont should be normally parsed */
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "any", 0, LYS_ANYDATA, 0, 0, NULL, 0);
+ CHECK_LYD_NODE_ANY((struct lyd_node_any *)tree, 0, 0, 0, LYD_ANYDATA_DATATREE);
+ struct lyd_node *tree_tmp = ((struct lyd_node_any *)tree)->value.tree;
+
+ CHECK_LYSC_NODE(tree_tmp->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "cont", 1, LYS_CONTAINER, 0, 0, NULL, 0);
+ /* but its children not */
+ assert_null(((struct lyd_node_inner *)tree_tmp)->child->schema);
+ /* canonized */
+ data =
+ "<any xmlns=\"urn:tests:types\">\n"
+ " <cont>\n"
+ " <elem1 xmlns=\"urn:tests:defs\">\n"
+ " <elem2 xmlns=\"urn:tests:types\" xmlns:defs=\"urn:tests:defs\" xmlns:defaults=\"urn:defaults\" "
+ "defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\"/>\n"
+ " </elem1>\n"
+ " </cont>\n"
+ "</any>\n";
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<any xmlns=\"urn:tests:types\">\n"
+ " <ahoj attr=\"&lt;test\">\n"
+ " ahoj jak se vede &lt; how are you"
+ " </ahoj>\n"
+ "</any>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<any xmlns=\"urn:tests:types\">\n"
+ " <leaflisttarget> ahoj </leaflisttarget>\n"
+ " <leaflisttarget> nazdar </leaflisttarget>\n"
+ " <leaflisttarget> ÄŒau </leaflisttarget>\n"
+ "</any>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<any xmlns=\"urn:tests:types\">\n"
+ " <cont2/>\n"
+ "</any>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "<any xmlns=\"urn:tests:types\">\n"
+ " <cont>\n"
+ " &lt; how are you"
+ " </cont>\n"
+ "</any>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+}
+
+static void
+test_defaults(void **state)
+{
+ struct lyd_node *tree;
+ const char *data;
+ const char *data_trim;
+ const char *data_all;
+ const char *data_all_tag;
+ const char *data_impl_tag;
+
+ 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-netconf-with-defaults", "2011-06-01", NULL));
+
+ /* standard default value */
+ data = "<c xmlns=\"urn:defaults\">aa</c>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ data = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><c xmlns=\"urn:defaults\">aa</c>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ data = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
+ " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>"
+ "<c xmlns=\"urn:defaults\">aa</c>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ data = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
+ " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>"
+ "<c xmlns=\"urn:defaults\">aa</c>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* string value equal to the default but default is an unresolved instance-identifier, so they are not considered equal */
+ data = "<a xmlns=\"urn:defaults\">/d:b</a><c xmlns=\"urn:defaults\">aa</c>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* instance-identifier value equal to the default, should be considered equal */
+ data = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>";
+ data_trim = "<b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>";
+ data_all = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>";
+ data_all_tag = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
+ " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>"
+ "<b xmlns=\"urn:defaults\">val</b>"
+ "<c xmlns=\"urn:defaults\">aa</c>";
+ data_impl_tag = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>";
+
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_trim);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_all);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_all_tag);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_impl_tag);
+ lyd_free_all(tree);
+}
+
+#if 0
+
+static void
+test_rpc(void **state)
+{
+ struct state_s *s = (struct state_s *)(*state);
+ struct lyd_node *tree1;
+ struct lyd_node *tree2;
+ const struct lyd_node **trees;
+ const char *request;
+ const char *reply, *result;
+ char *printed;
+ ssize_t len;
+ struct ly_out *out;
+
+ s->func = test_rpc;
+ assert_non_null(out = ly_out_new_memory(&printed, 0));
+
+ request = "<sum xmlns=\"urn:tests:types\"><x>10</x><y>20</y></sum>";
+ reply = "<result xmlns=\"urn:tests:types\">30</result>";
+ result = "<sum xmlns=\"urn:tests:types\"><result>30</result></sum>";
+ assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL));
+ assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0);
+ assert_int_equal(len, strlen(printed));
+ assert_string_equal(printed, request);
+ ly_out_reset(out);
+ assert_non_null(trees = lyd_trees_new(1, tree1));
+ assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees));
+ assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0);
+ assert_int_equal(len, strlen(printed));
+ assert_string_equal(printed, result);
+ ly_out_reset(out);
+ lyd_trees_free(trees, 0);
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* no arguments */
+ request = "<sum xmlns=\"urn:tests:types\"/>";
+ reply = "";
+ result = "<sum xmlns=\"urn:tests:types\"/>";
+ assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL));
+ assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0);
+ assert_int_equal(len, strlen(printed));
+ assert_string_equal(printed, request);
+ ly_out_reset(out);
+ assert_non_null(trees = lyd_trees_new(1, tree1));
+ assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees));
+ assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0);
+ assert_int_equal(len, strlen(printed));
+ assert_string_equal(printed, result);
+ ly_out_reset(out);
+ lyd_trees_free(trees, 0);
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* action
+ * "container cont {leaf leaftarget {type empty;}"
+ "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}"
+ "action test {input {leaf a {type string;}} output {leaf b {type string;}}}}"
+ "leaf-list leaflisttarget {type uint8; max-elements 5;}}"
+ */
+ request = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><a>test</a></test></listtarget></cont>";
+ reply = "<b xmlns=\"urn:tests:types\">test-reply</b>";
+ result = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><b>test-reply</b></test></listtarget></cont>";
+ assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL));
+ assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0);
+ assert_int_equal(len, strlen(printed));
+ assert_string_equal(printed, request);
+ ly_out_reset(out);
+ assert_non_null(trees = lyd_trees_new(1, tree1));
+ assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees));
+ assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0);
+ assert_int_equal(len, strlen(printed));
+ assert_string_equal(printed, result);
+ ly_out_reset(out);
+ lyd_trees_free(trees, 0);
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ ly_out_free(out, NULL, 1);
+ s->func = NULL;
+}
+
+#endif
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_anydata, setup),
+ UTEST(test_defaults, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c
new file mode 100644
index 0000000..27dba42
--- /dev/null
+++ b/tests/utests/data/test_tree_data.c
@@ -0,0 +1,597 @@
+/**
+ * @file test_tree_data.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from tress_data.c
+ *
+ * Copyright (c) 2018-2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "common.h"
+#include "libyang.h"
+#include "path.h"
+#include "xpath.h"
+
+static int
+setup(void **state)
+{
+ const char *schema1 = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "leaf bar {type string;}"
+ "list l1 { key \"a b\"; leaf a {type string;} leaf b {type string;} leaf c {type string;}}"
+ "leaf foo { type string;}"
+ "leaf-list ll { type string;}"
+ "container c {leaf-list x {type string;}}"
+ "anydata any {config false;}"
+ "list l2 {config false;"
+ " container c{leaf x {type string;} leaf-list d {type string;}}"
+ "}}";
+
+ const char *schema2 = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "list l2 {config false;"
+ " container c{leaf x {type string;}}}"
+ "anydata any {config false;}"
+ "}";
+
+ const char *schema3 = "module c {yang-version 1.1; namespace \"http://example.com/main\";prefix m;"
+ "import \"ietf-inet-types\" {prefix inet;}"
+ "typedef optional-ip-address {type union {"
+ " type inet:ip-address;"
+ " type string;"
+ "}}"
+ "container cont {"
+ " list nexthop {min-elements 1; key \"gateway\";"
+ " leaf gateway {type optional-ip-address;}"
+ " }"
+ " leaf-list pref {type inet:ipv6-prefix;}"
+ "}}";
+
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema1, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(schema3, LYS_IN_YANG, NULL, NULL);
+
+ return 0;
+}
+
+#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE)
+
+#define CHECK_PARSE_LYD_PARAM_CTX(CTX, INPUT, PARSE_OPTION, OUT_NODE) \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(CTX, INPUT, LYD_XML, PARSE_OPTION, LYD_VALIDATE_PRESENT, &OUT_NODE)); \
+ assert_non_null(OUT_NODE);
+
+#define RECREATE_CTX_WITH_MODULE(CTX, MODULE) \
+ ly_ctx_destroy(CTX); \
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &CTX)); \
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(MODULE, &_UC->in)); \
+ assert_int_equal(LY_SUCCESS, lys_parse(CTX, _UC->in, LYS_IN_YANG, NULL, NULL)); \
+ ly_in_free(_UC->in, 0);
+
+static void
+test_compare(void **state)
+{
+ struct lyd_node *tree1, *tree2;
+ const char *data1;
+ const char *data2;
+
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(NULL, NULL, 0));
+
+ data1 = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>";
+ data2 = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>y</c></l1>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ assert_int_equal(LY_ENOT, lyd_compare_single(((struct lyd_node_inner *)tree1)->child, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data1 = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
+ data2 = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, 0));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next->next, tree2->next, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data1 = "<ll xmlns=\"urn:tests:a\">a</ll><ll xmlns=\"urn:tests:a\">b</ll>";
+ data2 = "<ll xmlns=\"urn:tests:a\">b</ll>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ assert_int_equal(LY_ENOT, lyd_compare_single(NULL, tree2, 0));
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, NULL, 0));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data1 = "<c xmlns=\"urn:tests:a\"><x>x</x></c>";
+ data2 = "<c xmlns=\"urn:tests:a\"><x>y</x></c>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data1 = "<c xmlns=\"urn:tests:a\"><x>x</x></c>";
+ data2 = "<c xmlns=\"urn:tests:a\"><x>x</x><x>y</x></c>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data1 = "<any xmlns=\"urn:tests:a\"><x>x</x></any>";
+ data2 = "<any xmlns=\"urn:tests:a\"><x>x</x><x>y</x></any>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, 0));
+ lyd_free_all(tree1);
+ data1 = "<any xmlns=\"urn:tests:a\"><x>x</x><x>y</x></any>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+}
+
+static void
+test_compare_diff_ctx(void **state)
+{
+ struct lyd_node *tree1, *tree2;
+ const char *data1, *data2;
+ struct ly_ctx *ctx2 = NULL;
+ const char *module;
+
+ /* create second context with the same schema */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "list l2 {config false;"
+ " container c{leaf x {type string;}}"
+ "}}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with schema that has a different name */
+ module = "module c {namespace urn:tests:c;prefix c;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "list l2 {config false;"
+ " container c{leaf x {type string;}}"
+ "}}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ data2 = "<l2 xmlns=\"urn:tests:c\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with schema that has a different revision */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2015-05-08;"
+ "list l2 {config false;"
+ " container c{leaf x {type string;}}"
+ "}}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with schema that has no revision */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "list l2 {config false;"
+ " container c{leaf x {type string;}}"
+ "}}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with schema that has a different parent nodetype */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "container l2 {config false;"
+ " container c{leaf x {type string;}}"
+ "}}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(lyd_child(lyd_child(tree1)), lyd_child(lyd_child(tree2)), 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with the same opaq data nodes */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "anydata any {config false;}"
+ "}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>";
+ data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>bb:x</x></any>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with the different opaq data node value */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "anydata any {config false;}"
+ "}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>";
+ data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>bb:y</x></any>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* recreate second context with the wrong prefix in opaq data node value */
+ module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;"
+ "revision 2014-05-08;"
+ "anydata any {config false;}"
+ "}";
+ RECREATE_CTX_WITH_MODULE(ctx2, module);
+ data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>";
+ data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>cc:x</x></any>";
+ CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1);
+ CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2);
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* clean up */
+ ly_ctx_destroy(ctx2);
+ _UC->in = NULL;
+}
+
+static void
+test_dup(void **state)
+{
+ struct lyd_node *tree1, *tree2;
+ const char *result;
+ const char *data;
+
+ data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, LYD_DUP_RECURSIVE, &tree2));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>";
+ result = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b></l1>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, 0, &tree2));
+ lyd_free_all(tree1);
+ CHECK_PARSE_LYD(result, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
+ result = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_siblings(tree1, NULL, LYD_DUP_RECURSIVE, &tree2));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2->next, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree2);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, LYD_DUP_RECURSIVE, &tree2));
+ lyd_free_all(tree1);
+ CHECK_PARSE_LYD(result, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree2);
+
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2));
+ lyd_free_all(tree1);
+ result = "<l2 xmlns=\"urn:tests:a\"/>";
+ CHECK_PARSE_LYD_PARAM(result, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data = "<any xmlns=\"urn:tests:a\"><c><a>a</a></c></any>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, 0, &tree2));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)((struct lyd_node_inner *)tree1->next)->child)->child, NULL,
+ LYD_DUP_WITH_PARENTS, &tree2));
+ int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM;
+
+ CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "x", 1, LYS_LEAF, 1, 0, NULL, 0);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, (struct lyd_node *)tree2->parent->parent,
+ LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>c</c></l1>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev, NULL,
+ LYD_DUP_WITH_PARENTS, &tree2));
+ flag = LYS_CONFIG_W | LYS_SET_ENUM;
+ CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "c", 0, LYS_LEAF, 1, 0, NULL, 0);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, (struct lyd_node *)tree2->parent, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2));
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)((struct lyd_node_inner *)tree1->next)->child)->child,
+ (struct lyd_node_inner *)tree2, LYD_DUP_WITH_PARENTS, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
+
+ /* invalid */
+ data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>c</c></l1><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
+ assert_int_equal(LY_EINVAL, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev,
+ (struct lyd_node_inner *)tree1->next, LYD_DUP_WITH_PARENTS, NULL));
+ lyd_free_all(tree1);
+}
+
+static void
+test_target(void **state)
+{
+ const struct lyd_node_term *term;
+ struct lyd_node *tree;
+ struct lyxp_expr *exp;
+ struct ly_path *path;
+ const char *path_str = "/a:l2[2]/c/d[3]";
+ const char *data =
+ "<l2 xmlns=\"urn:tests:a\"><c>"
+ " <d>a</d>"
+ " </c></l2>"
+ "<l2 xmlns=\"urn:tests:a\"><c>"
+ " <d>a</d>"
+ " <d>b</d>"
+ " <d>b</d>"
+ " <d>c</d>"
+ "</c></l2>"
+ "<l2 xmlns=\"urn:tests:a\"><c>"
+ "</c></l2>";
+
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_int_equal(LY_SUCCESS, ly_path_parse(UTEST_LYCTX, NULL, path_str, strlen(path_str), 0, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp));
+ assert_int_equal(LY_SUCCESS, ly_path_compile(UTEST_LYCTX, NULL, NULL, NULL, exp, LY_PATH_OPER_INPUT,
+ LY_PATH_TARGET_SINGLE, 1, LY_VALUE_JSON, NULL, &path));
+ term = lyd_target(path, tree);
+
+ const int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM | LYS_ORDBY_USER;
+
+ CHECK_LYSC_NODE(term->schema, NULL, 0, flag, 1, "d", 0, LYS_LEAFLIST, 1, 0, NULL, 0);
+ assert_string_equal(lyd_get_value(&term->node), "b");
+ assert_string_equal(lyd_get_value(term->prev), "b");
+
+ lyd_free_all(tree);
+ ly_path_free(UTEST_LYCTX, path);
+ lyxp_expr_free(UTEST_LYCTX, exp);
+}
+
+static void
+test_list_pos(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ data = "<bar xmlns=\"urn:tests:a\">test</bar>"
+ "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b></l1>"
+ "<l1 xmlns=\"urn:tests:a\"><a>two</a><b>two</b></l1>"
+ "<foo xmlns=\"urn:tests:a\">test</foo>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_int_equal(0, lyd_list_pos(tree));
+ assert_int_equal(1, lyd_list_pos(tree->next));
+ assert_int_equal(2, lyd_list_pos(tree->next->next));
+ assert_int_equal(0, lyd_list_pos(tree->next->next->next));
+ lyd_free_all(tree);
+
+ data = "<ll xmlns=\"urn:tests:a\">one</ll>"
+ "<ll xmlns=\"urn:tests:a\">two</ll>"
+ "<ll xmlns=\"urn:tests:a\">three</ll>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_int_equal(1, lyd_list_pos(tree));
+ assert_int_equal(2, lyd_list_pos(tree->next));
+ assert_int_equal(3, lyd_list_pos(tree->next->next));
+ lyd_free_all(tree);
+
+ data = "<ll xmlns=\"urn:tests:a\">one</ll>"
+ "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b></l1>"
+ "<ll xmlns=\"urn:tests:a\">two</ll>"
+ "<l1 xmlns=\"urn:tests:a\"><a>two</a><b>two</b></l1>"
+ "<ll xmlns=\"urn:tests:a\">three</ll>"
+ "<l1 xmlns=\"urn:tests:a\"><a>three</a><b>three</b></l1>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_string_equal("l1", tree->schema->name);
+ assert_int_equal(1, lyd_list_pos(tree));
+ assert_int_equal(2, lyd_list_pos(tree->next));
+ assert_int_equal(3, lyd_list_pos(tree->next->next));
+ assert_string_equal("ll", tree->next->next->next->schema->name);
+ assert_int_equal(1, lyd_list_pos(tree->next->next->next));
+ assert_int_equal(2, lyd_list_pos(tree->next->next->next->next));
+ assert_int_equal(3, lyd_list_pos(tree->next->next->next->next->next));
+ lyd_free_all(tree);
+}
+
+static void
+test_first_sibling(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct lyd_node_inner *parent;
+
+ data = "<bar xmlns=\"urn:tests:a\">test</bar>"
+ "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b><c>one</c></l1>"
+ "<foo xmlns=\"urn:tests:a\">test</foo>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_ptr_equal(tree, lyd_first_sibling(tree->next));
+ assert_ptr_equal(tree, lyd_first_sibling(tree));
+ assert_ptr_equal(tree, lyd_first_sibling(tree->prev));
+ parent = (struct lyd_node_inner *)tree->next;
+ assert_int_equal(LYS_LIST, parent->schema->nodetype);
+ assert_ptr_equal(parent->child, lyd_first_sibling(parent->child->next));
+ assert_ptr_equal(parent->child, lyd_first_sibling(parent->child));
+ assert_ptr_equal(parent->child, lyd_first_sibling(parent->child->prev));
+ lyd_free_all(tree);
+}
+
+static void
+test_find_path(void **state)
+{
+ struct lyd_node *root;
+ const struct lys_module *mod;
+
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c");
+ assert_non_null(mod);
+
+ assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "cont", 0, &root));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='10.0.0.1']", NULL, LYD_NEW_PATH_UPDATE, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='2100::1']", NULL, LYD_NEW_PATH_UPDATE, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/pref[.='fc00::/64']", NULL, 0, NULL));
+
+ assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='10.0.0.1']", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='2100::1']", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/pref[.='fc00::/64']", 0, NULL));
+ lyd_free_all(root);
+}
+
+static void
+test_data_hash(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema, *data;
+
+ schema =
+ "module test-data-hash {"
+ " yang-version 1.1;"
+ " namespace \"urn:tests:tdh\";"
+ " prefix t;"
+ " container c {"
+ " leaf-list ll {"
+ " type string;"
+ " }"
+ " }"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* The number of <ll/> must be greater or equal to LYD_HT_MIN_ITEMS
+ * for the correct test run. It should guarantee the creation of a hash table.
+ */
+ assert_true(LYD_HT_MIN_ITEMS <= 4);
+ data =
+ "<c xmlns='urn:tests:tdh'>"
+ " <ll/>"
+ " <ll/>"
+ " <ll/>"
+ " <ll/>"
+ "</c>";
+
+ /* The run must not crash due to the assert that checks the hash. */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ lyd_free_all(tree);
+}
+
+static void
+test_lyxp_vars(void **UNUSED(state))
+{
+ struct lyxp_var *vars;
+
+ /* Test free. */
+ vars = NULL;
+ lyxp_vars_free(vars);
+
+ /* Bad arguments for lyxp_vars_add(). */
+ assert_int_equal(LY_EINVAL, lyxp_vars_set(NULL, "var1", "val1"));
+ assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, NULL, "val1"));
+ assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, "var1", NULL));
+ lyxp_vars_free(vars);
+ vars = NULL;
+
+ /* Add one item. */
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1"));
+ assert_int_equal(LY_ARRAY_COUNT(vars), 1);
+ assert_string_equal(vars[0].name, "var1");
+ assert_string_equal(vars[0].value, "val1");
+ lyxp_vars_free(vars);
+ vars = NULL;
+
+ /* Add three items. */
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var3", "val3"));
+ assert_int_equal(LY_ARRAY_COUNT(vars), 3);
+ assert_string_equal(vars[0].name, "var1");
+ assert_string_equal(vars[0].value, "val1");
+ assert_string_equal(vars[1].name, "var2");
+ assert_string_equal(vars[1].value, "val2");
+ assert_string_equal(vars[2].name, "var3");
+ assert_string_equal(vars[2].value, "val3");
+ lyxp_vars_free(vars);
+ vars = NULL;
+
+ /* Change value of a variable. */
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "new_value"));
+ assert_string_equal(vars[0].name, "var1");
+ assert_string_equal(vars[0].value, "new_value");
+ assert_string_equal(vars[1].name, "var2");
+ assert_string_equal(vars[1].value, "val2");
+ lyxp_vars_free(vars);
+ vars = NULL;
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_compare, setup),
+ UTEST(test_compare_diff_ctx, setup),
+ UTEST(test_dup, setup),
+ UTEST(test_target, setup),
+ UTEST(test_list_pos, setup),
+ UTEST(test_first_sibling, setup),
+ UTEST(test_find_path, setup),
+ UTEST(test_data_hash, setup),
+ UTEST(test_lyxp_vars),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
new file mode 100644
index 0000000..415e16a
--- /dev/null
+++ b/tests/utests/data/test_validation.c
@@ -0,0 +1,1460 @@
+/**
+ * @file test_validation.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from validation.c
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "context.h"
+#include "in.h"
+#include "out.h"
+#include "parser_data.h"
+#include "printer_data.h"
+#include "tests_config.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+
+#define LYD_TREE_CREATE(INPUT, MODEL) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL)
+
+static void
+test_when(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container cont {\n"
+ " leaf a {\n"
+ " when \"../../c = 'val_c'\";\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf c {\n"
+ " when \"/cont/b = 'val_b'\";\n"
+ " type string;\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM("<c xmlns=\"urn:tests:a\">hey</c>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("When condition \"/cont/b = 'val_b'\" not satisfied.", "Data location \"/a:c\".");
+
+ LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><b>val_b</b></cont><c xmlns=\"urn:tests:a\">hey</c>", tree);
+ CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 0, LYS_LEAF, 0, 0, NULL, 1);
+ assert_int_equal(LYD_WHEN_TRUE, tree->next->flags);
+ lyd_free_all(tree);
+
+ LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val</a><b>val_b</b></cont><c xmlns=\"urn:tests:a\">val_c</c>", tree);
+ CHECK_LYSC_NODE(lyd_child(tree)->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 1);
+ assert_int_equal(LYD_WHEN_TRUE, lyd_child(tree)->flags);
+ CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 0, LYS_LEAF, 0, 0, NULL, 1);
+ assert_int_equal(LYD_WHEN_TRUE, tree->next->flags);
+ lyd_free_all(tree);
+}
+
+static void
+test_mandatory_when(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container cont {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " when \"../a = 'val_a'\";\n"
+ " mandatory true;\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ " leaf d {\n"
+ " when \"../c = 'val_c'\";\n"
+ " mandatory true;\n"
+ " type string;\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:a\">hey</d>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("When condition \"../c = 'val_c'\" not satisfied.", "Data location \"/a:d\".");
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:a\"><b>hey</b></cont>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("When condition \"../a = 'val_a'\" not satisfied.", "Data location \"/a:cont/b\".");
+
+ LYD_TREE_CREATE("<c xmlns=\"urn:tests:a\">val_c</c><d xmlns=\"urn:tests:a\">hey</d>", tree);
+ CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "d", 0, LYS_LEAF, 0, 0, NULL, 1);
+ assert_int_equal(LYD_WHEN_TRUE, tree->next->next->flags);
+ lyd_free_all(tree);
+
+ LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val_a</a><b>hey</b></cont>", tree);
+ CHECK_LYSC_NODE(lyd_child(tree)->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "b", 0, LYS_LEAF, tree->schema, 0, NULL, 1);
+ assert_int_equal(LYD_WHEN_TRUE, lyd_child(tree)->next->flags);
+ lyd_free_all(tree);
+}
+
+static void
+test_type_incomplete_when(void **state)
+{
+ struct lys_module *mod;
+ struct lyd_node *tree;
+ const char *schema =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container cont {\n"
+ " when \"../c = 'val_c'\";\n"
+ " leaf a {\n"
+ " type leafref {\n"
+ " path \"/a:c\";\n"
+ " }\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val_c</a><b>val</b></cont><c xmlns=\"urn:tests:a\">val_c</c>", tree);
+
+ /* make the when false */
+ assert_int_equal(LY_SUCCESS, lyd_change_term(tree->next, "wrong-val"));
+
+ /* autodelete when with a leafref */
+ assert_int_equal(LY_SUCCESS, lyd_validate_module(&tree, mod, 0, NULL));
+ assert_string_equal(LYD_NAME(tree), "c");
+ assert_null(tree->next);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_mandatory(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " choice choic {\n"
+ " mandatory true;\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " case b {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " leaf c {\n"
+ " mandatory true;\n"
+ " type string;\n"
+ " }\n"
+ " leaf d {\n"
+ " type empty;\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:b\"/>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Mandatory choice \"choic\" data do not exist.", "Schema location \"/b:choic\".", "missing-choice");
+
+ CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:b\">string</l><d xmlns=\"urn:tests:b\"/>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Mandatory node \"c\" instance does not exist.", "Schema location \"/b:c\".");
+
+ CHECK_PARSE_LYD_PARAM("<a xmlns=\"urn:tests:b\">string</a>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Mandatory node \"c\" instance does not exist.", "Schema location \"/b:c\".");
+
+ LYD_TREE_CREATE("<a xmlns=\"urn:tests:b\">string</a><c xmlns=\"urn:tests:b\">string2</c>", tree);
+ lyd_free_siblings(tree);
+}
+
+static void
+test_minmax(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module c {\n"
+ " namespace urn:tests:c;\n"
+ " prefix c;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " choice choic {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " case b {\n"
+ " leaf-list l {\n"
+ " min-elements 3;\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " list lt {\n"
+ " max-elements 4;\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf d {\n"
+ " type empty;\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">mate</l>"
+ "<d xmlns=\"urn:tests:c\"/>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "Schema location \"/c:choic/b/l\".", "too-few-elements");
+
+ CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>"
+ "<l xmlns=\"urn:tests:c\">val2</l>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "Schema location \"/c:choic/b/l\".", "too-few-elements");
+
+ LYD_TREE_CREATE("<l xmlns=\"urn:tests:c\">val1</l>"
+ "<l xmlns=\"urn:tests:c\">val2</l>"
+ "<l xmlns=\"urn:tests:c\">val3</l>", tree);
+ lyd_free_all(tree);
+
+ CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>"
+ "<l xmlns=\"urn:tests:c\">val2</l>"
+ "<l xmlns=\"urn:tests:c\">val3</l>"
+ "<lt xmlns=\"urn:tests:c\"><k>val1</k></lt>"
+ "<lt xmlns=\"urn:tests:c\"><k>val2</k></lt>"
+ "<lt xmlns=\"urn:tests:c\"><k>val3</k></lt>"
+ "<lt xmlns=\"urn:tests:c\"><k>val4</k></lt>"
+ "<lt xmlns=\"urn:tests:c\"><k>val5</k></lt>"
+ "<lt xmlns=\"urn:tests:c\"><k>val6</k></lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Too many \"lt\" instances.", "Data location \"/c:lt[k='val5']\".",
+ "too-many-elements");
+}
+
+const char *schema_d =
+ "module d {\n"
+ " namespace urn:tests:d;\n"
+ " prefix d;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " list lt {\n"
+ " key \"k\";\n"
+ " unique \"l1\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " list lt2 {\n"
+ " key \"k\";\n"
+ " unique \"cont/l2 l4\";\n"
+ " unique \"l5 l6\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " container cont {\n"
+ " leaf l2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf l4 {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l5 {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l6 {\n"
+ " type string;\n"
+ " }\n"
+ " list lt3 {\n"
+ " key \"kk\";\n"
+ " unique \"l3\";\n"
+ " leaf kk {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l3 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+
+static void
+test_unique(void **state)
+{
+ struct lyd_node *tree;
+
+ UTEST_ADD_MODULE(schema_d, LYS_IN_YANG, NULL, NULL);
+
+ LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <l1>same</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ "</lt>", tree);
+ lyd_free_all(tree);
+
+ LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <l1>same</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <l1>not-same</l1>\n"
+ "</lt>", tree);
+ lyd_free_all(tree);
+
+ CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <l1>same</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <l1>same</l1>\n"
+ "</lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val1']\" and \"/d:lt[k='val2']\".",
+ "Data location \"/d:lt[k='val2']\".", "data-not-unique");
+
+ /* now try with more instances */
+ LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <l1>1</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <l1>2</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ " <l1>3</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ " <l1>4</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ " <l1>5</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val6</k>\n"
+ " <l1>6</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val7</k>\n"
+ " <l1>7</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val8</k>\n"
+ " <l1>8</l1>\n"
+ "</lt>", tree);
+ lyd_free_all(tree);
+
+ LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <l1>1</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <l1>2</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ " <l1>3</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ " <l1>5</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val6</k>\n"
+ " <l1>6</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val7</k>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val8</k>\n"
+ "</lt>", tree);
+ lyd_free_all(tree);
+
+ CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <l1>1</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <l1>2</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ " <l1>4</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val6</k>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val7</k>\n"
+ " <l1>2</l1>\n"
+ "</lt>\n"
+ "<lt xmlns=\"urn:tests:d\">\n"
+ " <k>val8</k>\n"
+ " <l1>8</l1>\n"
+ "</lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val7']\" and \"/d:lt[k='val2']\".",
+ "Data location \"/d:lt[k='val2']\".", "data-not-unique");
+}
+
+static void
+test_unique_nested(void **state)
+{
+ struct lyd_node *tree;
+
+ UTEST_ADD_MODULE(schema_d, LYS_IN_YANG, NULL, NULL);
+
+ /* nested list uniquest are compared only with instances in the same parent list instance */
+ LYD_TREE_CREATE("<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <cont>\n"
+ " <l2>1</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <cont>\n"
+ " <l2>2</l2>\n"
+ " </cont>\n"
+ " <l4>2</l4>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>1</l3>\n"
+ " </lt3>\n"
+ " <lt3>\n"
+ " <kk>val2</kk>\n"
+ " <l3>2</l3>\n"
+ " </lt3>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ " <cont>\n"
+ " <l2>3</l2>\n"
+ " </cont>\n"
+ " <l4>3</l4>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>2</l3>\n"
+ " </lt3>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ " <cont>\n"
+ " <l2>4</l2>\n"
+ " </cont>\n"
+ " <l4>4</l4>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>3</l3>\n"
+ " </lt3>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ " <cont>\n"
+ " <l2>5</l2>\n"
+ " </cont>\n"
+ " <l4>5</l4>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>3</l3>\n"
+ " </lt3>\n"
+ "</lt2>", tree);
+ lyd_free_all(tree);
+
+ CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <cont>\n"
+ " <l2>1</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <cont>\n"
+ " <l2>2</l2>\n"
+ " </cont>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>1</l3>\n"
+ " </lt3>\n"
+ " <lt3>\n"
+ " <kk>val2</kk>\n"
+ " <l3>2</l3>\n"
+ " </lt3>\n"
+ " <lt3>\n"
+ " <kk>val3</kk>\n"
+ " <l3>1</l3>\n"
+ " </lt3>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ " <cont>\n"
+ " <l2>3</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>2</l3>\n"
+ " </lt3>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ " <cont>\n"
+ " <l2>4</l2>\n"
+ " </cont>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>3</l3>\n"
+ " </lt3>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ " <cont>\n"
+ " <l2>5</l2>\n"
+ " </cont>\n"
+ " <lt3>\n"
+ " <kk>val1</kk>\n"
+ " <l3>3</l3>\n"
+ " </lt3>\n"
+ "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l3\" not satisfied in \"/d:lt2[k='val2']/lt3[kk='val3']\" and "
+ "\"/d:lt2[k='val2']/lt3[kk='val1']\".",
+ "Data location \"/d:lt2[k='val2']/lt3[kk='val1']\".", "data-not-unique");
+
+ CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <cont>\n"
+ " <l2>1</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <cont>\n"
+ " <l2>2</l2>\n"
+ " </cont>\n"
+ " <l4>2</l4>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ " <cont>\n"
+ " <l2>3</l2>\n"
+ " </cont>\n"
+ " <l4>3</l4>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ " <cont>\n"
+ " <l2>2</l2>\n"
+ " </cont>\n"
+ " <l4>2</l4>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ " <cont>\n"
+ " <l2>5</l2>\n"
+ " </cont>\n"
+ " <l4>5</l4>\n"
+ "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"cont/l2 l4\" not satisfied in \"/d:lt2[k='val4']\" and \"/d:lt2[k='val2']\".",
+ "Data location \"/d:lt2[k='val2']\".", "data-not-unique");
+
+ CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val1</k>\n"
+ " <cont>\n"
+ " <l2>1</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ " <l5>1</l5>\n"
+ " <l6>1</l6>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val2</k>\n"
+ " <cont>\n"
+ " <l2>2</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ " <l5>1</l5>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val3</k>\n"
+ " <cont>\n"
+ " <l2>3</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ " <l5>3</l5>\n"
+ " <l6>3</l6>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val4</k>\n"
+ " <cont>\n"
+ " <l2>4</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ " <l6>1</l6>\n"
+ "</lt2>\n"
+ "<lt2 xmlns=\"urn:tests:d\">\n"
+ " <k>val5</k>\n"
+ " <cont>\n"
+ " <l2>5</l2>\n"
+ " </cont>\n"
+ " <l4>1</l4>\n"
+ " <l5>3</l5>\n"
+ " <l6>3</l6>\n"
+ "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l5 l6\" not satisfied in \"/d:lt2[k='val5']\" and \"/d:lt2[k='val3']\".",
+ "Data location \"/d:lt2[k='val3']\".", "data-not-unique");
+}
+
+static void
+test_dup(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module e {\n"
+ " namespace urn:tests:e;\n"
+ " prefix e;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " choice choic {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " case b {\n"
+ " leaf-list l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " list lt {\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf d {\n"
+ " type uint32;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ " container cont {\n"
+ " list lt {\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf d {\n"
+ " type uint32;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ " leaf-list ll2 {\n"
+ " type enumeration {\n"
+ " enum one;\n"
+ " enum two;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:e\">25</d><d xmlns=\"urn:tests:e\">50</d>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"d\".", "Data location \"/e:d\".");
+
+ CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:e\"><k>A</k></lt>"
+ "<lt xmlns=\"urn:tests:e\"><k>B</k></lt>"
+ "<lt xmlns=\"urn:tests:e\"><k>A</k></lt>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"lt\".", "Data location \"/e:lt[k='A']\".");
+
+ CHECK_PARSE_LYD_PARAM("<ll xmlns=\"urn:tests:e\">A</ll>"
+ "<ll xmlns=\"urn:tests:e\">B</ll>"
+ "<ll xmlns=\"urn:tests:e\">B</ll>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"ll\".", "Data location \"/e:ll[.='B']\".");
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"></cont><cont xmlns=\"urn:tests:e\"/>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"cont\".", "Data location \"/e:cont\".");
+
+ /* same tests again but using hashes */
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><d>25</d><d>50</d><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll></cont>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"d\".", "Data location \"/e:cont/d\", line number 1.");
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>"
+ "<lt><k>a</k></lt>"
+ "<lt><k>b</k></lt>"
+ "<lt><k>c</k></lt>"
+ "<lt><k>d</k></lt>"
+ "<lt><k>c</k></lt></cont>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"lt\".", "Data location \"/e:cont/lt[k='c']\", line number 1.");
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>"
+ "<ll>a</ll><ll>b</ll><ll>c</ll><ll>d</ll><ll>d</ll></cont>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"ll\".", "Data location \"/e:cont/ll[.='d']\", line number 1.");
+
+ /* cases */
+ CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:e\">a</l>"
+ "<l xmlns=\"urn:tests:e\">b</l>"
+ "<l xmlns=\"urn:tests:e\">c</l>"
+ "<l xmlns=\"urn:tests:e\">b</l>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"l\".", "Data location \"/e:l[.='b']\".");
+
+ CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:e\">a</l><l xmlns=\"urn:tests:e\">b</l>"
+ "<l xmlns=\"urn:tests:e\">c</l>"
+ "<a xmlns=\"urn:tests:e\">aa</a>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Data for both cases \"a\" and \"b\" exist.", "Schema location \"/e:choic\".");
+}
+
+static void
+test_defaults(void **state)
+{
+ struct lyd_node *tree, *node, *diff;
+ struct lys_module *mod;
+ const char *schema =
+ "module f {\n"
+ " namespace urn:tests:f;\n"
+ " prefix f;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " choice choic {\n"
+ " default \"c\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " case b {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case c {\n"
+ " leaf-list ll1 {\n"
+ " type string;\n"
+ " default \"def1\";\n"
+ " default \"def2\";\n"
+ " default \"def3\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " leaf d {\n"
+ " type uint32;\n"
+ " default 15;\n"
+ " }\n"
+ " leaf dd {\n"
+ " type uint32;\n"
+ " when '../d = 666';\n"
+ " default 15;\n"
+ " }\n"
+ " leaf-list ll2 {\n"
+ " type string;\n"
+ " default \"dflt1\";\n"
+ " default \"dflt2\";\n"
+ " }\n"
+ " container cont {\n"
+ " choice choic {\n"
+ " default \"c\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " case b {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case c {\n"
+ " leaf-list ll1 {\n"
+ " type string;\n"
+ " default \"def1\";\n"
+ " default \"def2\";\n"
+ " default \"def3\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " leaf d {\n"
+ " type uint32;\n"
+ " default 15;\n"
+ " }\n"
+ " leaf dd {\n"
+ " type uint32;\n"
+ " when '../d = 666';\n"
+ " default 15;\n"
+ " }\n"
+ " leaf-list ll2 {\n"
+ " type string;\n"
+ " default \"dflt1\";\n"
+ " default \"dflt2\";\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ 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-netconf-with-defaults", "2011-06-01", NULL));\
+
+ /* get defaults */
+ tree = NULL;
+ assert_int_equal(lyd_validate_module(&tree, mod, 0, &diff), LY_SUCCESS);
+ assert_non_null(tree);
+ assert_non_null(diff);
+
+ /* check all defaults exist */
+ CHECK_LYD_STRING_PARAM(tree,
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n"
+ "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n"
+ " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS);
+
+ /* check diff */
+ CHECK_LYD_STRING_PARAM(diff,
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def1</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def2</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def3</ll1>\n"
+ "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt1</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n"
+ " <ll1 yang:operation=\"create\">def1</ll1>\n"
+ " <ll1 yang:operation=\"create\">def2</ll1>\n"
+ " <ll1 yang:operation=\"create\">def3</ll1>\n"
+ " <d yang:operation=\"create\">15</d>\n"
+ " <ll2 yang:operation=\"create\">dflt1</ll2>\n"
+ " <ll2 yang:operation=\"create\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(diff);
+
+ /* create another explicit case and validate */
+ assert_int_equal(lyd_new_term(NULL, mod, "l", "value", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
+
+ /* check data tree */
+ CHECK_LYD_STRING_PARAM(tree,
+ "<l xmlns=\"urn:tests:f\">value</l>\n"
+ "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n"
+ " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS);
+
+ /* check diff */
+ CHECK_LYD_STRING_PARAM(diff,
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def1</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def2</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def3</ll1>\n",
+ LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(diff);
+
+ /* create explicit leaf-list and leaf and validate */
+ assert_int_equal(lyd_new_term(NULL, mod, "d", "15", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(NULL, mod, "ll2", "dflt2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
+
+ /* check data tree */
+ CHECK_LYD_STRING_PARAM(tree,
+ "<l xmlns=\"urn:tests:f\">value</l>\n"
+ "<d xmlns=\"urn:tests:f\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n"
+ " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS);
+
+ /* check diff */
+ CHECK_LYD_STRING_PARAM(diff,
+ "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt1</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt2</ll2>\n",
+ LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(diff);
+
+ /* create first explicit container, which should become implicit */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cont", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
+
+ /* check data tree */
+ CHECK_LYD_STRING_PARAM(tree,
+ "<l xmlns=\"urn:tests:f\">value</l>\n"
+ "<d xmlns=\"urn:tests:f\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n"
+ " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS);
+ /* check diff */
+ assert_null(diff);
+
+ /* create second explicit container, which should become implicit, so the first tree node should be removed */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cont", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
+
+ /* check data tree */
+ CHECK_LYD_STRING_PARAM(tree,
+ "<l xmlns=\"urn:tests:f\">value</l>\n"
+ "<d xmlns=\"urn:tests:f\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n"
+ " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n"
+ " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n"
+ " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS);
+ /* check diff */
+ assert_null(diff);
+
+ /* similar changes for nested defaults */
+ assert_int_equal(lyd_new_term(tree->prev, NULL, "ll1", "def3", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(tree->prev, NULL, "d", "5", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(tree->prev, NULL, "ll2", "non-dflt", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
+
+ /* check data tree */
+ CHECK_LYD_STRING_PARAM(tree,
+ "<l xmlns=\"urn:tests:f\">value</l>\n"
+ "<d xmlns=\"urn:tests:f\">15</d>\n"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1>def3</ll1>\n"
+ " <d>5</d>\n"
+ " <ll2>non-dflt</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WITHSIBLINGS);
+
+ /* check diff */
+ CHECK_LYD_STRING_PARAM(diff,
+ "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ll1 yang:operation=\"delete\">def1</ll1>\n"
+ " <ll1 yang:operation=\"delete\">def2</ll1>\n"
+ " <ll1 yang:operation=\"delete\">def3</ll1>\n"
+ " <d yang:operation=\"delete\">15</d>\n"
+ " <ll2 yang:operation=\"delete\">dflt1</ll2>\n"
+ " <ll2 yang:operation=\"delete\">dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(diff);
+ lyd_free_all(tree);
+
+ /* check data tree - when enabled node */
+ CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:f\">666</d><cont xmlns=\"urn:tests:f\"><d>666</d></cont>",
+ LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_STRING_PARAM(tree,
+ "<ll1 xmlns=\"urn:tests:f\">def1</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\">def2</ll1>\n"
+ "<ll1 xmlns=\"urn:tests:f\">def3</ll1>\n"
+ "<d xmlns=\"urn:tests:f\">666</d>\n"
+ "<dd xmlns=\"urn:tests:f\">15</dd>\n"
+ "<ll2 xmlns=\"urn:tests:f\">dflt1</ll2>\n"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n"
+ "<cont xmlns=\"urn:tests:f\">\n"
+ " <ll1>def1</ll1>\n"
+ " <ll1>def2</ll1>\n"
+ " <ll1>def3</ll1>\n"
+ " <d>666</d>\n"
+ " <dd>15</dd>\n"
+ " <ll2>dflt1</ll2>\n"
+ " <ll2>dflt2</ll2>\n"
+ "</cont>\n",
+ LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(tree);
+}
+
+static void
+test_state(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ const char *schema =
+ "module h {\n"
+ " namespace urn:tests:h;\n"
+ " prefix h;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container cont {\n"
+ " container cont2 {\n"
+ " config false;\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ data = "<cont xmlns=\"urn:tests:h\">\n"
+ " <cont2>\n"
+ " <l>val</l>\n"
+ " </cont2>\n"
+ "</cont>\n";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_NO_STATE, 0, LY_EVALID, tree);
+ CHECK_LOG_CTX("Unexpected data state node \"cont2\" found.",
+ "Data location \"/h:cont/cont2\", line number 3.");
+
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+ assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_NO_STATE, NULL));
+ CHECK_LOG_CTX("Unexpected data state node \"cont2\" found.",
+ "Data location \"/h:cont/cont2\".");
+ lyd_free_all(tree);
+}
+
+static void
+test_must(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module i {\n"
+ " namespace urn:tests:i;\n"
+ " prefix i;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container cont {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l2 {\n"
+ " must \"../l = 'right'\";\n"
+ " type string;\n"
+ " }\n"
+ " leaf l3 {\n"
+ " must \"../l = 'left'\" {\n"
+ " error-app-tag \"not-left\";\n"
+ " error-message \"l leaf is not left\";\n"
+ " }\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:i\">\n"
+ " <l>wrong</l>\n"
+ " <l2>val</l2>\n"
+ "</cont>\n", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.",
+ "Data location \"/i:cont/l2\".", "must-violation");
+
+ LYD_TREE_CREATE("<cont xmlns=\"urn:tests:i\">\n"
+ " <l>right</l>\n"
+ " <l2>val</l2>\n"
+ "</cont>\n", tree);
+ lyd_free_all(tree);
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:i\">\n"
+ " <l>wrong</l>\n"
+ " <l3>val</l3>\n"
+ "</cont>\n", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/i:cont/l3\".", "not-left");
+}
+
+const char *schema_j =
+ "module j {\n"
+ " namespace urn:tests:j;\n"
+ " prefix j;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " feature feat1;\n"
+ "\n"
+ " container cont {\n"
+ " must \"false()\";\n"
+ " list l1 {\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " action act {\n"
+ " if-feature feat1;\n"
+ " input {\n"
+ " must \"../../lf1 = 'true'\";\n"
+ " leaf lf2 {\n"
+ " type leafref {\n"
+ " path /lf3;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " must \"../../lf1 = 'true2'\";\n"
+ " leaf lf2 {\n"
+ " type leafref {\n"
+ " path /lf4;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf lf1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf lf3 {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf lf4 {\n"
+ " type string;\n"
+ " }\n"
+ "}";
+const char *feats_j[] = {"feat1", NULL};
+
+static void
+test_action(void **state)
+{
+ struct ly_in *in;
+ struct lyd_node *tree, *op_tree;
+
+ UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(
+ "<cont xmlns=\"urn:tests:j\">\n"
+ " <l1>\n"
+ " <k>val1</k>\n"
+ " <act>\n"
+ " <lf2>target</lf2>\n"
+ " </act>\n"
+ " </l1>\n"
+ "</cont>\n", &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &op_tree, NULL));
+ assert_non_null(op_tree);
+
+ /* missing leafref */
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_RPC_YANG, NULL));
+ CHECK_LOG_CTX("Invalid leafref value \"target\" - no target instance \"/lf3\" with the same value.",
+ "Data location \"/j:cont/l1[k='val1']/act/lf2\".");
+ ly_in_free(in, 0);
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n"
+ " <lf1>not true</lf1>\n"
+ "</cont>\n"
+ "<lf3 xmlns=\"urn:tests:j\">target</lf3>\n",
+ LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+
+ /* input must false */
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_RPC_YANG, NULL));
+ CHECK_LOG_CTX("Must condition \"../../lf1 = 'true'\" not satisfied.",
+ "Data location \"/j:cont/l1[k='val1']/act\".");
+
+ lyd_free_all(tree);
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n"
+ " <lf1>true</lf1>\n"
+ "</cont>\n"
+ "<lf3 xmlns=\"urn:tests:j\">target</lf3>\n",
+ LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+
+ /* success */
+ assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_RPC_YANG, NULL));
+
+ lyd_free_tree(op_tree);
+ lyd_free_siblings(tree);
+}
+
+static void
+test_rpc(void **state)
+{
+ const char *schema, *data;
+ struct ly_in *in;
+ struct lyd_node *tree;
+
+ /* Testing constraint violation in RPC. */
+ schema =
+ "module val-str {\n"
+ " namespace \"urn:vstr\";\n"
+ " prefix v;\n"
+ "\n"
+ " rpc modify-user-password {\n"
+ " input {\n"
+ " leaf old-password {\n"
+ " type string {\n"
+ " length \"4..8\";\n"
+ " }\n"
+ " }\n"
+ " leaf new-password {\n"
+ " type string {\n"
+ " length \"4..8\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data =
+ "<modify-user-password xmlns=\"urn:vstr\">\n"
+ " <old-password>12345</old-password>\n"
+ " <new-password>123</new-password>\n"
+ "</modify-user-password>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL));
+ CHECK_LOG_CTX("Unsatisfied length - string \"123\" length is not allowed.",
+ "Data location \"/val-str:modify-user-password/new-password\", line number 3.");
+ ly_in_free(in, 0);
+}
+
+static void
+test_reply(void **state)
+{
+ struct ly_in *in;
+ struct lyd_node *tree, *op_tree;
+
+ UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(
+ "<cont xmlns=\"urn:tests:j\">\n"
+ " <l1>\n"
+ " <k>val1</k>\n"
+ " <act>\n"
+ " <lf2>target</lf2>\n"
+ " </act>\n"
+ " </l1>\n"
+ "</cont>\n", &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &op_tree, NULL));
+ assert_non_null(op_tree);
+ ly_in_free(in, 0);
+
+ /* missing leafref */
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_REPLY_YANG, NULL));
+ CHECK_LOG_CTX("Invalid leafref value \"target\" - no target instance \"/lf4\" with the same value.",
+ "Data location \"/j:cont/l1[k='val1']/act/lf2\".");
+
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n"
+ " <lf1>not true</lf1>\n"
+ "</cont>\n"
+ "<lf4 xmlns=\"urn:tests:j\">target</lf4>\n",
+ LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+
+ /* input must false */
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_REPLY_YANG, NULL));
+ CHECK_LOG_CTX("Must condition \"../../lf1 = 'true2'\" not satisfied.", "Data location \"/j:cont/l1[k='val1']/act\".");
+
+ lyd_free_all(tree);
+ CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n"
+ " <lf1>true2</lf1>\n"
+ "</cont>\n"
+ "<lf4 xmlns=\"urn:tests:j\">target</lf4>\n",
+ LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+
+ /* success */
+ assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_REPLY_YANG, NULL));
+
+ lyd_free_tree(op_tree);
+ lyd_free_all(tree);
+}
+
+static void
+test_case(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module k {\n"
+ " namespace urn:tests:k;\n"
+ " prefix k;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container ch {\n"
+ " choice a0 {\n"
+ " case v0 {\n"
+ " leaf g0 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case v1 {\n"
+ " choice a1 {\n"
+ " case r0 {\n"
+ " leaf g1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case r1 {\n"
+ " leaf g2 {\n"
+ " type string;\n"
+ " }\n"
+ " leaf g3 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case r2 {\n"
+ " leaf g4 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " case v2 {\n"
+ " choice a2 {\n"
+ " case y0 {\n"
+ " leaf g5 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case y1 {\n"
+ " leaf g6 {\n"
+ " type string;\n"
+ " }\n"
+ " leaf g7 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " case y2 {\n"
+ " leaf g8 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ CHECK_PARSE_LYD_PARAM(
+ "{\n"
+ " \"k:ch\": {\n"
+ " \"g0\": \"value_g0\",\n"
+ " \"g7\": \"value_g7\"\n"
+ " }\n"
+ "}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.",
+ "Data location \"/k:ch\", line number 5.");
+
+ CHECK_PARSE_LYD_PARAM(
+ "{\n"
+ " \"k:ch\": {\n"
+ " \"g7\": \"value_g7\",\n"
+ " \"g0\": \"value_g0\"\n"
+ " }\n"
+ "}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.",
+ "Data location \"/k:ch\", line number 5.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_when),
+ UTEST(test_mandatory),
+ UTEST(test_mandatory_when),
+ UTEST(test_type_incomplete_when),
+ UTEST(test_minmax),
+ UTEST(test_unique),
+ UTEST(test_unique_nested),
+ UTEST(test_dup),
+ UTEST(test_defaults),
+ UTEST(test_state),
+ UTEST(test_must),
+ UTEST(test_action),
+ UTEST(test_rpc),
+ UTEST(test_reply),
+ UTEST(test_case),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/extensions/test_metadata.c b/tests/utests/extensions/test_metadata.c
new file mode 100644
index 0000000..39d29be
--- /dev/null
+++ b/tests/utests/extensions/test_metadata.c
@@ -0,0 +1,211 @@
+/**
+ * @file test_metadata.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for Metadata extension (annotation) support
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+#include "plugins_exts.h"
+#include "plugins_exts/metadata.h"
+
+static void
+test_yang(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ext_instance *e;
+ const char *units;
+
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:metadata:a; prefix a;"
+ "import ietf-yang-metadata {prefix md;}"
+ "feature f;"
+ "md:annotation x {"
+ " description \"test\";"
+ " if-feature f;"
+ " reference \"test\";"
+ " status \"current\";"
+ " type uint8;"
+ " units meters;"
+ "}}";
+ const char *feats[] = {"f", NULL};
+
+ UTEST_ADD_MODULE(data, LYS_IN_YANG, feats, &mod);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts));
+ e = &mod->compiled->exts[0];
+ assert_non_null(e->compiled);
+ assert_non_null(e->substmts);
+ lyplg_ext_get_storage(e, LY_STMT_UNITS, sizeof units, (const void **)&units);
+ assert_string_equal("meters", units);
+
+ /* invalid */
+ /* missing mandatory type substatement */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+ "import ietf-yang-metadata {prefix md;}"
+ "md:annotation aa;}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* not allowed substatement */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+ "import ietf-yang-metadata {prefix md;}"
+ "md:annotation aa {default x;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* invalid cardinality of units substatement */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+ "import ietf-yang-metadata {prefix md;}"
+ "md:annotation aa {type string; units x; units y;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate keyword \"units\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* invalid cardinality of status substatement */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+ "import ietf-yang-metadata {prefix md;}"
+ "md:annotation aa {type string; status current; status obsolete;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate keyword \"status\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* invalid cardinality of status substatement */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+ "import ietf-yang-metadata {prefix md;}"
+ "md:annotation aa {type string; type uint8;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate keyword \"type\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* duplication of the same annotation */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+ "import ietf-yang-metadata {prefix md;}"
+ "md:annotation aa {type string;} md:annotation aa {type uint8;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.",
+ "/aa:{extension='md:annotation'}/aa");
+}
+
+static void
+test_yin(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ext_instance *e;
+ const char *data, *units;
+
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"a\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:a\"/><prefix value=\"a\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<feature name=\"f\"/>\n"
+ "<md:annotation name=\"x\">\n"
+ " <description><text>test</text></description>\n"
+ " <reference><text>test</text></reference>\n"
+ " <if-feature name=\"f\"/>\n"
+ " <status value=\"current\"/>\n"
+ " <type name=\"uint8\"/>\n"
+ " <units name=\"meters\"/>\n"
+ "</md:annotation></module>";
+ const char *feats[] = {"f", NULL};
+
+ UTEST_ADD_MODULE(data, LYS_IN_YIN, feats, &mod);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts));
+ e = &mod->compiled->exts[0];
+ assert_non_null(e->compiled);
+ assert_non_null(e->substmts);
+ lyplg_ext_get_storage(e, LY_STMT_UNITS, sizeof units, (const void **)&units);
+ assert_string_equal("meters", units);
+
+ /* invalid */
+ /* missing mandatory type substatement */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<md:annotation name=\"aa\"/>\n"
+ "</module>";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* not allowed substatement */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<md:annotation name=\"aa\">\n"
+ " <default value=\"x\"/>\n"
+ "</md:annotation></module>";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* invalid cardinality of units substatement */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<md:annotation name=\"aa\">\n"
+ " <type name=\"string\"/>\n"
+ " <units name=\"x\"/>\n"
+ " <units name=\"y\"/>\n"
+ "</md:annotation></module>";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Duplicate keyword \"units\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* invalid cardinality of status substatement */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<md:annotation name=\"aa\">\n"
+ " <type name=\"string\"/>\n"
+ " <status value=\"current\"/>\n"
+ " <status value=\"obsolete\"/>\n"
+ "</md:annotation></module>";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Duplicate keyword \"status\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* invalid cardinality of status substatement */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<md:annotation name=\"aa\">\n"
+ " <type name=\"string\"/>\n"
+ " <type name=\"uint8\"/>\n"
+ "</md:annotation></module>";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Duplicate keyword \"type\".",
+ "/aa:{extension='md:annotation'}/aa");
+
+ /* duplication of the same annotation */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
+ "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n"
+ "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n"
+ "<md:annotation name=\"aa\">\n"
+ " <type name=\"string\"/>\n"
+ "</md:annotation><md:annotation name=\"aa\">\n"
+ " <type name=\"uint8\"/>\n"
+ "</md:annotation></module>";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.",
+ "/aa:{extension='md:annotation'}/aa");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_yang),
+ UTEST(test_yin),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/extensions/test_nacm.c b/tests/utests/extensions/test_nacm.c
new file mode 100644
index 0000000..1c999fb
--- /dev/null
+++ b/tests/utests/extensions/test_nacm.c
@@ -0,0 +1,124 @@
+/*
+ * @file test_nacm.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for NACM extensions support
+ *
+ * Copyright (c) 2019-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ 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-netconf-acm", "2018-02-14", NULL));
+
+ return 0;
+}
+
+static void
+test_deny_all(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_container *cont;
+ struct lysc_node_leaf *leaf;
+ struct lysc_ext_instance *e;
+
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "container a { nacm:default-deny-all; leaf aa {type string;}}"
+ "leaf b {type string;}}";
+
+ /* valid data */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod));
+ assert_non_null(cont = (struct lysc_node_container *)mod->compiled->data);
+ assert_non_null(leaf = (struct lysc_node_leaf *)cont->child);
+ assert_non_null(e = &cont->exts[0]);
+ assert_int_equal(LY_ARRAY_COUNT(cont->exts), 1);
+ assert_int_equal(LY_ARRAY_COUNT(leaf->exts), 1); /* NACM extensions inherit */
+ assert_ptr_equal(e->def, leaf->exts[0].def);
+ assert_int_equal(1, *((uint8_t *)e->compiled)); /* plugin's value for default-deny-all */
+ assert_null(cont->next->exts);
+
+ /* ignored - valid with warning */
+ data = "module b {yang-version 1.1; namespace urn:tests:extensions:nacm:b; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "nacm:default-deny-all;}";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
+ "Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.",
+ "/b:{extension='nacm:default-deny-all'}");
+
+ /* invalid */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "leaf l { type string; nacm:default-deny-all; nacm:default-deny-write;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
+ "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.",
+ "/aa:l/{extension='nacm:default-deny-all'}");
+}
+
+static void
+test_deny_write(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_container *cont;
+ struct lysc_node_leaf *leaf;
+ struct lysc_ext_instance *e;
+
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "container a { nacm:default-deny-write; leaf aa {type string;}}"
+ "leaf b {type string;}}";
+
+ /* valid data */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod));
+ assert_non_null(cont = (struct lysc_node_container *)mod->compiled->data);
+ assert_non_null(leaf = (struct lysc_node_leaf *)cont->child);
+ assert_non_null(e = &cont->exts[0]);
+ assert_int_equal(LY_ARRAY_COUNT(cont->exts), 1);
+ assert_int_equal(LY_ARRAY_COUNT(leaf->exts), 1); /* NACM extensions inherit */
+ assert_ptr_equal(e->def, leaf->exts[0].def);
+ assert_int_equal(2, *((uint8_t *)e->compiled)); /* plugin's value for default-deny-write */
+
+ /* ignored - valid with warning */
+ data = "module b {yang-version 1.1; namespace urn:tests:extensions:nacm:b; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "notification notif {nacm:default-deny-write;}}";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
+ "Extension nacm:default-deny-write is not allowed in notification statement.",
+ "/b:notif/{extension='nacm:default-deny-write'}");
+
+ /* invalid */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "leaf l { type string; nacm:default-deny-write; nacm:default-deny-write;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
+ "Extension nacm:default-deny-write is instantiated multiple times.",
+ "/aa:l/{extension='nacm:default-deny-write'}");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_deny_all, setup),
+ UTEST(test_deny_write, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/extensions/test_schema_mount.c b/tests/utests/extensions/test_schema_mount.c
new file mode 100644
index 0000000..be879ec
--- /dev/null
+++ b/tests/utests/extensions/test_schema_mount.c
@@ -0,0 +1,1566 @@
+/**
+ * @file test_schema_mount.c
+ * @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for Schema Mount extension support
+ *
+ * Copyright (c) 2021 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+void **glob_state;
+
+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_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL));
+
+ return 0;
+}
+
+static void
+test_schema(void **state)
+{
+ struct lys_module *mod;
+ const char *schema;
+ char *str;
+
+ /* invalid */
+ schema =
+ "module sm {\n"
+ " namespace \"urn:sm\";\n"
+ " prefix sm;\n"
+ "\n"
+ " import ietf-yang-schema-mount {\n"
+ " prefix yangmnt;\n"
+ " }\n"
+ "\n"
+ " container root {\n"
+ " yangmnt:mount-point \"root\";\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Extension \"yangmnt:mount-point\" instance not allowed in YANG version 1 module.",
+ "/sm:root/{extension='yangmnt:mount-point'}/root");
+
+ schema =
+ "module sm {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:sm\";\n"
+ " prefix sm;\n"
+ "\n"
+ " import ietf-yang-schema-mount {\n"
+ " prefix yangmnt;\n"
+ " }\n"
+ "\n"
+ " yangmnt:mount-point \"root\";\n"
+ "}\n";
+ assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.",
+ "/sm:{extension='yangmnt:mount-point'}/root");
+
+ schema =
+ "module sm {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:sm\";\n"
+ " prefix sm;\n"
+ "\n"
+ " import ietf-yang-schema-mount {\n"
+ " prefix yangmnt;\n"
+ " }\n"
+ "\n"
+ " container root {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " yangmnt:mount-point \"root\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.",
+ "/sm:root/l/{extension='yangmnt:mount-point'}/root");
+
+ schema =
+ "module sm {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:sm\";\n"
+ " prefix sm;\n"
+ "\n"
+ " import ietf-yang-schema-mount {\n"
+ " prefix yangmnt;\n"
+ " }\n"
+ "\n"
+ " list l {\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " yangmnt:mount-point \"root\";\n"
+ " yangmnt:mount-point \"root2\";\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Multiple extension \"yangmnt:mount-point\" instances.",
+ "/sm:l/{extension='yangmnt:mount-point'}/root");
+
+ /* valid */
+ schema =
+ "module sm {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:sm\";\n"
+ " prefix sm;\n"
+ "\n"
+ " import ietf-yang-schema-mount {\n"
+ " prefix yangmnt;\n"
+ " }\n"
+ "\n"
+ " container root {\n"
+ " yangmnt:mount-point \"root\";\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, &mod));
+ lys_print_mem(&str, mod, LYS_OUT_YANG, 0);
+ assert_string_equal(str, schema);
+ free(str);
+}
+
+static LY_ERR
+test_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ void **state = glob_state;
+ struct lyd_node *data = NULL;
+
+ (void)ext;
+
+ if (user_data) {
+ CHECK_PARSE_LYD_PARAM(user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
+static void
+test_parse_invalid(void **state)
+{
+ const char *xml, *json;
+ struct lyd_node *data;
+
+ /* no callback set */
+ xml =
+ "<root xmlns=\"urn:sm\">"
+ " <unknown xmlns=\"unknown\">"
+ " <interface>"
+ " <name>bu</name>"
+ " <type xmlns:ii=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ii:ethernetCsmacd</type>"
+ " </interface>"
+ " </unknown>"
+ "</root>";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EINVAL, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Failed to get extension data, no callback set.",
+ NULL);
+
+ json =
+ "{"
+ " \"sm:root\": {"
+ " \"unknown:unknown\": {"
+ " \"interface\": ["
+ " {"
+ " \"name\": \"bu\","
+ " \"type\": \"iana-if-type:ethernetCsmacd\""
+ " }"
+ " ]"
+ " }"
+ " }"
+ "}";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EINVAL, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Failed to get extension data, no callback set.",
+ NULL);
+
+ /* unknown data */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, NULL);
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ assert_string_equal(LYD_NAME(data), "root");
+ assert_null(lyd_child(data));
+ assert_non_null(data->next);
+ assert_true(data->next->flags & LYD_DEFAULT);
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("No module with namespace \"unknown\" in the context.",
+ "Data location \"/sm:root\", line number 1.");
+
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ assert_string_equal(LYD_NAME(data), "root");
+ assert_null(lyd_child(data));
+ assert_non_null(data->next);
+ assert_true(data->next->flags & LYD_DEFAULT);
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("No module named \"unknown\" in the context.",
+ "Data location \"/sm:root\", line number 1.");
+
+ /* missing required callback data */
+ xml =
+ "<root xmlns=\"urn:sm\">"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">"
+ " <interface>"
+ " <name>bu</name>"
+ " </interface>"
+ " </interfaces>"
+ "</root>";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.",
+ "Data location \"/sm:root\", line number 1.");
+
+ json =
+ "{"
+ " \"sm:root\": {"
+ " \"ietf-interfaces:interfaces\": {"
+ " \"interface\": ["
+ " {"
+ " \"name\": \"bu\""
+ " }"
+ " ]"
+ " }"
+ " }"
+ "}";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.",
+ "Data location \"/sm:root\", line number 1.");
+
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>");
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.",
+ "Data location \"/sm:root\", line number 1.");
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.",
+ "Data location \"/sm:root\", line number 1.");
+
+ /* missing module in yang-library data */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <inline/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.",
+ "Data location \"/sm:root\", line number 1.");
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.",
+ "Data location \"/sm:root\", line number 1.");
+
+ /* callback data correct, invalid YANG data */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <inline/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Mandatory node \"type\" instance does not exist.",
+ "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Mandatory node \"type\" instance does not exist.",
+ "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+
+ /* same validation fail in separate validation */
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data);
+ assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Mandatory node \"type\" instance does not exist.",
+ "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data);
+ assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Mandatory node \"type\" instance does not exist.",
+ "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ lyd_free_siblings(data);
+
+ /* success */
+ xml =
+ "<root xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ "</root>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+
+ json =
+ "{\n"
+ " \"sm:root\": {\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+}
+
+static void
+test_parse_inline(void **state)
+{
+ const char *xml, *json;
+ char *lyb;
+ struct lyd_node *data;
+ const struct ly_ctx *ext_ctx;
+
+ /* valid */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <inline/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <oper-status>not-present</oper-status>\n"
+ " <statistics>\n"
+ " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n"
+ " </statistics>\n"
+ " </interface>\n"
+ " </interfaces-state>\n"
+ "</root>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ ext_ctx = LYD_CTX(lyd_child(data));
+ lyd_free_siblings(data);
+
+ json =
+ "{\n"
+ " \"sm:root\": {\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"ietf-interfaces:interfaces-state\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\",\n"
+ " \"oper-status\": \"not-present\",\n"
+ " \"statistics\": {\n"
+ " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data)));
+ lyd_free_siblings(data);
+
+ /* different yang-lib data with the same content-id */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-ip</name>"
+ " <revision>2014-06-16</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <inline/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_ptr_not_equal(ext_ctx, LYD_CTX(lyd_child(data)));
+ ext_ctx = LYD_CTX(lyd_child(data));
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data)));
+
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, 0));
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data)));
+ free(lyb);
+ lyd_free_siblings(data);
+}
+
+static void
+test_parse_shared(void **state)
+{
+ const char *xml, *json;
+ char *lyb;
+ struct lyd_node *data;
+
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <shared-schema/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <oper-status>not-present</oper-status>\n"
+ " <statistics>\n"
+ " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n"
+ " </statistics>\n"
+ " </interface>\n"
+ " </interfaces-state>\n"
+ "</root>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+
+ json =
+ "{\n"
+ " \"sm:root\": {\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"ietf-interfaces:interfaces-state\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\",\n"
+ " \"oper-status\": \"not-present\",\n"
+ " \"statistics\": {\n"
+ " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+
+ /* different yang-lib data */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-ip</name>"
+ " <revision>2014-06-16</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>2</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <shared-schema/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root2 xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <oper-status>not-present</oper-status>\n"
+ " <statistics>\n"
+ " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n"
+ " </statistics>\n"
+ " </interface>\n"
+ " </interfaces-state>\n"
+ "</root2>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Shared-schema yang-library content-id \"2\" differs from \"1\" used previously.",
+ "/ietf-yang-library:yang-library/content-id");
+
+ /* data for 2 mount points */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <shared-schema/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <oper-status>not-present</oper-status>\n"
+ " <statistics>\n"
+ " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n"
+ " </statistics>\n"
+ " </interface>\n"
+ " </interfaces-state>\n"
+ "</root>\n"
+ "<root2 xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>fu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:fddi</type>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>fu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:fddi</type>\n"
+ " <oper-status>down</oper-status>\n"
+ " <statistics>\n"
+ " <discontinuity-time>2020-01-01T10:00:00-00:00</discontinuity-time>\n"
+ " </statistics>\n"
+ " </interface>\n"
+ " </interfaces-state>\n"
+ "</root2>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+
+ json =
+ "{\n"
+ " \"sm:root\": {\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"ietf-interfaces:interfaces-state\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\",\n"
+ " \"oper-status\": \"not-present\",\n"
+ " \"statistics\": {\n"
+ " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"sm:root2\": {\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"fu\",\n"
+ " \"type\": \"iana-if-type:fddi\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"ietf-interfaces:interfaces-state\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"fu\",\n"
+ " \"type\": \"iana-if-type:fddi\",\n"
+ " \"oper-status\": \"down\",\n"
+ " \"statistics\": {\n"
+ " \"discontinuity-time\": \"2020-01-01T10:00:00-00:00\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, LYD_PRINT_WITHSIBLINGS));
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ free(lyb);
+ lyd_free_siblings(data);
+}
+
+static void
+test_parse_shared_parent_ref(void **state)
+{
+ const char *xml, *json;
+ struct lyd_node *data;
+
+ /* wrong leafref value */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>sm</name>"
+ " <namespace>urn:sm</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <namespace>"
+ " <prefix>smp</prefix>"
+ " <uri>urn:sm</uri>"
+ " </namespace>"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>mnt-root</label>"
+ " <shared-schema>"
+ " <parent-reference>/smp:target[. = current()/smp:name]</parent-reference>"
+ " </shared-schema>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root3 xmlns=\"urn:sm\">\n"
+ " <ls>\n"
+ " <name>target-value</name>\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <sm-name xmlns=\"urn:sm\">target-value</sm-name>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " </ls>\n"
+ "</root3>\n"
+ "<target xmlns=\"urn:sm\">wrong-target-value</target>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Invalid leafref value \"target-value\" - no target instance \"/sm:target\" with the same value.",
+ "Data location \"/ietf-interfaces:interfaces/interface[name='bu']/sm:sm-name\".");
+
+ json =
+ "{\n"
+ " \"sm:root3\": {\n"
+ " \"ls\": ["
+ " {\n"
+ " \"name\": \"target-value\",\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\",\n"
+ " \"sm:sm-name\": \"target-value\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"sm:target\": \"wrong-target-value\"\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
+ CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
+ "Invalid leafref value \"target-value\" - no target instance \"/sm:target\" with the same value.",
+ "Data location \"/ietf-interfaces:interfaces/interface[name='bu']/sm:sm-name\".");
+
+ /* success */
+ xml =
+ "<root3 xmlns=\"urn:sm\">\n"
+ " <ls>\n"
+ " <name>target-value</name>\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <sm-name xmlns=\"urn:sm\">target-value</sm-name>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " </ls>\n"
+ "</root3>\n"
+ "<target xmlns=\"urn:sm\">target-value</target>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+
+ json =
+ "{\n"
+ " \"sm:root3\": {\n"
+ " \"ls\": [\n"
+ " {\n"
+ " \"name\": \"target-value\",\n"
+ " \"ietf-interfaces:interfaces\": {\n"
+ " \"interface\": [\n"
+ " {\n"
+ " \"name\": \"bu\",\n"
+ " \"type\": \"iana-if-type:ethernetCsmacd\",\n"
+ " \"sm:sm-name\": \"target-value\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"sm:target\": \"target-value\"\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+}
+
+static void
+test_parse_config(void **state)
+{
+ const char *xml;
+ char *lyb;
+ struct lyd_node *data;
+ const struct lyd_node *node;
+
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <config>false</config>"
+ " <inline/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <enabled>true</enabled>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ "</root>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+
+ node = lyd_child(data);
+ assert_string_equal(LYD_NAME(node), "interfaces");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = lyd_child(node);
+ assert_string_equal(LYD_NAME(node), "interface");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = lyd_child(node);
+ assert_string_equal(LYD_NAME(node), "name");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = node->next;
+ assert_string_equal(LYD_NAME(node), "type");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+
+ lyd_print_mem(&lyb, data, LYD_LYB, 0);
+ lyd_free_siblings(data);
+ CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ free(lyb);
+
+ node = lyd_child(data);
+ assert_string_equal(LYD_NAME(node), "interfaces");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = lyd_child(node);
+ assert_string_equal(LYD_NAME(node), "interface");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = lyd_child(node);
+ assert_string_equal(LYD_NAME(node), "name");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = node->next;
+ assert_string_equal(LYD_NAME(node), "type");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+
+ lyd_free_siblings(data);
+
+ /* the same effect but use a config false mount point instead of the separate metadata node */
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <inline/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root4 xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <enabled>true</enabled>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ "</root4>\n";
+ CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+
+ node = lyd_child(data->next->next->next);
+ assert_string_equal(LYD_NAME(node), "interfaces");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = lyd_child(node);
+ assert_string_equal(LYD_NAME(node), "interface");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = lyd_child(node);
+ assert_string_equal(LYD_NAME(node), "name");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+ node = node->next;
+ assert_string_equal(LYD_NAME(node), "type");
+ assert_true(node->schema->flags & LYS_CONFIG_R);
+
+ lyd_free_siblings(data);
+}
+
+static void
+test_new(void **state)
+{
+ const char *xml;
+ const struct lys_module *mod;
+ struct lyd_node *data, *node;
+
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-ip</name>"
+ " <revision>2014-06-16</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-ip</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <shared-schema/>"
+ " </mount-point>"
+ "</schema-mounts>");
+ xml =
+ "<root xmlns=\"urn:sm\">\n"
+ " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n"
+ " <enabled>false</enabled>\n"
+ " </ipv4>\n"
+ " </interface>\n"
+ " </interfaces>\n"
+ " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
+ " <interface>\n"
+ " <name>bu</name>\n"
+ " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
+ " <oper-status>not-present</oper-status>\n"
+ " <statistics>\n"
+ " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n"
+ " </statistics>\n"
+ " </interface>\n"
+ " </interfaces-state>\n"
+ "</root>\n";
+
+ /* create the data manually with simple new functions */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "sm");
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "root", 0, &data));
+
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-interfaces");
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lyd_new_inner(data, mod, "interfaces", 0, &node));
+ assert_int_equal(LY_SUCCESS, lyd_new_list(node, NULL, "interface", 0, &node, "bu"));
+ assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "type", "iana-if-type:ethernetCsmacd", 0, NULL));
+ mod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-ip");
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lyd_new_inner(node, mod, "ipv4", 0, &node));
+ assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "enabled", "false", 0, NULL));
+
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-interfaces");
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lyd_new_inner(data, mod, "interfaces-state", 0, &node));
+ assert_int_equal(LY_SUCCESS, lyd_new_list(node, NULL, "interface", 0, &node, "bu"));
+ assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "type", "iana-if-type:ethernetCsmacd", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "oper-status", "not-present", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_inner(node, NULL, "statistics", 0, &node));
+ assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "discontinuity-time", "2022-01-01T10:00:00-00:00", 0, NULL));
+
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+
+ /* create the data using lyd_new_path */
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX,
+ "/sm:root/ietf-interfaces:interfaces/interface[name='bu']/type", "iana-if-type:ethernetCsmacd", 0, &data));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL,
+ "/sm:root/ietf-interfaces:interfaces/interface[name='bu']/ietf-ip:ipv4/enabled", "false", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL,
+ "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/type", "iana-if-type:ethernetCsmacd", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL,
+ "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/oper-status", "not-present", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL,
+ "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/statistics/discontinuity-time",
+ "2022-01-01T10:00:00-00:00", 0, NULL));
+
+ CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_siblings(data);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema),
+ UTEST(test_parse_invalid, setup),
+ UTEST(test_parse_inline, setup),
+ UTEST(test_parse_shared, setup),
+ UTEST(test_parse_shared_parent_ref, setup),
+ UTEST(test_parse_config, setup),
+ UTEST(test_new, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/extensions/test_structure.c b/tests/utests/extensions/test_structure.c
new file mode 100644
index 0000000..23af450
--- /dev/null
+++ b/tests/utests/extensions/test_structure.c
@@ -0,0 +1,255 @@
+/**
+ * @file test_structure.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for structure extensions support
+ *
+ * Copyright (c) 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+static void
+test_schema(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ext_instance *e;
+ char *printed = NULL;
+ const char *data, *info;
+
+ /* valid data */
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct {"
+ " must \"/n2/l\";"
+ " status deprecated;"
+ " description desc;"
+ " reference no-ref;"
+ " typedef my-type {type string;}"
+ " grouping my-grp {leaf gl {type my-type;}}"
+ " container n1 {leaf l {config false; type uint32;}}"
+ " list n2 {leaf l {type leafref {path /n1/l;}}}"
+ " uses my-grp;"
+ "}}";
+
+ UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(e = mod->compiled->exts);
+ assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1);
+
+ /* valid augment data */
+ data = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "import a {prefix a;}"
+ "sx:augment-structure \"/a:struct/a:n1\" {"
+ " status obsolete;"
+ " reference none;"
+ " leaf aug-leaf {type string;}"
+ "}}";
+
+ UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(e = mod->compiled->exts);
+ assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1);
+
+ /* yang compiled print */
+ info = "module a {\n"
+ " namespace \"urn:tests:extensions:structure:a\";\n"
+ " prefix a;\n"
+ "\n"
+ " ietf-yang-structure-ext:structure \"struct\" {\n"
+ " must \"/n2/l\";\n"
+ " status deprecated;\n"
+ " description\n"
+ " \"desc\";\n"
+ " reference\n"
+ " \"no-ref\";\n"
+ " container n1 {\n"
+ " status deprecated;\n"
+ " leaf l {\n"
+ " type uint32;\n"
+ " status deprecated;\n"
+ " }\n"
+ " leaf aug-leaf {\n"
+ " type string;\n"
+ " status obsolete;\n"
+ " }\n"
+ " }\n"
+ " list n2 {\n"
+ " min-elements 0;\n"
+ " max-elements 4294967295;\n"
+ " ordered-by user;\n"
+ " status deprecated;\n"
+ " leaf l {\n"
+ " type leafref {\n"
+ " path \"/n1/l\";\n"
+ " require-instance true;\n"
+ " type uint32;\n"
+ " }\n"
+ " status deprecated;\n"
+ " }\n"
+ " }\n"
+ " leaf gl {\n"
+ " type string;\n"
+ " status deprecated;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+
+ info = "module b {\n"
+ " namespace \"urn:tests:extensions:structure:b\";\n"
+ " prefix b;\n"
+ "\n"
+ " ietf-yang-structure-ext:augment-structure \"/a:struct/a:n1\";\n"
+ "}\n";
+
+ assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b"));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+
+ /* no substatements */
+ data = "module c {yang-version 1.1; namespace urn:tests:extensions:structure:c; prefix c;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct;}";
+ info = "module c {\n"
+ " namespace \"urn:tests:extensions:structure:c\";\n"
+ " prefix c;\n"
+ "\n"
+ " ietf-yang-structure-ext:structure \"struct\";\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(e = mod->compiled->exts);
+ assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+}
+
+static void
+test_schema_invalid(void **state)
+{
+ const char *data;
+
+ /* structure */
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct {import yang;}}";
+ UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid keyword \"import\" as a child of \"sx:structure struct\" extension instance.",
+ "/a:{extension='sx:structure'}/struct");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "container b { sx:structure struct { container x { leaf x {type string;}}}}}";
+ UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": "
+ "Extension sx:structure must not be used as a non top-level statement in \"container\" statement.",
+ "/a:b/{extension='sx:structure'}/struct");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure { container x { leaf x {type string;}}}}";
+ UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Extension instance \"sx:structure\" missing argument element \"name\".", NULL);
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct { container x { leaf x {type string;}}}"
+ "sx:structure struct { container y { leaf y {type string;}}}}";
+ UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure is instantiated multiple times.",
+ "/a:{extension='sx:structure'}/struct");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct { container x { leaf x {type string;}}}"
+ "choice struct { container y { leaf y {type string;}}}}";
+ UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure collides with a choice with the same identifier.",
+ "/a:{extension='sx:structure'}/struct");
+
+ /* augment-structure */
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct {"
+ " container n1 {leaf l {config false; type uint32;}}"
+ " list n2 {leaf l {type string;}}"
+ "}"
+ "container n1 {leaf l2 {type uint8;}}}";
+ UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, NULL);
+
+ data = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "import a {prefix a;}"
+ "sx:augment-structure \"/a:n1\" {"
+ " leaf aug-leaf {type string;}"
+ "}}";
+ UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_ENOTFOUND);
+ CHECK_LOG_CTX("Augment extension target node \"/a:n1\" from module \"b\" was not found.",
+ "/b:{extension='sx:augment-structure'}/{augment='/a:n1'}");
+}
+
+static void
+test_parse(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ext_instance *e;
+ struct lyd_node *tree = NULL;
+ const char *yang;
+ const char *xml = "<x xmlns=\"urn:tests:extensions:structure:a\">"
+ "<x>test</x>"
+ "<x2 xmlns=\"urn:tests:extensions:structure:b\">25</x2>"
+ "</x>";
+ const char *json = "{\"a:x\":{\"x\":\"test\",\"b:x2\":25}}";
+
+ yang = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "sx:structure struct { container x { leaf x { type string;}}}}";
+ UTEST_ADD_MODULE(yang, LYS_IN_YANG, NULL, &mod);
+
+ yang = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;"
+ "import ietf-yang-structure-ext {prefix sx;}"
+ "import a {prefix a;}"
+ "sx:augment-structure \"/a:struct/a:x\" {"
+ " leaf x2 {type uint32;}"
+ "}}";
+ UTEST_ADD_MODULE(yang, LYS_IN_YANG, NULL, NULL);
+
+ /* get extension after recompilation */
+ assert_non_null(e = mod->compiled->exts);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(xml, &UTEST_IN));
+ assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LYD_STRING_PARAM(tree, xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(tree);
+
+ ly_in_memory(UTEST_IN, json);
+ assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LYD_STRING_PARAM(tree, json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema),
+ UTEST(test_schema_invalid),
+ UTEST(test_parse),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/extensions/test_yangdata.c b/tests/utests/extensions/test_yangdata.c
new file mode 100644
index 0000000..8c0176f
--- /dev/null
+++ b/tests/utests/extensions/test_yangdata.c
@@ -0,0 +1,273 @@
+/*
+ * @file test_yangdata.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for yang-data extensions support
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "libyang.h"
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ 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));
+
+ return 0;
+}
+
+static void
+test_schema(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ext_instance *e;
+ char *printed = NULL;
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "feature x;"
+ "rc:yang-data template { container x { list l { leaf x { type string;}} leaf y {if-feature x; type string; config false;}}}}";
+ const char *info = "module a {\n"
+ " namespace \"urn:tests:extensions:yangdata:a\";\n"
+ " prefix self;\n\n"
+ " ietf-restconf:yang-data \"template\" {\n"
+ " container x {\n"
+ " status current;\n"
+ " list l {\n" /* no key */
+ " min-elements 0;\n"
+ " max-elements 4294967295;\n"
+ " ordered-by user;\n"
+ " status current;\n"
+ " leaf x {\n"
+ " type string;\n"
+ " status current;\n"
+ " }\n"
+ " }\n"
+ " leaf y {\n" /* config and if-feature are ignored */
+ " type string;\n"
+ " status current;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* valid data */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod));
+ assert_non_null(e = mod->compiled->exts);
+ assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+
+ data = "module c {yang-version 1.1; namespace urn:tests:extensions:yangdata:c; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "grouping g { choice ch { container a {presence a; config false;} container b {presence b; config true;}}}"
+ "rc:yang-data template { uses g;}}";
+ info = "module c {\n"
+ " namespace \"urn:tests:extensions:yangdata:c\";\n"
+ " prefix self;\n\n"
+ " ietf-restconf:yang-data \"template\" {\n"
+ " choice ch {\n"
+ " status current;\n"
+ " case a {\n"
+ " status current;\n"
+ " container a {\n"
+ " presence \"true\";\n"
+ " status current;\n"
+ " }\n"
+ " }\n"
+ " case b {\n"
+ " status current;\n"
+ " container b {\n"
+ " presence \"true\";\n"
+ " status current;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod));
+ assert_non_null(e = mod->compiled->exts);
+ assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+
+ /* ignored - valid with warning */
+ data = "module b {yang-version 1.1; namespace urn:tests:extensions:yangdata:b; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "container b { rc:yang-data template { container x { leaf x {type string;}}}}}";
+ info = "module b {\n"
+ " namespace \"urn:tests:extensions:yangdata:b\";\n"
+ " prefix self;\n\n"
+ " container b {\n"
+ " config true;\n"
+ " status current;\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod));
+ assert_null(mod->compiled->exts);
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is ignored since it appears as a non top-level statement in \"container\" statement.",
+ "/b:b/{extension='rc:yang-data'}/template");
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+
+ /* sama data nodes name, but not conflicting */
+ data = "module d {yang-version 1.1; namespace urn:tests:extensions:yangdata:d; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "leaf d { type string;}"
+ "rc:yang-data template1 { container d {presence d;}}"
+ "rc:yang-data template2 { container d {presence d;}}}";
+ info = "module d {\n"
+ " namespace \"urn:tests:extensions:yangdata:d\";\n"
+ " prefix self;\n\n"
+ " ietf-restconf:yang-data \"template1\" {\n"
+ " container d {\n"
+ " presence \"true\";\n"
+ " status current;\n"
+ " }\n"
+ " }\n"
+ " ietf-restconf:yang-data \"template2\" {\n"
+ " container d {\n"
+ " presence \"true\";\n"
+ " status current;\n"
+ " }\n"
+ " }\n\n"
+ " leaf d {\n"
+ " type string;\n"
+ " config true;\n"
+ " status current;\n"
+ " }\n"
+ "}\n";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod));
+ assert_non_null(e = mod->compiled->exts);
+ assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 2);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ assert_string_equal(printed, info);
+ free(printed);
+}
+
+static void
+test_schema_invalid(void **state)
+{
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template { leaf x {type string;}}}";
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid keyword \"leaf\" as a child of \"rc:yang-data template\" extension instance.",
+ "/a:{extension='rc:yang-data'}/template");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template { choice x { leaf x {type string;}}}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is instantiated with leaf top level data node (inside a choice), "
+ "but only a single container data node is allowed.",
+ "/a:{extension='rc:yang-data'}/template");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template { choice x { case x { container z {presence ppp;} leaf x {type string;}}}}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is instantiated with multiple top level data nodes (inside a single choice's case), "
+ "but only a single container data node is allowed.",
+ "/a:{extension='rc:yang-data'}/template");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template { container x { leaf x {type string;}} container y { leaf y {type string;}}}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is instantiated with multiple top level data nodes, "
+ "but only a single container data node is allowed.",
+ "/a:{extension='rc:yang-data'}/template");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template;}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is instantiated without any top level data node, "
+ "but exactly one container data node is expected.",
+ "/a:{extension='rc:yang-data'}/template");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data { container x { leaf x {type string;}}}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Extension instance \"rc:yang-data\" missing argument element \"name\".", NULL);
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template { container x { leaf x {type string;}}}"
+ "rc:yang-data template { container y { leaf y {type string;}}}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is instantiated multiple times.",
+ "/a:{extension='rc:yang-data'}/template");
+
+ data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "grouping t { leaf-list x {type string;}}"
+ "rc:yang-data template { uses t;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
+ "Extension rc:yang-data is instantiated with leaf-list top level data node, "
+ "but only a single container data node is allowed.",
+ "/a:{extension='rc:yang-data'}/template");
+}
+
+static void
+test_parse(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ext_instance *e;
+ struct lyd_node *tree = NULL;
+ const char *schema = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template { container x { leaf x { type string;}}}}";
+ const char *xml = "<x xmlns=\"urn:tests:extensions:yangdata:a\"><x>test</x></x>";
+ const char *json = "{\"a:x\":{\"x\":\"test\"}}";
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, &mod));
+ assert_non_null(e = mod->compiled->exts);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(xml, &UTEST_IN));
+ assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LYD_STRING_PARAM(tree, xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(tree);
+
+ ly_in_memory(UTEST_IN, json);
+ assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LYD_STRING_PARAM(tree, json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema, setup),
+ UTEST(test_schema_invalid, setup),
+ UTEST(test_parse, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/node/list.c b/tests/utests/node/list.c
new file mode 100644
index 0000000..8b14ece
--- /dev/null
+++ b/tests/utests/node/list.c
@@ -0,0 +1,1632 @@
+/**
+ * @file list.c
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @brief test for list node
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+
+#define LYD_TREE_CREATE(INPUT, MODEL) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL)
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+static void
+test_schema_yang(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_list *lysc_leaf;
+ struct lysc_node *lysc_node;
+
+ schema = MODULE_CREATE_YANG("T0", "list user {"
+ "key uid;"
+ "unique name;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T1", "list user {"
+ "key uid;"
+ "container name{"
+ " leaf fist {type string;}"
+ " container second{leaf sub { type int32;}}"
+ "}"
+ "leaf uid{type int32;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 0, LYS_CONTAINER, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T2", "list grup {"
+ "key \"guid\";"
+ "leaf guid{type int32;}"
+ "list users{ key name; leaf name {type string;}}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "grup", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0);
+
+ /* restriction */
+ schema = MODULE_CREATE_YANG("T3", "list grup {"
+ "key guid;"
+ "min-elements 10;"
+ "max-elements 20;"
+ "leaf guid{type int32;}"
+ "list users{ key name; leaf name {type string;}}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_ORDBY_SYSTEM, 1, \
+ "grup", 0, 0, 0, 0, 0, 1, 20, 10, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T4", "list user {"
+ "key \"uid name\";"
+ "unique name;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE | LYS_KEY, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T5", "list rule {"
+ "key \"id\";"
+ "unique \"name\";"
+ "unique \"ip port\";"
+ "leaf id{type int32;}"
+ "leaf name{type string;}"
+ "leaf ip{type string;}"
+ "leaf port{type int16;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "rule", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 2, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "id", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "ip", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ /* test error */
+ schema = MODULE_CREATE_YANG("TERR_0", "list user {"
+ "key uid;"
+ "min-elements 10;"
+ "max-elements -1;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Invalid value \"-1\" of \"max-elements\".", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERR_0", "list user {"
+ "key uid;"
+ "min-elements 10;"
+ "max-elements 4294967298;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Value \"4294967298\" is out of \"max-elements\" bounds.", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERR_0", "list user {"
+ "key uid;"
+ "min-elements 20;"
+ "max-elements 10;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "/TERR_0:user");
+
+ schema = MODULE_CREATE_YANG("TERR_0", "list user {"
+ "key uid;"
+ "min-elements -1;"
+ "max-elements 20;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Invalid value \"-1\" of \"min-elements\".", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERR_0", "list user {"
+ "key uid;"
+ "key name;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Duplicate keyword \"key\".", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("T6", "list user {"
+ "config false;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_KEYLESS | LYS_SET_CONFIG, \
+ 1, "user", 0, 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T7", "list user {"
+ "key uid;"
+ "unique name;"
+ "ordered-by user;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T8", "list user {"
+ "key uid;"
+ "unique name;"
+ "ordered-by system;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("TERROR0", "list user {"
+ "key uid;"
+ "unique name;"
+ "ordered-by systeme;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL,
+ "Invalid value \"systeme\" of \"ordered-by\".", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERROR0", "list \"\" {"
+ "key uid;"
+ "unique name;"
+ "ordered-by system;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL,
+ "Statement argument is required.", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("T9", "list user {"
+ "key uid;"
+ "unique name;"
+ "ordered-by system;"
+ "leaf uid{type int32;}"
+ "leaf name{type string;}"
+ "leaf group{type string; default \"abcd\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T10", "list user {"
+ "key uid;"
+ "leaf uid{type int32; default \"25\";}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY | LYS_SET_DFLT, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YANG("T11",
+ "typedef my_type {"
+ " type int8; default \"25\";"
+ "}"
+ "list user {"
+ " key uid;"
+ " leaf uid{type my_type;}"
+ " leaf name{type string;}"
+ " leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+}
+
+static void
+test_schema_yin(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_list *lysc_leaf;
+ struct lysc_node *lysc_node;
+
+ schema = MODULE_CREATE_YIN("T0", "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <unique tag=\"name\"/>"
+ " <leaf name=\"uid\"><type name=\"int32\"/></leaf>"
+ " <leaf name=\"name\"><type name=\"string\"/></leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/></leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("T00", "<list name=\"user\">"
+ " <key value=\"u&lt;id\"/>"
+ " <leaf name=\"uid\"><type name=\"int32\"/></leaf>"
+ " <leaf name=\"name\"><type name=\"string\"/></leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/></leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("The list's key \"u<id\" not found.", "/T00:user");
+
+ schema = MODULE_CREATE_YIN("T1", "<list name=\"user\"> "
+ " <key value=\"uid\"/>"
+ " <container name=\"name\">"
+ " <leaf name=\"fist\"> <type name=\"string\"/> </leaf>"
+ " <container name=\"second\">"
+ " <leaf name=\"sub\"> <type name=\"int32\"/></leaf>"
+ " </container>"
+ " </container>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/></leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 0, LYS_CONTAINER, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("T2", "<list name=\"grup\">"
+ "<key value=\"guid\"/>"
+ "<leaf name=\"guid\"> <type name=\"int32\"/> </leaf>"
+ "<list name=\"users\">"
+ " <key value=\"name\"/>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ "</list>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "grup", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0);
+
+ /* restriction */
+ schema = MODULE_CREATE_YIN("T3",
+ "<list name = \"grup\">"
+ " <key value=\"guid\"/>"
+ " <min-elements value=\"10\"/>"
+ " <max-elements value=\"20\"/>"
+ " <leaf name=\"guid\"> <type name=\"int32\"/> </leaf>"
+ " <list name=\"users\"> <key value=\"name\"/>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " </list>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_ORDBY_SYSTEM, 1, "grup", \
+ 0, 0, 0, 0, 0, 1, 20, 10, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("T4",
+ "<list name=\"user\">"
+ " <key value=\"uid name\"/>"
+ " <unique tag=\"name\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE | LYS_KEY, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("T5",
+ "<list name=\"rule\">"
+ " <key value=\"id\"/>"
+ " <unique tag=\"name\"/>"
+ " <unique tag=\"ip port\"/>"
+ " <leaf name=\"id\"> <type name=\"int32\"/></leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"ip\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"port\"> <type name=\"int16\"/> </leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "rule", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 2, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "id", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "ip", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ /* test error */
+ schema = MODULE_CREATE_YIN("TERR_0",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <min-elements value=\"10\"/>"
+ " <max-elements value=\"-1\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"> </leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Invalid value \"-1\" of \"value\" attribute in \"max-elements\" element.", "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_0",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <min-elements value=\"10\"/>"
+ " <max-elements value=\"4294967298\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Value \"4294967298\" of \"value\" attribute in \"max-elements\" element is out of bounds.", "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_0",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <min-elements value=\"20\"/>"
+ " <max-elements value=\"10\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Invalid combination of min-elements and max-elements: min value 20 is bigger than the max value 10.", "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_0",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <min-elements value=\"-1\"/>"
+ " <max-elements value=\"20\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Value \"-1\" of \"value\" attribute in \"min-elements\" element is out of bounds.", "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_0",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <key value=\"name\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
+ "Redefinition of \"key\" sub-element in \"list\" element.", "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("T6",
+ "<list name=\"user\">"
+ " <config value=\"false\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_KEYLESS | LYS_SET_CONFIG, \
+ 1, "user", 0, 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("T7",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <unique tag=\"name\"/>"
+ " <ordered-by value=\"user\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("T8",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <unique tag=\"name\"/>"
+ " <ordered-by value=\"system\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("TERROR0",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <unique tag=\"name\"/>"
+ " <ordered-by value=\"systeme\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/> </leaf>"
+ "</list>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL,
+ "Invalid value \"systeme\" of \"value\" attribute in \"ordered-by\" element. Valid values are \"system\" and \"user\".",
+ "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("T_DEFS1",
+ "<list name=\"user\">"
+ " <key value=\"uid\"/>"
+ " <unique tag=\"name\"/>"
+ " <ordered-by value=\"system\"/>"
+ " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>"
+ " <leaf name=\"name\"> <type name=\"string\"/> </leaf>"
+ " <leaf name=\"group\"><type name=\"string\"/> <default value=\"ath\"/> </leaf>"
+ "</list>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \
+ 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0);
+ lysc_node = lysc_leaf->child;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0);
+ lysc_node = lysc_node->next;
+ CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0);
+
+}
+
+static void
+test_schema_print(void **state)
+{
+ const char *schema_yang, *schema_yin;
+ char *printed;
+ struct lys_module *mod;
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT0",
+ "list user {"
+ " min-elements 10;"
+ " max-elements 20;"
+ " key \"uid name\";"
+ " unique name;"
+ " leaf uid{type int32;}"
+ " leaf name{type string;}"
+ " leaf group{type string;}"
+ "}");
+ schema_yin = MODULE_CREATE_YIN("PRINT0",
+ " <list name=\"user\">\n"
+ " <key value=\"uid name\"/>\n"
+ " <unique tag=\"name\"/>\n"
+ " <min-elements value=\"10\"/>\n"
+ " <max-elements value=\"20\"/>\n"
+ " <leaf name=\"uid\">\n"
+ " <type name=\"int32\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"name\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"group\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </list>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT1",
+ "\n"
+ " list user {\n"
+ " key \"uid name\";\n"
+ " unique \"name\";\n"
+ " min-elements 10;\n"
+ " max-elements 20;\n"
+ " leaf uid {\n"
+ " type int32;\n"
+ " }\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " leaf group {\n"
+ " type string;\n"
+ " }\n"
+ " }\n");
+ schema_yin = MODULE_CREATE_YIN("PRINT1",
+ " <list name=\"user\">\n"
+ " <key value=\"uid name\"/>\n"
+ " <unique tag=\"name\"/>\n"
+ " <min-elements value=\"10\"/>\n"
+ " <max-elements value=\"20\"/>\n"
+ " <leaf name=\"uid\">\n"
+ " <type name=\"int32\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"name\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"group\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </list>\n");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+
+ /* test no segmentation fold due ignoring default value */
+ schema_yang = MODULE_CREATE_YANG("PRINT2", "list user {"
+ "key uid;"
+ "leaf uid{type int32; default \"25\";}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ free(printed);
+}
+
+static void
+test_xml(void **state)
+{
+ struct lyd_node *tree;
+ const char *data, *schema;
+ struct lyd_node_inner *list_tree;
+ struct lyd_node_term *list_leaf;
+
+ schema = MODULE_CREATE_YANG("T0", "list user {"
+ "key uid;"
+ "unique \"name group\";"
+ "leaf uid{type uint32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* add data */
+ data =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Martin Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ lyd_free_all(tree);
+
+ /* add data */
+ data =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin");
+ lyd_free_all(tree);
+
+ data =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Duplicate instance of \"user\".",
+ "Data location \"/T0:user[uid='0']\".");
+
+ data =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Unique data leaf(s) \"name group\" not satisfied in \"/T0:user[uid='0']\" and \"/T0:user[uid='1']\".",
+ "Data location \"/T0:user[uid='1']\".");
+
+ /* double key */
+ schema = MODULE_CREATE_YANG("T1", "list user {"
+ "key \"uid group\";"
+ "leaf uid{type uint32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data =
+ "<user xmlns=\"urn:tests:T1\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T1\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "User");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Tomáš Novák");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Admin");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Tomáš Novák");
+ lyd_free_all(tree);
+
+ data =
+ "<user xmlns=\"urn:tests:T1\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T1\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Duplicate instance of \"user\".",
+ "Data location \"/T1:user[uid='0'][group='User']\".");
+
+ /* min elements max elements */
+ schema = MODULE_CREATE_YANG("T2",
+ "list user {"
+ " key uid;"
+ " min-elements 3;"
+ " max-elements 5;"
+ " leaf uid{type uint32;}"
+ " leaf name{type string;}"
+ " leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data =
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>1</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>2</uid>"
+ " <name>Tomáš Jak</name>"
+ " <group>Admin</group>"
+ "</user>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin");
+ /* check third item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "2", 2);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Jak");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin");
+ lyd_free_all(tree);
+
+ data =
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>1</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>2</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>3</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>4</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ lyd_free_all(tree);
+
+ /* check wrong number of items */
+ data =
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>1</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Too few \"user\" instances.",
+ "Schema location \"/T2:user\".");
+
+ data =
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>1</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>2</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>3</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>4</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T2\">"
+ " <uid>5</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Too many \"user\" instances.",
+ "Data location \"/T2:user[uid='5']\".");
+
+ /* empty list */
+ schema = MODULE_CREATE_YANG("T_EMPTY_LIST",
+ "container user_list {"
+ " list user {"
+ " key uid;"
+ " unique \"name group\";"
+ " leaf uid{type uint32;}"
+ " leaf name{type string;}"
+ " leaf group{type string;}"
+ "}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* empty list */
+ data = "<user_list xmlns=\"urn:tests:T_EMPTY_LIST\"/>";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 0, 0, 0, 1, 0, 0, 0, 1);
+ lyd_free_all(tree);
+}
+
+static void
+test_json(void **state)
+{
+ struct lyd_node *tree;
+ const char *data, *schema;
+ struct lyd_node_inner *list_tree;
+ struct lyd_node_term *list_leaf;
+
+ schema = MODULE_CREATE_YANG("T0", "list user {"
+ "key uid;"
+ "unique \"name group\";"
+ "leaf uid{type uint32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* add data */
+ data =
+ "{\"T0:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":1, \"name\":\"Martin Novák\", \"group\":\"User\"}"
+ "]}";
+
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Martin Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ lyd_free_all(tree);
+
+ /* Unique */
+ data =
+ "{\"T0:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":1, \"name\":\"Jan Kuba\", \"group\":\"Admin\"}"
+ "]}";
+
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin");
+ lyd_free_all(tree);
+
+ data =
+ "{\"T0:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"Admin\"}"
+ "]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Duplicate instance of \"user\".",
+ "Data location \"/T0:user[uid='0']\".");
+
+ data =
+ "{\"T0:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":1, \"name\":\"Jan Kuba\", \"group\":\"User\"}"
+ "]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Unique data leaf(s) \"name group\" not satisfied in \"/T0:user[uid='0']\" and \"/T0:user[uid='1']\".",
+ "Data location \"/T0:user[uid='1']\".");
+
+ /* double key */
+ schema = MODULE_CREATE_YANG("T1", "list user {"
+ "key \"uid group\";"
+ "leaf uid{type uint32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data =
+ "{\"T1:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"Admin\"}"
+ "]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "User");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Jan Kuba");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Admin");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Jan Kuba");
+ lyd_free_all(tree);
+
+ data =
+ "{\"T1:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}"
+ "]}";
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Duplicate instance of \"user\".",
+ "Data location \"/T1:user[uid='0'][group='User']\".");
+
+ /* min elements max elements */
+ schema = MODULE_CREATE_YANG("T2",
+ "list user {"
+ " key uid;"
+ " min-elements 3;"
+ " max-elements 5;"
+ " leaf uid{type uint32;}"
+ " leaf name{type string;}"
+ " leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data =
+ "{\"T2:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":1, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":2, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}"
+ "]}";
+
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check second item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Antonín Kuba");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User");
+ /* check third item */
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ list_leaf = (void *) list_tree->child;
+ assert_string_equal(list_leaf->schema->name, "uid");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "2", 2);
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "name");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák");
+ list_leaf = (void *) list_leaf->next;
+ assert_string_equal(list_leaf->schema->name, "group");
+ CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin");
+ lyd_free_all(tree);
+
+ data =
+ "{\"T2:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":1, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":2, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":3, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":4, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}"
+ "]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1);
+ list_tree = (void *) list_tree->next;
+ CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1);
+ lyd_free_all(tree);
+
+ /* check wrong number of items */
+ data =
+ "{\"T2:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":4, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}"
+ "]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Too few \"user\" instances.",
+ "Schema location \"/T2:user\".");
+
+ data =
+ "{\"T2:user\": ["
+ " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"},"
+ " {\"uid\":1, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":2, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":3, \"name\":\"Antonín Kuba\", \"group\":\"User\"},"
+ " {\"uid\":4, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"},"
+ " {\"uid\":5, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}"
+ "]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_null(tree);
+ CHECK_LOG_CTX("Too many \"user\" instances.",
+ "Data location \"/T2:user[uid='5']\".");
+
+ schema = MODULE_CREATE_YANG("T_EMPTY_LIST",
+ "container user_list {"
+ " list user {"
+ " key uid;"
+ " unique \"name group\";"
+ " leaf uid{type uint32;}"
+ " leaf name{type string;}"
+ " leaf group{type string;}"
+ "}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* empty list */
+ data =
+ "{\"T_EMPTY_LIST:user_list\": {}"
+ "}";
+
+ /* check first item */
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ list_tree = (void *)tree;
+ CHECK_LYD_NODE_INNER(list_tree, 0, 0, 0, 1, 0, 0, 0, 1);
+ lyd_free_all(tree);
+}
+
+static void
+test_diff(void **state)
+{
+ const char *schema;
+ struct lyd_node *model_1, *model_2;
+ struct lyd_node *diff;
+ const char *data_1;
+ const char *data_2;
+ const char *diff_expected;
+
+ schema = MODULE_CREATE_YANG("T0", "list user {"
+ "key uid;"
+ "unique \"name group\";"
+ "leaf uid{type uint32;}"
+ "leaf name{type string;}"
+ "leaf group{type string;}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* delete item */
+ data_1 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ data_2 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ diff_expected =
+ "<user xmlns=\"urn:tests:T0\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"delete\">"
+ "<uid>1</uid>"
+ "<name>Martin Novák</name>"
+ "<group>User</group>"
+ "</user>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+
+ /* add item */
+ data_1 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ data_2 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ diff_expected =
+ "<user xmlns=\"urn:tests:T0\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"create\">"
+ "<uid>1</uid><name>Martin Novák</name><group>User</group>"
+ "</user>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+
+ /* diff one */
+ data_1 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ data_2 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>Admin</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ diff_expected =
+ "<user xmlns=\"urn:tests:T0\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"none\">"
+ "<uid>0</uid>"
+ "<group yang:operation=\"replace\" yang:orig-default=\"false\""
+ " yang:orig-value=\"User\">Admin</group></user>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+
+ /* diff same */
+ data_1 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ data_2 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ " <group>User</group>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>User</group>"
+ "</user>";
+
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_null(diff);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+}
+
+static void
+test_print(void **state)
+{
+
+ const char *schema;
+ const char *expected_string;
+
+ schema = MODULE_CREATE_YANG("T0",
+ "list user {"
+ "key uid;"
+ "leaf uid{type uint32;}"
+ "leaf name{type string;}"
+ "leaf group{type string; default \"User\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ struct lyd_node *model_1;
+ const char *data_1 =
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>0</uid>"
+ " <name>Tomáš Novák</name>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ " <uid>1</uid>"
+ " <name>Martin Novák</name>"
+ " <group>Admin</group>"
+ "</user>";
+
+ LYD_TREE_CREATE(data_1, model_1);
+
+ /* XML */
+ expected_string =
+ "<user xmlns=\"urn:tests:T0\">"
+ "<uid>0</uid><name>Tomáš Novák</name>"
+ "</user>"
+ "<user xmlns=\"urn:tests:T0\">"
+ "<uid>1</uid><name>Martin Novák</name><group>Admin</group>"
+ "</user>";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+
+ /* JSON */
+ expected_string = "{\"T0:user\":["
+ "{\"uid\":0,\"name\":\"Tomáš Novák\"},"
+ "{\"uid\":1,\"name\":\"Martin Novák\",\"group\":\"Admin\"}"
+ "]}";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+
+ lyd_free_all(model_1);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema_yang),
+ UTEST(test_schema_yin),
+ UTEST(test_schema_print),
+
+ UTEST(test_xml),
+ UTEST(test_json),
+ UTEST(test_diff),
+ UTEST(test_print),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/restriction/test_pattern.c b/tests/utests/restriction/test_pattern.c
new file mode 100644
index 0000000..94539d6
--- /dev/null
+++ b/tests/utests/restriction/test_pattern.c
@@ -0,0 +1,397 @@
+/**
+ * @file test_pattern.c
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @brief test for int8 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_ERROR_JSON(MOD_NAME, DATA) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_schema_yang(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_pattern *pattern;
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type string {"
+ "pattern \"[A-Za-z]*\"{"
+ "description \"pattern description\";"
+ "error-app-tag \"pattern err-apt-tag\";"
+ "error-message \"pattern error message\";}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag",
+ "pattern error message", "[A-Za-z]*", 0, 0, 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, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Za-z]*", "pattern description",
+ "pattern err-apt-tag", "pattern error message", 0, NULL);
+
+ /* heredity */
+ schema = MODULE_CREATE_YANG("T1", "typedef my_type {type string {"
+ "pattern \"[A-Za-z]*\"{"
+ "description \"pattern description\";"
+ "error-app-tag \"pattern err-apt-tag\";"
+ "error-message \"pattern error message\";}}}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag",
+ "pattern error message", "[A-Za-z]*", 0, 0, 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ /* heredity new pattern */
+ schema = MODULE_CREATE_YANG("T2", "typedef my_type {type string {"
+ "pattern \"[A-Za-z]*\"{"
+ "description \"pattern description\";"
+ "error-app-tag \"pattern err-apt-tag\";"
+ "error-message \"pattern error message\";}}}"
+ "leaf port {type my_type{pattern \"[A-Z]*\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag",
+ "pattern error message", "[A-Za-z]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[A-Z]*", 0, 0, 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", NULL, NULL, NULL, 0, NULL);
+
+ /* heredity new pattern */
+ schema = MODULE_CREATE_YANG("T3", "typedef my_type {type string {"
+ "pattern \"[A-Za-z]*\"{"
+ " description \"pattern 0 description\";"
+ " error-app-tag \"pattern 0 err-apt-tag\";"
+ " error-message \"pattern 0 error message\";}}}"
+ "leaf port {type my_type{pattern \"[A-Z]*\"{"
+ " description \"pattern 1 description\";"
+ " error-app-tag \"pattern 1 err-apt-tag\";"
+ " error-message \"pattern 1 error message\";"
+ "}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern 0 description", "pattern 0 err-apt-tag",
+ "pattern 0 error message", "[A-Za-z]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, "pattern 1 description", "pattern 1 err-apt-tag",
+ "pattern 1 error message", "[A-Z]*", 0, 0, 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", "pattern 1 description",
+ "pattern 1 err-apt-tag", "pattern 1 error message", 0, NULL);
+}
+
+static void
+test_schema_yin(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_pattern *pattern;
+
+ schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\"> <type name=\"string\">"
+ "<pattern value=\"[A-Za-z]*\">"
+ " <description><text>pattern description</text></description>"
+ " <error-app-tag value=\"pattern err-apt-tag\"/>"
+ " <error-message> <value>pattern error message</value></error-message>"
+ "</pattern></type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag",
+ "pattern error message", "[A-Za-z]*", 0, 0, 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, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Za-z]*", "pattern description",
+ "pattern err-apt-tag", "pattern error message", 0, NULL);
+
+ /* heredity */
+ schema = MODULE_CREATE_YIN("T1", "<typedef name=\"my_type\"> <type name=\"string\">"
+ "<pattern value=\"[A-Za-z]*\">"
+ " <description><text>pattern description</text></description>"
+ " <error-app-tag value=\"pattern err-apt-tag\"/>"
+ " <error-message><value>pattern error message</value></error-message>"
+ "</pattern></type></typedef>"
+ "<leaf name=\"port\"><type name=\"my_type\"/></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag",
+ "pattern error message", "[A-Za-z]*", 0, 0, 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ /* heredity new pattern */
+ schema = MODULE_CREATE_YIN("T2", "<typedef name=\"my_type\"> <type name=\"string\">"
+ "<pattern value=\"[A-Za-z]*\">"
+ " <description><text>pattern description</text></description>"
+ " <error-app-tag value=\"pattern err-apt-tag\"/>"
+ " <error-message><value>pattern error message</value></error-message>"
+ "</pattern></type></typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"><pattern value=\"[A-Z]*\"/>"
+ "</type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag",
+ "pattern error message", "[A-Za-z]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[A-Z]*", 0, 0, 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", NULL, NULL, NULL, 0, NULL);
+
+ /* heredity new pattern */
+ schema = MODULE_CREATE_YIN("T3", "<typedef name=\"my_type\"> <type name=\"string\">"
+ "<pattern value=\"[A-Za-z]*\">"
+ " <description> <text>pattern 0 description</text></description>"
+ " <error-app-tag value=\"pattern 0 err-apt-tag\"/>"
+ " <error-message> <value>pattern 0 error message</value></error-message>"
+ "</pattern></type></typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ "<pattern value=\"[A-Z]*\">"
+ " <description><text>pattern 1 description</text></description>"
+ " <error-app-tag value=\"pattern 1 err-apt-tag\"/>"
+ " <error-message><value>pattern 1 error message</value></error-message>"
+ "</pattern></type></leaf>");
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, "pattern 0 description", "pattern 0 err-apt-tag",
+ "pattern 0 error message", "[A-Za-z]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, "pattern 1 description", "pattern 1 err-apt-tag",
+ "pattern 1 error message", "[A-Z]*", 0, 0, 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", "pattern 1 description",
+ "pattern 1 err-apt-tag", "pattern 1 error message", 0, NULL);
+}
+
+static void
+test_schema_print(void **state)
+{
+ const char *schema_yang, *schema_yin;
+ char *printed;
+ struct lys_module *mod;
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT0", "leaf port {type string {"
+ "pattern \"[A-Z]*\"{"
+ "description \"desc < \";"
+ "error-app-tag \"err-apt-tag <\";"
+ "error-message \"error message <\";}}}");
+
+ schema_yin = MODULE_CREATE_YIN("PRINT0",
+ " <leaf name=\"port\">\n"
+ " <type name=\"string\">\n"
+ " <pattern value=\"[A-Z]*\">\n"
+ " <error-message>\n"
+ " <value>error message &lt;</value>\n"
+ " </error-message>\n"
+ " <error-app-tag value=\"err-apt-tag &lt;\"/>\n"
+ " <description>\n"
+ " <text>desc &lt; </text>\n"
+ " </description>\n"
+ " </pattern>\n"
+ " </type>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT1",
+ "\n"
+ " leaf port {\n"
+ " type string {\n"
+ " pattern \"[A-Z]*\" {\n"
+ " error-message\n"
+ " \"error message <\";\n"
+ " error-app-tag \"err-apt-tag <\";\n"
+ " description\n"
+ " \"desc < \";\n"
+ " }\n"
+ " }\n"
+ " }\n");
+
+ schema_yin = MODULE_CREATE_YIN("PRINT1",
+ " <leaf name=\"port\">\n"
+ " <type name=\"string\">\n"
+ " <pattern value=\"[A-Z]*\">\n"
+ " <error-message>\n"
+ " <value>error message &lt;</value>\n"
+ " </error-message>\n"
+ " <error-app-tag value=\"err-apt-tag &lt;\"/>\n"
+ " <description>\n"
+ " <text>desc &lt; </text>\n"
+ " </description>\n"
+ " </pattern>\n"
+ " </type>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+}
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("TPATTERN_0", "typedef my_type {type string {"
+ "pattern \"[A-Za-z]*\"{"
+ " description \"pattern 0 description\";"
+ " error-app-tag \"pattern 0 err-apt-tag\";"
+ " error-message \"pattern 0 error message\";}}}"
+ "leaf port {type my_type{pattern \"[A-Z]*\"{"
+ " description \"pattern 1 description\";"
+ " error-app-tag \"pattern 1 err-apt-tag\";"
+ " error-message \"pattern 1 error message\";"
+ "}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* test success */
+ TEST_SUCCESS_XML("TPATTERN_0", "AHOJ", STRING, "AHOJ");
+ /* test print error */
+ TEST_ERROR_XML("TPATTERN_0", "T128");
+ CHECK_LOG_CTX("pattern 0 error message",
+ "Schema location \"/TPATTERN_0:port\", line number 1.");
+ TEST_ERROR_XML("TPATTERN_0", "ahoj");
+ CHECK_LOG_CTX("pattern 1 error message",
+ "Schema location \"/TPATTERN_0:port\", line number 1.");
+
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema_yang),
+ UTEST(test_schema_yin),
+ UTEST(test_schema_print),
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/restriction/test_range.c b/tests/utests/restriction/test_range.c
new file mode 100644
index 0000000..f202b6c
--- /dev/null
+++ b/tests/utests/restriction/test_range.c
@@ -0,0 +1,431 @@
+/**
+ * @file test_range.c
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @brief test for int8 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_ERROR_JSON(MOD_NAME, DATA) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_schema_yang(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_range *range;
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type int8 {"
+ "range \"0 .. 50 | 127\"{"
+ "description \"description test\";"
+ "error-app-tag \"err-apt-tag\";"
+ "error-message \"error message\";}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, "description test", "err-apt-tag", "error message", 0, 2, 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", "description test", "err-apt-tag", "error message", 0, NULL);
+
+ /* heredity */
+ schema = MODULE_CREATE_YANG("T1", "typedef my_type {type uint16 {"
+ "range \"0 .. 100\"{"
+ "description \"percentage\";"
+ "error-app-tag \"err-apt-tag\";"
+ "error-message \"error message\";}}}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_UINT16, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, "percentage", "err-apt-tag", "error message", 0, 1, 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ /* heredity new range */
+ schema = MODULE_CREATE_YANG("T2", "typedef my_type {type uint16 {"
+ "range \"0 .. 100\"{"
+ "description \"percentage\";"
+ "error-app-tag \"err-apt-tag\";"
+ "error-message \"error message\";}}}"
+ "leaf port {type my_type{range \"0 .. 20\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_UINT16, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, 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, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0);
+
+ /* change */
+ schema = MODULE_CREATE_YANG("T3", "typedef my_type {type uint16 {"
+ "range \"0 .. 100\"{"
+ "description \"percentage\";"
+ "error-app-tag \"err-apt-tag\";"
+ "error-message \"error message\";}}}"
+ "leaf port {type my_type{"
+ " range \"0 .. 50\"{"
+ " description \"description 0-50\";"
+ " error-app-tag \"err-apt-tag 0-50\";"
+ " error-message \"error message 0-50\";}}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_UINT16, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, "description 0-50", "err-apt-tag 0-50", "error message 0-50", 0, 1, 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, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50", "description 0-50", "err-apt-tag 0-50", "error message 0-50", 0, NULL);
+
+}
+
+static void
+test_schema_yin(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_range *range;
+
+ schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\">"
+ "<type name=\"int64\">"
+ "<range value = \"0 .. 50 | 256\">"
+ " <description>"
+ " <text>desc</text>\n"
+ " </description>\n"
+ "<error-app-tag value=\"text &lt; tag\"/>"
+ " <error-message>"
+ " <value>yin error message &lt;</value>\n"
+ " </error-message>\n"
+ "</range>"
+ "</type>"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT64, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, "desc", "text < tag", "yin error message <", 0, 2, 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, 0x80, 0, 0, "int64", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 256", "desc", "text < tag", "yin error message <", 0, NULL);
+
+ /* heredity */
+ schema = MODULE_CREATE_YIN("T1", "<typedef name=\"my_type\">"
+ "<type name=\"int16\">"
+ "<range value = \"0 .. 50\">"
+ " <description>"
+ " <text>percentage</text>\n"
+ " </description>\n"
+ "<error-app-tag value=\"text &lt; tag\"/>"
+ " <error-message>"
+ " <value>yin error message &lt;</value>\n"
+ " </error-message>\n"
+ "</range>"
+ "</type>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT16, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, "percentage", "text < tag", "yin error message <", 0, 1, 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ /* heredity new range */
+ schema = MODULE_CREATE_YIN("T2", "<typedef name=\"my_type\">"
+ "<type name=\"int32\">"
+ "<range value = \"0 .. 100\">"
+ " <description>"
+ " <text>percentage</text>\n"
+ " </description>\n"
+ " <error-app-tag value=\"text &lt; tag\"/>"
+ " <error-message>"
+ " <value>yin error message &lt;</value>\n"
+ " </error-message>\n"
+ " </range>"
+ " </type>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ " <range value = \"0 .. 50\"/>"
+ "</type></leaf>");
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT32, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, 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, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0);
+
+ /* change */
+ schema = MODULE_CREATE_YIN("T3", "<typedef name=\"my_type\">"
+ "<type name=\"int32\">"
+ "<range value = \"0 .. 100\">"
+ " <description>"
+ " <text>percentage</text>\n"
+ " </description>\n"
+ " <error-app-tag value=\"text &lt; tag\"/>"
+ " <error-message>"
+ " <value>yin error message &lt;</value>\n"
+ " </error-message>\n"
+ " </range>"
+ " </type>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ " <range value = \"0 .. 50\">"
+ " <description>"
+ " <text>percentage 0-50</text>\n"
+ " </description>\n"
+ " <error-app-tag value=\"text tag 0-50\"/>"
+ " <error-message>"
+ " <value>yin error message 0-50</value>\n"
+ " </error-message>\n"
+ " </range>"
+ "</type></leaf>");
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT32, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, "percentage 0-50", "text tag 0-50", "yin error message 0-50", 0, 1, 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, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50", "percentage 0-50", "text tag 0-50", "yin error message 0-50", 0, NULL);
+
+}
+
+static void
+test_schema_print(void **state)
+{
+ const char *schema_yang, *schema_yin;
+ char *printed;
+ struct lys_module *mod;
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT0", "leaf port {type int32 {"
+ "range \"0 .. 50\"{"
+ "description \"desc < \";"
+ "error-app-tag \"err-apt-tag <\";"
+ "error-message \"error message <\";}}}");
+
+ schema_yin = MODULE_CREATE_YIN("PRINT0",
+ " <leaf name=\"port\">\n"
+ " <type name=\"int32\">\n"
+ " <range value=\"0 .. 50\">\n"
+ " <error-message>\n"
+ " <value>error message &lt;</value>\n"
+ " </error-message>\n"
+ " <error-app-tag value=\"err-apt-tag &lt;\"/>\n"
+ " <description>\n"
+ " <text>desc &lt; </text>\n"
+ " </description>\n"
+ " </range>\n"
+ " </type>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT1",
+ "\n"
+ " leaf port {\n"
+ " type int32 {\n"
+ " range \"0 .. 50\" {\n"
+ " error-message\n"
+ " \"error message <\";\n"
+ " error-app-tag \"err-apt-tag <\";\n"
+ " description\n"
+ " \"desc < \";\n"
+ " }\n"
+ " }\n"
+ " }\n");
+
+ schema_yin = MODULE_CREATE_YIN("PRINT1",
+ " <leaf name=\"port\">\n"
+ " <type name=\"int32\">\n"
+ " <range value=\"0 .. 50\">\n"
+ " <error-message>\n"
+ " <value>error message &lt;</value>\n"
+ " </error-message>\n"
+ " <error-app-tag value=\"err-apt-tag &lt;\"/>\n"
+ " <description>\n"
+ " <text>desc &lt; </text>\n"
+ " </description>\n"
+ " </range>\n"
+ " </type>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+}
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("TRANGE_0", "leaf port {type int8 {"
+ "range \"0 .. 50 | 126\"{"
+ "description \"description test\";"
+ "error-app-tag \"err-apt-tag\";"
+ "error-message \"error message\";}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* test success */
+ TEST_SUCCESS_XML("TRANGE_0", "126", INT8, "126", 126);
+ /* test print error */
+ TEST_ERROR_XML("TRANGE_0", "-1");
+ CHECK_LOG_CTX("error message",
+ "Schema location \"/TRANGE_0:port\", line number 1.");
+ TEST_ERROR_XML("TRANGE_0", "51");
+ CHECK_LOG_CTX("error message",
+ "Schema location \"/TRANGE_0:port\", line number 1.");
+ TEST_ERROR_XML("TRANGE_0", "127");
+ CHECK_LOG_CTX("error message",
+ "Schema location \"/TRANGE_0:port\", line number 1.");
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("TRANGE_1", "leaf port {type uint8 {"
+ "range \"30 .. 50 | 126\"{"
+ "description \"description test\";"
+ "error-app-tag \"err-apt-tag\";"
+ "error-message \"error message\";}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* test success */
+ TEST_SUCCESS_XML("TRANGE_1", "126", UINT8, "126", 126);
+ /* test print error */
+ TEST_ERROR_XML("TRANGE_1", "0");
+ CHECK_LOG_CTX("error message",
+ "Schema location \"/TRANGE_1:port\", line number 1.");
+ TEST_ERROR_XML("TRANGE_1", "51");
+ CHECK_LOG_CTX("error message",
+ "Schema location \"/TRANGE_1:port\", line number 1.");
+ TEST_ERROR_XML("TRANGE_1", "127");
+ CHECK_LOG_CTX("error message",
+ "Schema location \"/TRANGE_1:port\", line number 1.");
+
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema_yang),
+ UTEST(test_schema_yin),
+ UTEST(test_schema_print),
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/schema/test_printer_tree.c b/tests/utests/schema/test_printer_tree.c
new file mode 100644
index 0000000..c076ece
--- /dev/null
+++ b/tests/utests/schema/test_printer_tree.c
@@ -0,0 +1,2405 @@
+/*
+ * @file test_printer_tree.c
+ * @author: Adam Piecek <piecek@cesnet.cz>
+ * @brief unit tests for functions from printer_tree.c
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "common.h"
+#include "context.h"
+#include "out.h"
+#include "printer_schema.h"
+#include "tree_schema.h"
+
+#define TEST_LOCAL_SETUP \
+ char *printed; \
+ struct lys_module *mod; \
+ const char *orig; \
+ const char *expect; \
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &UTEST_OUT));
+
+#define TEST_LOCAL_PRINT(MOD, LINE_LENGTH) \
+ assert_int_equal(LY_SUCCESS, lys_print_module(UTEST_OUT, MOD, LYS_OUT_TREE, LINE_LENGTH, 0));
+
+#define TEST_LOCAL_TEARDOWN \
+ ly_out_free(UTEST_OUT, NULL, 1);
+
+static void
+base_sections(void **state)
+{
+ TEST_LOCAL_SETUP;
+ struct lys_module *modxx;
+
+ orig =
+ "module a01xx {\n"
+ " yang-version 1.1;\n"
+ " namespace \"xx:y\";\n"
+ " prefix xx;\n"
+ " container c;\n"
+ " container d;\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &modxx);
+
+ /* module with import statement */
+ orig =
+ "module a01 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " import a01xx {\n"
+ " prefix xx;\n"
+ " }\n"
+ "\n"
+ " grouping g1;\n"
+ "\n"
+ " grouping g2;\n"
+ " container g;\n"
+ " augment \"/xx:c\" {\n"
+ " container e;\n"
+ " }\n"
+ " augment \"/xx:d\" {\n"
+ " container f;\n"
+ " }\n"
+ " rpc rpc1;\n"
+ " rpc rpc2;\n"
+ " notification n1;\n"
+ " notification n2;\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+
+ /* from pyang */
+ expect =
+ "module: a01\n"
+ " +--rw g\n"
+ "\n"
+ " augment /xx:c:\n"
+ " +--rw e\n"
+ " augment /xx:d:\n"
+ " +--rw f\n"
+ "\n"
+ " rpcs:\n"
+ " +---x rpc1\n"
+ " +---x rpc2\n"
+ "\n"
+ " notifications:\n"
+ " +---n n1\n"
+ " +---n n2\n"
+ "\n"
+ " grouping g1\n"
+ " grouping g2\n";
+
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
+ /* from pyang */
+ expect =
+ "module: a01\n"
+ " +--rw g\n"
+ "\n"
+ " augment /xx:c:\n"
+ " +--rw e\n"
+ " augment /xx:d:\n"
+ " +--rw f\n"
+ "\n"
+ " rpcs:\n"
+ " +---x rpc1\n"
+ " +---x rpc2\n"
+ "\n"
+ " notifications:\n"
+ " +---n n1\n"
+ " +---n n2\n";
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
+ /* from pyang */
+ expect =
+ "module: a01xx\n"
+ " +--rw c\n"
+ " | +--rw x:e\n"
+ " +--rw d\n"
+ " +--rw x:f\n";
+
+ TEST_LOCAL_PRINT(modxx, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_status(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a02 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container l {\n"
+ " status current;\n"
+ " }\n"
+ " container m {\n"
+ " status deprecated;\n"
+ " }\n"
+ " container n {\n"
+ " status obsolete;\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a02\n"
+ " +--rw l\n"
+ " x--rw m\n"
+ " o--rw n\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_config_flags(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a03 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container l {\n"
+ " config true;\n"
+ " }\n"
+ " container m {\n"
+ " config false;\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a03\n"
+ " +--rw l\n"
+ " +--ro m\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_rpcs_flags(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a04 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container cont {\n"
+ " action rpc1 {\n"
+ "\n"
+ " input {\n"
+ " leaf in {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " output {\n"
+ " leaf out {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a04\n"
+ " +--rw cont\n"
+ " +---x rpc1\n"
+ " +---w input\n"
+ " | +---w in? string\n"
+ " +--ro output\n"
+ " +--ro out? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_grouping_flags(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a05 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " grouping g {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " config true;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " config false;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ " container d {\n"
+ " config false;\n"
+ " leaf e {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " container f {\n"
+ " leaf g {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container d {\n"
+ " uses g;\n"
+ " }\n"
+ "}\n";
+
+ /* from yanglint1 */
+ expect =
+ "module: a05\n"
+ " +--rw d\n"
+ " +---u g\n"
+ "\n"
+ " grouping g:\n"
+ " +--rw a? string\n"
+ " +--ro b? string\n"
+ " +---- c? string\n"
+ " +--ro d\n"
+ " | +--ro e? string\n"
+ " +---- f\n"
+ " +---- g? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ /* from pyang */
+ expect =
+ "module: a05\n"
+ " +--rw d\n"
+ " +--rw a? string\n"
+ " +--ro b? string\n"
+ " +--rw c? string\n"
+ " +--ro d\n"
+ " | +--ro e? string\n"
+ " +--rw f\n"
+ " +--rw g? string\n";
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+notif_inside_container(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a06 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container c {\n"
+ " notification notif;\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a06\n"
+ " +--rw c\n"
+ " +---n notif\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_choice(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a07 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " choice my_choice;\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a07\n"
+ " +--rw (my_choice)?\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_case(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a08 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " feature foo;\n"
+ " choice my_choice {\n"
+ " case my_case;\n"
+ " }\n"
+ " choice shorthand {\n"
+ " container cont1 {\n"
+ " if-feature \"foo\";\n"
+ " status obsolete;\n"
+ " }\n"
+ " container cont2 {\n"
+ " container cont3;\n"
+ " }\n"
+ " }\n"
+ " container top {\n"
+ " choice shorthand1 {\n"
+ " container cont1;\n"
+ " }\n"
+ " choice shorthand2 {\n"
+ " container cont2 {\n"
+ " container cont3;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a08\n"
+ " +--rw (my_choice)?\n"
+ " | +--:(my_case)\n"
+ " +--rw (shorthand)?\n"
+ " | o--:(cont1)\n"
+ " | | o--rw cont1 {foo}?\n"
+ " | +--:(cont2)\n"
+ " | +--rw cont2\n"
+ " | +--rw cont3\n"
+ " +--rw top\n"
+ " +--rw (shorthand1)?\n"
+ " | +--:(cont1)\n"
+ " | +--rw cont1\n"
+ " +--rw (shorthand2)?\n"
+ " +--:(cont2)\n"
+ " +--rw cont2\n"
+ " +--rw cont3\n";
+
+ const char *feats[] = {"foo", NULL};
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+optional_opts(void **state)
+{
+ TEST_LOCAL_SETUP;
+ /* throws libyang warn: Use of anydata to define configuration data is not recommended... */
+ orig =
+ "module a09 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " leaf l1 {\n"
+ " type string;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf l2 {\n"
+ " type string;\n"
+ " mandatory false;\n"
+ " }\n"
+ " choice c1 {\n"
+ " mandatory true;\n"
+ " }\n"
+ " choice c2 {\n"
+ " mandatory false;\n"
+ " }\n"
+ " anydata a1 {\n"
+ " mandatory true;\n"
+ " }\n"
+ " anydata a2 {\n"
+ " mandatory false;\n"
+ " }\n"
+ " anyxml x1 {\n"
+ " mandatory true;\n"
+ " }\n"
+ " anyxml x2 {\n"
+ " mandatory false;\n"
+ " }\n"
+ "}\n";
+
+ /* from yanglint 1 */
+ expect =
+ "module: a09\n"
+ " +--rw l1 string\n"
+ " +--rw l2? string\n"
+ " +--rw (c1)\n"
+ " +--rw (c2)?\n"
+ " +--rw a1 anydata\n"
+ " +--rw a2? anydata\n"
+ " +--rw x1 anyxml\n"
+ " +--rw x2? anyxml\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+presence_container(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a10 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container c;\n"
+ " container d {\n"
+ " presence \"str1\";\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a10\n"
+ " +--rw c\n"
+ " +--rw d!\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_keys(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a11 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " list l1 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " list l2 {\n"
+ " key \"a b\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a11\n"
+ " +--rw l1* [a]\n"
+ " | +--rw a string\n"
+ " +--rw l2* [a b]\n"
+ " | +--rw a string\n"
+ " | +--rw b string\n"
+ " +--rw ll* string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_type_target(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a12 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " leaf a {\n"
+ " type leafref {\n"
+ " path \"/x:b\";\n"
+ " }\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ "}\n";
+
+ /* from yanglint 1 */
+ expect =
+ "module: a12\n"
+ " +--rw a? -> /x:b\n"
+ " +--rw b? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_type_leafref(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a13 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " leaf pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters {\n"
+ " type string;\n"
+ " }\n"
+ " leaf a {\n"
+ " type leafref {\n"
+ " path \"/x:pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* yanglint --tree-no-leafref-target --tree-line-length=72 */
+ expect =
+ "module: a13\n"
+ " +--rw pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters?\n"
+ " | string\n"
+ " +--rw a?\n"
+ " leafref\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+node_iffeatures(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a14 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " feature foo;\n"
+ "\n"
+ " feature bar;\n"
+ " container c {\n"
+ " if-feature \"foo or bar\";\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a14\n"
+ " +--rw c {foo or bar}?\n";
+
+ const char *feats[] = {"foo", NULL};
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+indent_wrapper(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a15 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container a {\n"
+ " container b;\n"
+ " }\n"
+ " container c {\n"
+ " container d {\n"
+ " container e;\n"
+ " }\n"
+ " container f {\n"
+ " container g;\n"
+ " }\n"
+ " }\n"
+ " container h;\n"
+ " container i {\n"
+ " container j;\n"
+ " container k;\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a15\n"
+ " +--rw a\n"
+ " | +--rw b\n"
+ " +--rw c\n"
+ " | +--rw d\n"
+ " | | +--rw e\n"
+ " | +--rw f\n"
+ " | +--rw g\n"
+ " +--rw h\n"
+ " +--rw i\n"
+ " +--rw j\n"
+ " +--rw k\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+line_length_twiddling(void **state)
+{
+ TEST_LOCAL_SETUP;
+ /* node_fits_tight */
+
+ orig =
+ "module a16 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " feature f;\n"
+ "\n"
+ " typedef some-long-type {\n"
+ " type string;\n"
+ " }\n"
+ " list my-list-name {\n"
+ " key \"key\";\n"
+ " leaf key {\n"
+ " type string;\n"
+ " }\n"
+ " leaf nod-leaf {\n"
+ " if-feature \"f\";\n"
+ " type some-long-type;\n"
+ " }\n"
+ " leaf nos-leaf {\n"
+ " if-feature \"f\";\n"
+ " type int32;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* pyang --tree-line-length 42 */
+ expect =
+ "module: a16\n"
+ " +--rw my-list-name* [key]\n"
+ " +--rw key string\n"
+ " +--rw nod-leaf? some-long-type {f}?\n"
+ " +--rw nos-leaf? int32 {f}?\n";
+
+ const char *feats[] = {"f", NULL};
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 42);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 42);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ ly_out_reset(UTEST_OUT);
+ /* break_before_iffeature */
+
+ /* pyang --tree-line-length 41 */
+ expect =
+ "module: a16\n"
+ " +--rw my-list-name* [key]\n"
+ " +--rw key string\n"
+ " +--rw nod-leaf? some-long-type\n"
+ " | {f}?\n"
+ " +--rw nos-leaf? int32 {f}?\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 41);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 41);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ ly_out_reset(UTEST_OUT);
+ /* break_before_type */
+
+ /* pyang --tree-line-length 29 */
+ expect =
+ "module: a16\n"
+ " +--rw my-list-name* [key]\n"
+ " +--rw key string\n"
+ " +--rw nod-leaf?\n"
+ " | some-long-type\n"
+ " | {f}?\n"
+ " +--rw nos-leaf? int32\n"
+ " {f}?\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 29);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 29);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ ly_out_reset(UTEST_OUT);
+ /* break_before_keys */
+
+ /* pyang --tree-line-length 23 */
+ expect =
+ "module: a16\n"
+ " +--rw my-list-name*\n"
+ " [key]\n"
+ " +--rw key\n"
+ " | string\n"
+ " +--rw nod-leaf?\n"
+ " | some-long-type\n"
+ " | {f}?\n"
+ " +--rw nos-leaf?\n"
+ " int32 {f}?\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 23);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 23);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ ly_out_reset(UTEST_OUT);
+ /* every_node_name_is_too_long */
+
+ /* pyang --tree-line-length 14 */
+ expect =
+ "module: a16\n"
+ " +--rw my-list-name*\n"
+ " [key]\n"
+ " +--rw key\n"
+ " | string\n"
+ " +--rw nod-leaf?\n"
+ " | some-long-type\n"
+ " | {f}?\n"
+ " +--rw nos-leaf?\n"
+ " int32\n"
+ " {f}?\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 14);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 14);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+break_before_leafref(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a17 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " leaf e {\n"
+ " type string;\n"
+ " }\n"
+ " leaf abcd {\n"
+ " type leafref {\n"
+ " path \"/x:e\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* yanglint --tree-line-length 14 */
+ expect =
+ "module: a17\n"
+ " +--rw e?\n"
+ " | string\n"
+ " +--rw abcd?\n"
+ " -> /x:e\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 14);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 14);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+break_before_leafref_and_iffeature(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a18 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " feature f;\n"
+ " leaf some-long-id {\n"
+ " type string;\n"
+ " }\n"
+ " leaf a {\n"
+ " if-feature \"f\";\n"
+ " type leafref {\n"
+ " path \"/x:some-long-id\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* yanglint --tree-no-leafref-target --tree-line-length=20 */
+ expect =
+ "module: a18\n"
+ " +--rw some-long-id?\n"
+ " | string\n"
+ " +--rw a?\n"
+ " leafref\n"
+ " {f}?\n";
+
+ const char *feats[] = {"f", NULL};
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod);
+ TEST_LOCAL_PRINT(mod, 20);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 20);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+basic_unified_indent_before_type(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a19 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " typedef longType {\n"
+ " type string;\n"
+ " }\n"
+ " container A {\n"
+ " leaf Bnode {\n"
+ " type int8;\n"
+ " }\n"
+ " leaf Cnode {\n"
+ " type int8;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf Dnode {\n"
+ " type int8;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf E {\n"
+ " type longType;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf G {\n"
+ " type int8;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a19\n"
+ " +--rw A\n"
+ " +--rw Bnode? int8\n"
+ " +--rw Cnode int8\n"
+ " +--rw Dnode int8\n"
+ " +--rw E longType\n"
+ " +--rw G? int8\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+twiddling_unified_indent_before_type(void **state)
+{
+ TEST_LOCAL_SETUP;
+ /* basic_functionality */
+
+ orig =
+ "module a20 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " typedef longType {\n"
+ " type string;\n"
+ " }\n"
+ " container A {\n"
+ " leaf Bnode {\n"
+ " type int8;\n"
+ " }\n"
+ " leaf CnodeIsBigger {\n"
+ " type int8;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf Dnode {\n"
+ " type int8;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf E {\n"
+ " type longType;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf G {\n"
+ " type int8;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* pyang --tree-line-length 36 */
+ expect =
+ "module: a20\n"
+ " +--rw A\n"
+ " +--rw Bnode? int8\n"
+ " +--rw CnodeIsBigger int8\n"
+ " +--rw Dnode int8\n"
+ " +--rw E longType\n"
+ " +--rw G? int8\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 36);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 36);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ ly_out_reset(UTEST_OUT);
+ /* unified_indent_before_type_long_node_name */
+
+ /* pyang --tree-line-length 32 */
+ expect =
+ "module: a20\n"
+ " +--rw A\n"
+ " +--rw Bnode? int8\n"
+ " +--rw CnodeIsBigger int8\n"
+ " +--rw Dnode int8\n"
+ " +--rw E\n"
+ " | longType\n"
+ " +--rw G? int8\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 32);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 32);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ ly_out_reset(UTEST_OUT);
+ /* unified_indent_before_type_long_node_type */
+
+ /* pyang --tree-line-length 31 */
+ expect =
+ "module: a20\n"
+ " +--rw A\n"
+ " +--rw Bnode?\n"
+ " | int8\n"
+ " +--rw CnodeIsBigger\n"
+ " | int8\n"
+ " +--rw Dnode\n"
+ " | int8\n"
+ " +--rw E\n"
+ " | longType\n"
+ " +--rw G?\n"
+ " int8\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 31);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 31);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+inheritance_of_config_flag(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a21 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container a {\n"
+ " config false;\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a21\n"
+ " +--ro a\n"
+ " +--ro b? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+inheritance_of_status_flag(void **state)
+{
+ TEST_LOCAL_SETUP;
+ /* throws libyang warn: Missing explicit "..." status that was already specified in parent, inheriting. */
+ orig =
+ "module a22 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container a {\n"
+ " status current;\n"
+ " container b {\n"
+ " status deprecated;\n"
+ " leaf f {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " container c {\n"
+ " status obsolete;\n"
+ " leaf g {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container d {\n"
+ " status deprecated;\n"
+ " container h {\n"
+ " status obsolete;\n"
+ " leaf e {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from yanglint 1 */
+ expect =
+ "module: a22\n"
+ " +--rw a\n"
+ " | x--rw b\n"
+ " | | x--rw f? string\n"
+ " | o--rw c\n"
+ " | o--rw g? string\n"
+ " x--rw d\n"
+ " o--rw h\n"
+ " o--rw e? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+key_leaf_is_always_mandatory_true(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a23 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " list a {\n"
+ " key \"k1\";\n"
+ " list b {\n"
+ " key \"k2\";\n"
+ " leaf k1 {\n"
+ " type string;\n"
+ " }\n"
+ " leaf k2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf k1 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a23\n"
+ " +--rw a* [k1]\n"
+ " +--rw b* [k2]\n"
+ " | +--rw k1? string\n"
+ " | +--rw k2 string\n"
+ " +--rw k1 string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
+ /* from pyang but with some swapped lines */
+ expect =
+ "module: a23\n"
+ " +--rw a* [k1]\n"
+ " +--rw k1 string\n"
+ " +--rw b* [k2]\n"
+ " +--rw k2 string\n"
+ " +--rw k1? string\n";
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+transition_between_rpc_and_notif(void **state)
+{
+ TEST_LOCAL_SETUP;
+ orig =
+ "module a24 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container top {\n"
+ " leaf g {\n"
+ " type string;\n"
+ " }\n"
+ " action rpc1 {\n"
+ "\n"
+ " input {\n"
+ " leaf in {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " action rpc2 {\n"
+ "\n"
+ " input {\n"
+ " leaf in {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " output {\n"
+ " leaf out {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " notification n1;\n"
+ " notification n2;\n"
+ " }\n"
+ "}\n";
+
+ /* from pyang */
+ expect =
+ "module: a24\n"
+ " +--rw top\n"
+ " +--rw g? string\n"
+ " +---x rpc1\n"
+ " | +---w input\n"
+ " | +---w in? string\n"
+ " +---x rpc2\n"
+ " | +---w input\n"
+ " | | +---w in? string\n"
+ " | +--ro output\n"
+ " | +--ro out? string\n"
+ " +---n n1\n"
+ " +---n n2\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+local_augment(void **state)
+{
+ TEST_LOCAL_SETUP;
+
+ orig =
+ "module a25 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container g;\n"
+ " augment \"/x:g\" {\n"
+ " container e;\n"
+ " }\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+
+ /* from pyang */
+ expect =
+ "module: a25\n"
+ " +--rw g\n"
+ " +--rw e\n";
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+print_compiled_node(void **state)
+{
+ TEST_LOCAL_SETUP;
+ const struct lysc_node *node;
+
+ orig =
+ "module a26 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ " container g {\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " container h {\n"
+ " leaf b {\n"
+ " type string;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+
+ /* pyang -f tree --tree-path /g/h/c */
+ expect =
+ "module: a26\n"
+ " +--rw g\n"
+ " +--rw h\n"
+ " +--rw c? string\n";
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h/c", 0);
+ CHECK_POINTER(node, 1);
+ assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0));
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
+ /* pyang -f tree --tree-path /g/h */
+ expect =
+ "module: a26\n"
+ " +--rw g\n"
+ " +--rw h\n"
+ " +--rw b string\n"
+ " +--rw c? string\n";
+
+ node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0);
+ CHECK_POINTER(node, 1);
+ assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0));
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
+ /* pyang whose output is adjusted manually */
+ expect =
+ "module: a26\n"
+ " +--rw g\n"
+ " +--rw h\n";
+
+ node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0);
+ CHECK_POINTER(node, 1);
+ assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, LYS_PRINT_NO_SUBSTMT));
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static LY_ERR
+local_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
+ const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
+ const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+print_parsed_submodule(void **state)
+{
+ TEST_LOCAL_SETUP;
+
+ orig = "module a27 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " include \"a27-sub\";\n"
+ "}\n";
+
+ char *submodule =
+ "submodule a27-sub {\n"
+ " yang-version 1.1;\n"
+ " belongs-to a27 {\n"
+ " prefix x;\n"
+ " }\n"
+ "\n"
+ " container h {\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* edited pyang output */
+ expect =
+ "submodule: a27-sub\n"
+ " +--rw h\n"
+ " +--rw b? string\n";
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, local_imp_clb, submodule);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ assert_int_equal(LY_SUCCESS, lys_print_submodule(UTEST_OUT, mod->parsed->includes[0].submodule, LYS_OUT_TREE, 72, 0));
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+yang_data(void **state)
+{
+ TEST_LOCAL_SETUP;
+
+ 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));
+
+ orig =
+ "module a28 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " import ietf-restconf {\n"
+ " prefix rc;\n"
+ " revision-date 2017-01-26;\n"
+ " }\n"
+ "\n"
+ " rc:yang-data \"tmp1\" {\n"
+ " container cont1 {\n"
+ " leaf lf {\n"
+ " type string;\n"
+ " }\n"
+ " list l2 {\n"
+ " key\n"
+ " \"a b\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " rc:yang-data \"tmp2\" {\n"
+ " container con2 {\n"
+ " leaf lf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " rc:yang-data \"tmp3\" {\n"
+ " uses g1;\n"
+ " uses g2;\n"
+ " }\n"
+ " rc:yang-data \"tmp4\" {\n"
+ " choice x {\n"
+ " case a {\n"
+ " container z;\n"
+ " }\n"
+ " case b {\n"
+ " container y;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " grouping g1 {\n"
+ " description\n"
+ " \"Some Text\";\n"
+ " }\n"
+ "\n"
+ " grouping g2 {\n"
+ " container cont3;\n"
+ " }\n"
+ " container mont;\n"
+ "}\n";
+
+ /* from pyang (--tree-print-yang-data --tree-print-groupings -p "...")
+ * but with these adjustments:
+ * - <flags> is always '--' for yang-data nodes
+ * - yang-data tmp3 has two 'uses' nodes
+ * - grouping g2 has ':' character at the end
+ */
+ expect =
+ "module: a28\n"
+ " +--rw mont\n"
+ "\n"
+ " grouping g1\n"
+ " grouping g2:\n"
+ " +---- cont3\n"
+ "\n"
+ " yang-data tmp1:\n"
+ " +---- cont1\n"
+ " +---- lf? string\n"
+ " +---- l2* [a b]\n"
+ " +---- a string\n"
+ " +---- b string\n"
+ " yang-data tmp2:\n"
+ " +---- con2\n"
+ " +---- lf? string\n"
+ " yang-data tmp3:\n"
+ " +---u g1\n"
+ " +---u g2\n"
+ " yang-data tmp4:\n"
+ " +---- (x)?\n"
+ " +--:(a)\n"
+ " | +---- z\n"
+ " +--:(b)\n"
+ " +---- y\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
+ /* from pyang (--tree-print-yang-data -p "...")
+ * but with these adjustments:
+ * <flags> is always '--' for yang-data nodes
+ */
+ expect =
+ "module: a28\n"
+ " +--rw mont\n"
+ "\n"
+ " yang-data tmp1:\n"
+ " +---- cont1\n"
+ " +---- lf? string\n"
+ " +---- l2* [a b]\n"
+ " +---- a string\n"
+ " +---- b string\n"
+ " yang-data tmp2:\n"
+ " +---- con2\n"
+ " +---- lf? string\n"
+ " yang-data tmp3:\n"
+ " +---- cont3\n"
+ " yang-data tmp4:\n"
+ " +---- (x)?\n"
+ " +--:(a)\n"
+ " | +---- z\n"
+ " +--:(b)\n"
+ " +---- y\n";
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+static LY_ERR
+getter(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *data = NULL;
+
+ ctx = ext->module->ctx;
+ if (user_data) {
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, user_data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &data));
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
+#define SM_MODNAME_EXT "sm-extension"
+#define SM_MOD_EXT_NAMESPACE "urn:sm-ext"
+#define SM_PREF "sm"
+#define SCHEMA_REF_INLINE "<inline></inline>"
+#define SCHEMA_REF_SHARED(REF) "<shared-schema>"REF"</shared-schema>"
+
+#define EXT_DATA(MPMOD_NAME, MODULES, SCHEMA_REF) \
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"\n" \
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">\n" \
+ "<module-set>\n" \
+ " <name>test-set</name>\n" \
+ " <module>\n" \
+ " <name>"SM_MODNAME_EXT"</name>\n" \
+ " <namespace>"SM_MOD_EXT_NAMESPACE"</namespace>\n" \
+ " </module>\n" \
+ MODULES \
+ "</module-set>\n" \
+ "<content-id>1</content-id>\n" \
+ "</yang-library>\n" \
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" \
+ "<module-set-id>1</module-set-id>\n" \
+ "</modules-state>\n" \
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">\n" \
+ "<namespace>\n" \
+ " <prefix>"SM_PREF"</prefix>\n" \
+ " <uri>x:"MPMOD_NAME"</uri>\n" \
+ "</namespace>\n" \
+ "<mount-point>\n" \
+ " <module>"MPMOD_NAME"</module>\n" \
+ " <label>mnt-root</label>\n" \
+ SCHEMA_REF \
+ "</mount-point>\n" \
+ "</schema-mounts>"
+
+#define SM_MOD_MAIN(NAME, BODY) \
+ "module "NAME" {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"x:"NAME"\";\n" \
+ " prefix \"x\";\n" \
+ " import ietf-yang-schema-mount {\n" \
+ " prefix yangmnt;\n" \
+ " }\n" \
+ BODY \
+ "}"
+
+static void
+mount_point(void **state)
+{
+ char *data;
+
+ TEST_LOCAL_SETUP;
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ /* interested in sm-extension.yang and sm-mod.yang */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+
+ /*
+ * 'mp' flag for list and container
+ */
+ orig = SM_MOD_MAIN("a29",
+ "list lt {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n"
+ "container cont {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n");
+ expect =
+ "module: a29\n"
+ " +--mp lt* [name]\n"
+ " | +--rw name string\n"
+ " +--mp cont\n";
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * mount schema by 'inline' schema-ref
+ */
+ orig = SM_MOD_MAIN("a30",
+ "list lt {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n");
+ expect =
+ "module: a30\n"
+ " +--mp lt* [name]\n"
+ " +--rw tlist/* [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw name string\n";
+ data = EXT_DATA("a30", "", SCHEMA_REF_INLINE);
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * mount schema into empty container
+ */
+ orig = SM_MOD_MAIN("a31",
+ "container cont {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n"
+ "leaf lf {\n"
+ " type string;\n"
+ "}\n");
+ expect =
+ "module: a31\n"
+ " +--mp cont\n"
+ " | +--rw tlist/* [name]\n"
+ " | | +--rw name uint32\n"
+ " | +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw lf? string\n";
+ data = EXT_DATA("a31", "", SCHEMA_REF_INLINE);
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * mount schema into non-empty container
+ */
+ orig = SM_MOD_MAIN("a32",
+ "container cont {\n"
+ " leaf lf1 {\n"
+ " type string;\n"
+ " }\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ " leaf lf2 {\n"
+ " type string;\n"
+ " }\n"
+ "}\n");
+ expect =
+ "module: a32\n"
+ " +--mp cont\n"
+ " +--rw tlist/* [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw lf1? string\n"
+ " +--rw lf2? string\n";
+ data = EXT_DATA("a32", "", SCHEMA_REF_INLINE);
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * mounting with parent-reference
+ */
+ orig = SM_MOD_MAIN("a33",
+ "list pr {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " leaf prlf {\n"
+ " type string;\n"
+ " }\n"
+ "}\n"
+ "leaf lf {\n"
+ " type string;\n"
+ "}\n"
+ "container cont {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ " list lt {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n");
+ expect =
+ "module: a33\n"
+ " +--rw pr* [name]\n"
+ " | +--rw name string\n"
+ " | +--rw prlf? string\n"
+ " +--rw lf? string\n"
+ " +--mp cont\n"
+ " +--rw tlist/* [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw pr@* [name]\n"
+ " | +--rw prlf? string\n"
+ " +--rw lf@? string\n"
+ " +--rw lt* [name]\n"
+ " +--rw name string\n";
+ data = EXT_DATA("a33", "", SCHEMA_REF_SHARED(
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":prlf</parent-reference>\n"
+ "<parent-reference>/"SM_PREF ":lf</parent-reference>\n"));
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * mounting with parent-reference into empty container
+ */
+ orig = SM_MOD_MAIN("a34",
+ "container cont {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n"
+ "leaf lf {\n"
+ " type string;\n"
+ "}\n");
+ expect =
+ "module: a34\n"
+ " +--mp cont\n"
+ " | +--rw tlist/* [name]\n"
+ " | | +--rw name uint32\n"
+ " | +--rw tcont/\n"
+ " | | +--rw tleaf? uint32\n"
+ " | +--rw lf@? string\n"
+ " +--rw lf? string\n";
+ data = EXT_DATA("a34", "",
+ SCHEMA_REF_SHARED(
+ "<parent-reference>/"SM_PREF ":lf</parent-reference>\n"));
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * mounting module which is only parsed
+ */
+ orig = SM_MOD_MAIN("a35",
+ "import sm-mod {\n"
+ " prefix smm;\n"
+ "}\n"
+ "container pr {\n"
+ " leaf prlf {\n"
+ " type uint32;\n"
+ " }\n"
+ "}\n"
+ "list lt {\n"
+ " key \"name\";\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ "}\n");
+ expect =
+ "module: a35\n"
+ " +--rw pr\n"
+ " | +--rw prlf? uint32\n"
+ " +--mp lt* [name]\n"
+ " +--rw tlist/* [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--mp ncmp/\n"
+ " +--rw not-compiled/\n"
+ " | +--rw first? string\n"
+ " | +--rw second? string\n"
+ " +--rw pr@\n"
+ " | +--rw prlf? uint32\n"
+ " +--rw name string\n";
+ data = EXT_DATA("a35",
+ "<module>\n"
+ " <name>sm-mod</name>\n"
+ " <namespace>urn:sm-mod</namespace>\n"
+ "</module>\n",
+ SCHEMA_REF_SHARED(
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":prlf</parent-reference>\n"));
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * notifications and rpcs in mounted module
+ */
+ orig = SM_MOD_MAIN("a36",
+ "container cont {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n");
+ expect =
+ "module: a36\n"
+ " +--mp cont\n"
+ " +--rw tlist/* [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw cont/\n"
+ " | +---x cr\n"
+ " | | +---w input\n"
+ " | | | +---w in? string\n"
+ " | | +--ro output\n"
+ " | | +--ro out? string\n"
+ " | +---n cn\n"
+ " +---x r1/\n"
+ " +---x r2/\n"
+ " +---n n1/\n"
+ " +---n n2/\n";
+ data = EXT_DATA("a36",
+ "<module>\n"
+ " <name>sm-rpcnotif</name>\n"
+ " <namespace>urn:rpcnotif</namespace>\n"
+ "</module>\n",
+ SCHEMA_REF_INLINE);
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /*
+ * parent-ref composes the '@' subtree
+ */
+ orig = SM_MOD_MAIN("a37",
+ "container pr {\n"
+ " leaf ignored_node {\n"
+ " type string;\n"
+ " }\n"
+ " container cont {\n"
+ " leaf ignored_lf {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " container ignored_subtree {\n"
+ " leaf ignored_lf {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " container cont_sibl {\n"
+ " leaf slf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf lf {\n"
+ " type uint32;\n"
+ " }\n"
+ "}\n"
+ "container cont_mount {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n");
+ expect =
+ "module: a37\n"
+ " +--rw pr\n"
+ " | +--rw ignored_node? string\n"
+ " | +--rw cont\n"
+ " | | +--rw ignored_lf? uint32\n"
+ " | +--rw ignored_subtree\n"
+ " | | +--rw ignored_lf? uint32\n"
+ " | +--rw cont_sibl\n"
+ " | | +--rw slf? string\n"
+ " | +--rw lf? uint32\n"
+ " +--mp cont_mount\n"
+ " +--rw tlist/* [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw pr@\n"
+ " +--rw cont\n"
+ " +--rw cont_sibl\n"
+ " | +--rw slf? string\n"
+ " +--rw lf? uint32\n";
+ data = EXT_DATA("a37", "", SCHEMA_REF_SHARED(
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont_sibl/slf</parent-reference>\n"
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont</parent-reference>\n"
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":lf</parent-reference>\n"));
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_TEARDOWN;
+}
+
+static void
+structure(void **state)
+{
+ TEST_LOCAL_SETUP;
+
+ orig =
+ "module example-module {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:example:example-module\";\n"
+ " prefix exm;\n"
+ "\n"
+ " import ietf-yang-structure-ext {\n"
+ " prefix sx;\n"
+ " }\n"
+ "\n"
+ " sx:structure address-book {\n"
+ " list address {\n"
+ " key \"last first\";\n"
+ " leaf last {\n"
+ " type string;\n"
+ " }\n"
+ " leaf first {\n"
+ " type string;\n"
+ " }\n"
+ " leaf street {\n"
+ " type string;\n"
+ " }\n"
+ " leaf city {\n"
+ " type string;\n"
+ " }\n"
+ " leaf state {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from RFC 8791, Appendix A.1 */
+ expect =
+ "module: example-module\n"
+ "\n"
+ " structure address-book:\n"
+ " +-- address* [last first]\n"
+ " +-- last string\n"
+ " +-- first string\n"
+ " +-- street? string\n"
+ " +-- city? string\n"
+ " +-- state? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ orig =
+ "module example-module-aug {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:example:example-module-aug\";\n"
+ " prefix exma;\n"
+ "\n"
+ " import ietf-yang-structure-ext {\n"
+ " prefix sx;\n"
+ " }\n"
+ " import example-module {\n"
+ " prefix exm;\n"
+ " }\n"
+ "\n"
+ " sx:augment-structure \"/exm:address-book/exm:address\" {\n"
+ " leaf county {\n"
+ " type string;\n"
+ " }\n"
+ " leaf zipcode {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* from RFC 8791, Appendix A.2 */
+ expect =
+ "module: example-module-aug\n"
+ "\n"
+ " augment-structure /exm:address-book/exm:address:\n"
+ " +-- county? string\n"
+ " +-- zipcode? string\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(base_sections),
+ UTEST(node_status),
+ UTEST(node_config_flags),
+ UTEST(node_rpcs_flags),
+ UTEST(node_grouping_flags),
+ UTEST(notif_inside_container),
+ UTEST(node_choice),
+ UTEST(node_case),
+ UTEST(optional_opts),
+ UTEST(presence_container),
+ UTEST(node_keys),
+ UTEST(node_type_target),
+ UTEST(node_type_leafref),
+ UTEST(node_iffeatures),
+ UTEST(indent_wrapper),
+ UTEST(line_length_twiddling),
+ UTEST(break_before_leafref),
+ UTEST(break_before_leafref_and_iffeature),
+ UTEST(basic_unified_indent_before_type),
+ UTEST(twiddling_unified_indent_before_type),
+ UTEST(inheritance_of_config_flag),
+ UTEST(inheritance_of_status_flag),
+ UTEST(key_leaf_is_always_mandatory_true),
+ UTEST(transition_between_rpc_and_notif),
+ UTEST(local_augment),
+ UTEST(print_compiled_node),
+ UTEST(print_parsed_submodule),
+ UTEST(yang_data),
+ UTEST(mount_point),
+ UTEST(structure),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c
new file mode 100644
index 0000000..175b569
--- /dev/null
+++ b/tests/utests/schema/test_schema.c
@@ -0,0 +1,1887 @@
+/**
+ * @file test_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for schema related functions
+ *
+ * Copyright (c) 2018 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <string.h>
+
+#include "compat.h"
+#include "context.h"
+#include "log.h"
+#include "parser_schema.h"
+#include "plugins_exts.h"
+#include "set.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+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))
+{
+ *module_data = user_data;
+ if ((*module_data)[0] == '<') {
+ *format = LYS_IN_YIN;
+ } else {
+ *format = LYS_IN_YANG;
+ }
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+#define TEST_YANG_MODULE_10(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \
+ "module "MOD_NAME" { namespace "MOD_NS"; prefix "MOD_PREFIX"; "CONTENT"}"
+
+#define TEST_YANG_MODULE_11(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \
+ "module "MOD_NAME" {yang-version 1.1; namespace "MOD_NS"; prefix "MOD_PREFIX"; "CONTENT"}"
+
+#define TEST_YIN_MODULE_10(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \
+ "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\""MOD_NAME"\">" \
+ "<namespace uri=\""MOD_NS"\"/><prefix value=\""MOD_PREFIX"\"/>"CONTENT"</module>"
+
+#define TEST_YIN_MODULE_11(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \
+ "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\""MOD_NAME"\"><yang-version value=\"1.1\"/>" \
+ "<namespace uri=\""MOD_NS"\"/><prefix value=\""MOD_PREFIX"\"/>"CONTENT"</module>"
+
+#define TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, STR) \
+ if (YIN) { \
+ if (RFC7950) { \
+ STR = TEST_YIN_MODULE_11(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \
+ } else { \
+ STR = TEST_YIN_MODULE_10(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \
+ } \
+ } else { /* YANG */ \
+ if (RFC7950) { \
+ STR = TEST_YANG_MODULE_11(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \
+ } else { \
+ STR = TEST_YANG_MODULE_10(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \
+ } \
+ }
+
+#define TEST_SCHEMA_OK(RFC7950, YIN, MOD_NAME, CONTENT, RESULT) \
+ { \
+ const char *test_str__; \
+ TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, &(RESULT))); \
+ }
+
+#define TEST_SCHEMA_ERR(RFC7950, YIN, MOD_NAME, CONTENT, ERRMSG, ERRPATH) \
+ { \
+ const char *test_str__; \
+ TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \
+ assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \
+ CHECK_LOG_CTX(ERRMSG, ERRPATH); \
+ }
+
+#define TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, MOD_NAME, CONTENT, ERRMSG, ERRPATH) \
+ { \
+ const char *test_str__; \
+ TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \
+ assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \
+ CHECK_LOG_CTX("Parsing module \""MOD_NAME"\" failed.", NULL, ERRMSG, ERRPATH); \
+ }
+
+#define TEST_STMT_DUP(RFC7950, YIN, STMT, MEMBER, VALUE1, VALUE2, LINE) \
+ if (YIN) { \
+ TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, "dup", "", "Duplicate keyword \""MEMBER"\".", "Line number "LINE"."); \
+ } else { \
+ TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, "dup", STMT"{"MEMBER" "VALUE1";"MEMBER" "VALUE2";}", \
+ "Duplicate keyword \""MEMBER"\".", "Line number "LINE"."); \
+ }
+
+#define TEST_STMT_SUBSTM_ERR(RFC7950, STMT, SUBSTMT, VALUE) ;\
+ TEST_SCHEMA_PARSE_ERR(RFC7950, 0, "inv", STMT" test {"SUBSTMT" "VALUE";}", \
+ "Invalid keyword \""SUBSTMT"\" as a child of \""STMT"\".", "Line number 1.");
+
+struct module_clb_list {
+ const char *name;
+ const char *data;
+};
+
+static LY_ERR
+module_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))
+{
+ struct module_clb_list *list = (struct module_clb_list *)user_data;
+
+ for (unsigned int i = 0; list[i].data; i++) {
+
+ if ((submod_name && !strcmp(list[i].name, submod_name)) ||
+ (!submod_name && mod_name && !strcmp(list[i].name, mod_name))) {
+ *module_data = list[i].data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+ }
+ }
+ return LY_EINVAL;
+}
+
+static void
+test_getnext(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node *node = NULL, *four;
+ const struct lysc_node_container *cont;
+ const struct lysc_action *rpc;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;"
+ "container a { container one {presence test;} leaf two {type string;} leaf-list three {type string;}"
+ " list four {config false;} choice x { leaf five {type string;} case y {leaf six {type string;}}}"
+ " anyxml seven; action eight {input {leaf eight-input {type string;}} output {leaf eight-output {type string;}}}"
+ " notification nine {leaf nine-data {type string;}}}"
+ "leaf b {type string;} leaf-list c {type string;} list d {config false;}"
+ "choice x { case empty-x { choice empty-xc { case nothing;}} leaf e {type string;} case y {leaf f {type string;}}} anyxml g;"
+ "rpc h {input {leaf h-input {type string;}} output {leaf h-output {type string;}}}"
+ "rpc i;"
+ "notification j {leaf i-data {type string;}}"
+ "notification k;}", LYS_IN_YANG, &mod));
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("a", node->name);
+ cont = (const struct lysc_node_container *)node;
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("b", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("c", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("d", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("e", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("f", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("g", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("h", node->name);
+ rpc = (const struct lysc_action *)node;
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("i", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("j", node->name);
+ assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ assert_string_equal("k", node->name);
+ assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+ /* Inside container */
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("one", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("two", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("three", node->name);
+ assert_non_null(node = four = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("four", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("five", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("six", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("seven", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("eight", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ assert_string_equal("nine", node->name);
+ assert_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0));
+ /* Inside RPC */
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, 0));
+ assert_string_equal("h-input", node->name);
+ assert_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, 0));
+
+ /* options */
+ assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCHOICE));
+ assert_string_equal("x", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCHOICE));
+ assert_string_equal("seven", node->name);
+
+ assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_NOCHOICE));
+ assert_string_equal("seven", node->name);
+
+ assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE));
+ assert_string_equal("five", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE));
+ assert_string_equal("y", node->name);
+ assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE));
+ assert_string_equal("seven", node->name);
+
+ assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_INTONPCONT));
+ assert_string_equal("one", node->name);
+
+ assert_non_null(node = lys_getnext(NULL, (const struct lysc_node *)rpc, mod->compiled, LYS_GETNEXT_OUTPUT));
+ assert_string_equal("h-output", node->name);
+ assert_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, LYS_GETNEXT_OUTPUT));
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; rpc c;}", LYS_IN_YANG, &mod));
+ assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
+ assert_string_equal("c", node->name);
+ assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; notification d;}", LYS_IN_YANG, &mod));
+ assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
+ assert_string_equal("d", node->name);
+ assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e; container c {container cc;} leaf a {type string;}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
+ assert_string_equal("c", node->name);
+ assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_INTONPCONT));
+ assert_string_equal("a", node->name);
+}
+
+static void
+test_date(void **state)
+{
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, NULL, 0, "date"));
+ CHECK_LOG("Invalid argument date (lysp_check_date()).", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "x", 1, "date"));
+ CHECK_LOG("Invalid length 1 of a date.", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "nonsencexx", 10, "date"));
+ CHECK_LOG("Invalid value \"nonsencexx\" of \"date\".", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "123x-11-11", 10, "date"));
+ CHECK_LOG("Invalid value \"123x-11-11\" of \"date\".", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-13-11", 10, "date"));
+ CHECK_LOG("Invalid value \"2018-13-11\" of \"date\".", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-11-41", 10, "date"));
+ CHECK_LOG("Invalid value \"2018-11-41\" of \"date\".", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-02-29", 10, "date"));
+ CHECK_LOG("Invalid value \"2018-02-29\" of \"date\".", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018.02-28", 10, "date"));
+ CHECK_LOG("Invalid value \"2018.02-28\" of \"date\".", NULL);
+ assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-02.28", 10, "date"));
+ CHECK_LOG("Invalid value \"2018-02.28\" of \"date\".", NULL);
+
+ assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-11-11", 10, "date"));
+ assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-02-28", 10, "date"));
+ assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2016-02-29", 10, "date"));
+}
+
+static void
+test_revisions(void **state)
+{
+ struct lysp_revision *revs = NULL, *rev;
+
+ /* no error, it just does nothing */
+ lysp_sort_revisions(NULL);
+ CHECK_LOG(NULL, NULL);
+
+ /* revisions are stored in wrong order - the newest is the last */
+ LY_ARRAY_NEW_RET(NULL, revs, rev, );
+ strcpy(rev->date, "2018-01-01");
+ LY_ARRAY_NEW_RET(NULL, revs, rev, );
+ strcpy(rev->date, "2018-12-31");
+
+ assert_int_equal(2, LY_ARRAY_COUNT(revs));
+ assert_string_equal("2018-01-01", &revs[0]);
+ assert_string_equal("2018-12-31", &revs[1]);
+ /* the order should be fixed, so the newest revision will be the first in the array */
+ lysp_sort_revisions(revs);
+ assert_string_equal("2018-12-31", &revs[0]);
+ assert_string_equal("2018-01-01", &revs[1]);
+
+ LY_ARRAY_FREE(revs);
+}
+
+static void
+test_collision_typedef(void **state)
+{
+ const char *str;
+ char *submod;
+ struct module_clb_list list[3] = {0};
+
+ list[0].name = "asub";
+ list[1].name = "bsub";
+
+ /* collision with a built-in type */
+ str = "module a {namespace urn:a; prefix a; typedef binary {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"binary\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef bits {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"bits\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef boolean {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"boolean\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef decimal64 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"decimal64\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef empty {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"empty\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef enumeration {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"enumeration\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef int8 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"int8\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef int16 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"int16\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef int32 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"int32\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef int64 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"int64\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef instance-identifier {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"instance-identifier\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef identityref {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"identityref\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef leafref {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"leafref\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef string {type int8;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"string\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef union {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"union\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef uint8 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"uint8\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef uint16 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"uint16\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef uint32 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"uint32\" of typedef statement - name collision with a built-in type.", NULL);
+ str = "module a {namespace urn:a; prefix a; typedef uint64 {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"uint64\" of typedef statement - name collision with a built-in type.", NULL);
+
+ str = "module mytypes {namespace urn:types; prefix t; typedef binary_ {type string;} typedef bits_ {type string;} typedef boolean_ {type string;} "
+ "typedef decimal64_ {type string;} typedef empty_ {type string;} typedef enumeration_ {type string;} typedef int8_ {type string;} typedef int16_ {type string;}"
+ "typedef int32_ {type string;} typedef int64_ {type string;} typedef instance-identifier_ {type string;} typedef identityref_ {type string;}"
+ "typedef leafref_ {type string;} typedef string_ {type int8;} typedef union_ {type string;} typedef uint8_ {type string;} typedef uint16_ {type string;}"
+ "typedef uint32_ {type string;} typedef uint64_ {type string;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+
+ /* collision in node's scope */
+ str = "module a {namespace urn:a; prefix a; container c {typedef y {type int8;} typedef y {type string;}}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"y\" of typedef statement - name collision with sibling type.", NULL);
+
+ /* collision with parent node */
+ str = "module a {namespace urn:a; prefix a; container c {container d {typedef y {type int8;}} typedef y {type string;}}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"y\" of typedef statement - name collision with another scoped type.", NULL);
+
+ /* collision with module's top-level */
+ str = "module a {namespace urn:a; prefix a; typedef x {type string;} container c {typedef x {type int8;}}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
+
+ /* collision of submodule's node with module's top-level */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {typedef x {type string;}}}");
+ str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
+
+ /* collision of module's node with submodule's top-level */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type int8;}}");
+ str = "module a {namespace urn:a; prefix a; include b; container c {typedef x {type string;}}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
+
+ /* collision of submodule's node with another submodule's top-level */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
+ list[0].data = "submodule asub {belongs-to a {prefix a;} typedef g {type int;}}";
+ list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {typedef g {type int;}}}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of typedef statement - scoped type collide with a top-level type.", NULL);
+
+ /* collision of module's top-levels */
+ str = "module a {namespace urn:a; prefix a; typedef test {type string;} typedef test {type int8;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"test\" of typedef statement - name collision with another top-level type.", NULL);
+
+ /* collision of submodule's top-levels */
+ submod = "submodule asub {belongs-to a {prefix a;} typedef g {type int;} typedef g {type int;}}";
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL);
+
+ /* collision of module's top-level with submodule's top-levels */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type string;}}");
+ str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of typedef statement - name collision with another top-level type.", NULL);
+
+ /* collision of submodule's top-level with another submodule's top-levels */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
+ list[0].data = "submodule asub {belongs-to a {prefix a;} typedef g {type int;}}";
+ list[1].data = "submodule bsub {belongs-to a {prefix a;} typedef g {type int;}}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL);
+
+ /* error in type-stmt */
+ str = "module a {namespace urn:a; prefix a; container c {typedef x {type t{}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Unexpected end-of-input.", "Line number 1.");
+ UTEST_LOG_CLEAN;
+
+ /* no collision if the same names are in different scope */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
+ "container c {typedef g {type int;}} container d {typedef g {type int;}}}";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+}
+
+static void
+test_collision_grouping(void **state)
+{
+ const char *str;
+ char *submod;
+ struct module_clb_list list[3] = {0};
+
+ list[0].name = "asub";
+ list[1].name = "bsub";
+
+ /* collision in node's scope */
+ str = "module a {namespace urn:a; prefix a; container c {grouping y; grouping y;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"y\" of grouping statement - name collision with sibling grouping.", NULL);
+
+ /* collision with parent node */
+ str = "module a {namespace urn:a; prefix a; container c {container d {grouping y;} grouping y;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"y\" of grouping statement - name collision with another scoped grouping.", NULL);
+
+ /* collision with module's top-level */
+ str = "module a {namespace urn:a; prefix a; grouping x; container c {grouping x;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+
+ /* collision of submodule's node with module's top-level */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {grouping x;}}");
+ str = "module a {namespace urn:a; prefix a; include b; grouping x;}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+
+ /* collision of module's node with submodule's top-level */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}");
+ str = "module a {namespace urn:a; prefix a; include b; container c {grouping x;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+
+ /* collision of submodule's node with another submodule's top-level */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
+ list[0].data = "submodule asub {belongs-to a {prefix a;} grouping g;}";
+ list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {grouping g;}}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+
+ /* collision of module's top-levels */
+ str = "module a {namespace urn:a; prefix a; grouping test; grouping test;}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"test\" of grouping statement - name collision with another top-level grouping.", NULL);
+
+ /* collision of submodule's top-levels */
+ submod = "submodule asub {belongs-to a {prefix a;} grouping g; grouping g;}";
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL);
+
+ /* collision of module's top-level with submodule's top-levels */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}");
+ str = "module a {namespace urn:a; prefix a; include b; grouping x;}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"x\" of grouping statement - name collision with another top-level grouping.", NULL);
+
+ /* collision of submodule's top-level with another submodule's top-levels */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
+ list[0].data = "submodule asub {belongs-to a {prefix a;} grouping g;}";
+ list[1].data = "submodule bsub {belongs-to a {prefix a;} grouping g;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL);
+
+ /* collision in nested groupings, top-level */
+ str = "module a {namespace urn:a; prefix a; grouping g {grouping g;}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+
+ /* collision in nested groupings, in node */
+ str = "module a {namespace urn:a; prefix a; container c {grouping g {grouping g;}}}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of grouping statement - name collision with another scoped grouping.", NULL);
+
+ /* no collision if the same names are in different scope */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
+ "container c {grouping g;} container d {grouping g;}}";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+}
+
+static void
+test_collision_identity(void **state)
+{
+ const char *str;
+ char *submod;
+ struct module_clb_list list[3] = {0};
+
+ list[0].name = "asub";
+ list[1].name = "bsub";
+
+ /* collision of module's top-levels */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; identity g; identity g;}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+
+ /* collision of submodule's top-levels */
+ submod = "submodule asub {belongs-to a {prefix a;} identity g; identity g;}";
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+
+ /* collision of module's top-level with submodule's top-levels */
+ submod = "submodule asub {belongs-to a {prefix a;} identity g;}";
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; identity g;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+
+ /* collision of submodule's top-level with another submodule's top-levels */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
+ list[0].data = "submodule asub {belongs-to a {prefix a;} identity g;}";
+ list[1].data = "submodule bsub {belongs-to a {prefix a;} identity g;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+}
+
+static void
+test_collision_feature(void **state)
+{
+ const char *str;
+ char *submod;
+ struct module_clb_list list[3] = {0};
+
+ list[0].name = "asub";
+ list[1].name = "bsub";
+
+ /* collision of module's top-levels */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; feature g; feature g;}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+
+ /* collision of submodule's top-levels */
+ submod = "submodule asub {belongs-to a {prefix a;} feature g; feature g;}";
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+
+ /* collision of module's top-level with submodule's top-levels */
+ submod = "submodule asub {belongs-to a {prefix a;} feature g;}";
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; feature g;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+
+ /* collision of submodule's top-level with another submodule's top-levels */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
+ list[0].data = "submodule asub {belongs-to a {prefix a;} feature g;}";
+ list[1].data = "submodule bsub {belongs-to a {prefix a;} feature g;}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
+ "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+}
+
+static void
+test_accessible_tree(void **state)
+{
+ const char *str;
+
+ /* config -> config */
+ str = "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " container cont {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " container cont2 {\n"
+ " leaf l2 {\n"
+ " must ../../cont/l;\n"
+ " type leafref {\n"
+ " path /cont/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* config -> state leafref */
+ str = "module b {\n"
+ " namespace urn:b;\n"
+ " prefix b;\n"
+ " container cont {\n"
+ " config false;\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " container cont2 {\n"
+ " leaf l2 {\n"
+ " type leafref {\n"
+ " path /cont/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid leafref path \"/cont/l\" - target is supposed to represent configuration data"
+ " (as the leafref does), but it does not.", "Schema location \"/b:cont2/l2\".");
+
+ /* config -> state must */
+ str = "module b {\n"
+ " namespace urn:b;\n"
+ " prefix b;\n"
+ " container cont {\n"
+ " config false;\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " container cont2 {\n"
+ " leaf l2 {\n"
+ " must ../../cont/l;\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX("Schema node \"cont\" for parent \"<config-root>\" not found; in expr \"../../cont\" "
+ "with context node \"/b:cont2/l2\".", NULL);
+
+ /* state -> config */
+ str = "module c {\n"
+ " namespace urn:c;\n"
+ " prefix c;\n"
+ " container cont {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " container cont2 {\n"
+ " config false;\n"
+ " leaf l2 {\n"
+ " must ../../cont/l;\n"
+ " type leafref {\n"
+ " path /cont/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* notif -> state */
+ str = "module d {\n"
+ " namespace urn:d;\n"
+ " prefix d;\n"
+ " container cont {\n"
+ " config false;\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " notification notif {\n"
+ " leaf l2 {\n"
+ " must ../../cont/l;\n"
+ " type leafref {\n"
+ " path /cont/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* notif -> notif */
+ str = "module e {\n"
+ " namespace urn:e;\n"
+ " prefix e;\n"
+ " notification notif {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " leaf l2 {\n"
+ " must ../../notif/l;\n"
+ " type leafref {\n"
+ " path /notif/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* rpc input -> state */
+ str = "module f {\n"
+ " namespace urn:f;\n"
+ " prefix f;\n"
+ " container cont {\n"
+ " config false;\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " rpc rp {\n"
+ " input {\n"
+ " leaf l2 {\n"
+ " must ../../cont/l;\n"
+ " type leafref {\n"
+ " path /cont/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* rpc input -> rpc input */
+ str = "module g {\n"
+ " namespace urn:g;\n"
+ " prefix g;\n"
+ " rpc rp {\n"
+ " input {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " leaf l2 {\n"
+ " must ../l;\n"
+ " type leafref {\n"
+ " path /rp/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* rpc input -> rpc output leafref */
+ str = "module h {\n"
+ " namespace urn:h;\n"
+ " prefix h;\n"
+ " rpc rp {\n"
+ " input {\n"
+ " leaf l2 {\n"
+ " type leafref {\n"
+ " path /rp/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Not found node \"l\" in path.", "Schema location \"/h:rp/input/l2\".");
+
+ /* rpc input -> rpc output must */
+ str = "module h {\n"
+ " namespace urn:h;\n"
+ " prefix h;\n"
+ " rpc rp {\n"
+ " input {\n"
+ " leaf l2 {\n"
+ " must ../l;\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX("Schema node \"l\" for parent \"/h:rp\" not found; in expr \"../l\" with context node \"/h:rp/input/l2\".", NULL);
+
+ /* rpc input -> notif leafref */
+ str = "module i {\n"
+ " namespace urn:i;\n"
+ " prefix i;\n"
+ " rpc rp {\n"
+ " input {\n"
+ " leaf l2 {\n"
+ " type leafref {\n"
+ " path ../../notif/l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " notification notif {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Not found node \"notif\" in path.", "Schema location \"/i:rp/input/l2\".");
+
+ /* rpc input -> notif must */
+ str = "module i {\n"
+ " namespace urn:i;\n"
+ " prefix i;\n"
+ " rpc rp {\n"
+ " input {\n"
+ " leaf l2 {\n"
+ " must /notif/l;\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " notification notif {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX("Schema node \"notif\" for parent \"<root>\" not found; in expr \"/notif\" "
+ "with context node \"/i:rp/input/l2\".", NULL);
+
+ /* action output -> state */
+ str = "module j {\n"
+ " yang-version 1.1;\n"
+ " namespace urn:j;\n"
+ " prefix j;\n"
+ " container cont {\n"
+ " list ll {\n"
+ " key k;\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " action act {\n"
+ " output {\n"
+ " leaf l2 {\n"
+ " must /cont/l;\n"
+ " type leafref {\n"
+ " path ../../../l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " leaf l {\n"
+ " config false;\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* action output -> action input leafref */
+ str = "module k {\n"
+ " yang-version 1.1;\n"
+ " namespace urn:k;\n"
+ " prefix k;\n"
+ " container cont {\n"
+ " list ll {\n"
+ " key k;\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " action act {\n"
+ " input {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf l2 {\n"
+ " type leafref {\n"
+ " path ../l;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Not found node \"l\" in path.", "Schema location \"/k:cont/ll/act/output/l2\".");
+
+ /* action output -> action input must */
+ str = "module k {\n"
+ " yang-version 1.1;\n"
+ " namespace urn:k;\n"
+ " prefix k;\n"
+ " container cont {\n"
+ " list ll {\n"
+ " key k;\n"
+ " leaf k {\n"
+ " type string;\n"
+ " }\n"
+ " action act {\n"
+ " input {\n"
+ " leaf l {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf l2 {\n"
+ " must /cont/ll/act/l;\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX("Schema node \"l\" for parent \"/k:cont/ll/act\" not found; in expr \"/cont/ll/act/l\" "
+ "with context node \"/k:cont/ll/act/output/l2\".", NULL);
+}
+
+static void
+test_includes(void **state)
+{
+ struct lys_module *mod;
+
+ {
+ /* YANG 1.0 - the missing include sub_a_two in main_a will be injected from sub_a_one */
+ struct module_clb_list list[] = {
+ {"main_a", "module main_a { namespace urn:test:main_a; prefix ma; include sub_a_one;}"},
+ {"sub_a_one", "submodule sub_a_one { belongs-to main_a { prefix ma; } include sub_a_two;}"},
+ {"sub_a_two", "submodule sub_a_two { belongs-to main_a { prefix ma; } }"},
+ {NULL, NULL}
+ };
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ mod = ly_ctx_load_module(UTEST_LYCTX, "main_a", NULL, NULL);
+ assert_non_null(mod);
+ assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->includes));
+ assert_true(mod->parsed->includes[1].injected);
+ }
+
+ {
+ /* YANG 1.1 - the missing include sub_b_two in main_b is error */
+ struct module_clb_list list[] = {
+ {"main_b", "module main_b { yang-version 1.1; namespace urn:test:main_b; prefix mb; include sub_b_one;}"},
+ {"sub_b_one", "submodule sub_b_one { yang-version 1.1; belongs-to main_b { prefix mb; } include sub_b_two;}"},
+ {"sub_b_two", "submodule sub_b_two { yang-version 1.1; belongs-to main_b { prefix mb; } }"},
+ {NULL, NULL}
+ };
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ mod = ly_ctx_load_module(UTEST_LYCTX, "main_b", NULL, NULL);
+ assert_null(mod);
+ CHECK_LOG_CTX("Loading \"main_b\" module failed.", NULL,
+ "Data model \"main_b\" not found in local searchdirs.", NULL,
+ "Parsing module \"main_b\" failed.", NULL,
+ "Including \"sub_b_one\" submodule into \"main_b\" failed.", NULL,
+ "Data model \"sub_b_one\" not found in local searchdirs.", NULL,
+ "Parsing submodule \"sub_b_one\" failed.", NULL,
+ "YANG 1.1 requires all submodules to be included from main module. But submodule \"sub_b_one\" includes "
+ "submodule \"sub_b_two\" which is not included by main module \"main_b\".", NULL,
+ "YANG version 1.1 expects all includes in main module, includes in submodules (sub_b_one) are not necessary.", NULL);
+ }
+
+ {
+ /* YANG 1.1 - all includes are in main_c, includes in submodules are not necessary, so expect warning */
+ struct module_clb_list list[] = {
+ {"main_c", "module main_c { yang-version 1.1; namespace urn:test:main_c; prefix mc; include sub_c_one; include sub_c_two;}"},
+ {"sub_c_one", "submodule sub_c_one { yang-version 1.1; belongs-to main_c { prefix mc; } include sub_c_two;}"},
+ {"sub_c_two", "submodule sub_c_two { yang-version 1.1; belongs-to main_c { prefix mc; } include sub_c_one;}"},
+ {NULL, NULL}
+ };
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
+ mod = ly_ctx_load_module(UTEST_LYCTX, "main_c", NULL, NULL);
+ assert_non_null(mod);
+ assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->includes));
+ assert_false(mod->parsed->includes[1].injected);
+ /* result is ok, but log includes the warning */
+ CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_two) are not necessary.", NULL);
+ }
+}
+
+static void
+test_key_order(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node *node;
+
+ struct module_clb_list list1[] = {
+ {"a", "module a {"
+ "yang-version 1.1;"
+ "namespace urn:test:a;"
+ "prefix a;"
+ "list l {"
+ " key \"k1 k2\";"
+ " leaf k2 {type string;}"
+ " leaf k1 {type string;}"
+ "}"
+ "}"},
+ {NULL, NULL}
+ };
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list1);
+ mod = ly_ctx_load_module(UTEST_LYCTX, "a", NULL, NULL);
+ assert_non_null(mod);
+
+ node = lysc_node_child(mod->compiled->data);
+ assert_string_equal("k1", node->name);
+ node = node->next;
+ assert_string_equal("k2", node->name);
+
+ struct module_clb_list list2[] = {
+ {"b", "module b {"
+ "yang-version 1.1;"
+ "namespace urn:test:b;"
+ "prefix b;"
+ "list l {"
+ " key \"k1 k2 k3 k4\";"
+ " leaf k4 {type string;}"
+ " container c {"
+ " leaf l1 {type string;}"
+ " }"
+ " leaf k2 {type string;}"
+ " leaf l2 {type string;}"
+ " leaf k1 {type string;}"
+ " leaf k3 {type string;}"
+ "}"
+ "}"},
+ {NULL, NULL}
+ };
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list2);
+ mod = ly_ctx_load_module(UTEST_LYCTX, "b", NULL, NULL);
+ assert_non_null(mod);
+
+ node = lysc_node_child(mod->compiled->data);
+ assert_string_equal("k1", node->name);
+ node = node->next;
+ assert_string_equal("k2", node->name);
+ node = node->next;
+ assert_string_equal("k3", node->name);
+ node = node->next;
+ assert_string_equal("k4", node->name);
+}
+
+static void
+test_disabled_enum(void **state)
+{
+ const char *str;
+
+ /* no enabled enum */
+ str = "module a {"
+ "yang-version 1.1;"
+ "namespace urn:test:a;"
+ "prefix a;"
+ "feature f;"
+ "leaf l {type enumeration {"
+ " enum e1 {if-feature f;}"
+ " enum e2 {if-feature f;}"
+ "}}"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Enumeration type of node \"l\" without any (or all disabled) valid values.", "Schema location \"/a:l\".");
+
+ /* disabled default value */
+ str = "module a {"
+ "yang-version 1.1;"
+ "namespace urn:test:a;"
+ "prefix a;"
+ "feature f;"
+ "leaf l {"
+ " type enumeration {"
+ " enum e1 {if-feature f;}"
+ " enum e2;"
+ " }"
+ " default e1;"
+ "}"
+ "}";
+ assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid enumeration value \"e1\".).",
+ "Schema location \"/a:l\".");
+}
+
+static void
+test_identity(void **state)
+{
+ struct lys_module *mod, *mod_imp;
+
+ /*
+ * parsing YANG
+ */
+ TEST_STMT_DUP(1, 0, "identity id", "description", "a", "b", "1");
+ TEST_STMT_DUP(1, 0, "identity id", "reference", "a", "b", "1");
+ TEST_STMT_DUP(1, 0, "identity id", "status", "current", "obsolete", "1");
+
+ /* full content */
+ TEST_SCHEMA_OK(1, 0, "identityone",
+ "identity test {base \"a\";base b; description text;reference \'another text\';status current; if-feature x;if-feature y; identityone:ext;}"
+ "identity a; identity b; extension ext; feature x; feature y;", mod);
+ assert_non_null(mod->parsed->identities);
+ assert_int_equal(3, LY_ARRAY_COUNT(mod->parsed->identities));
+
+ /* invalid substatement */
+ TEST_STMT_SUBSTM_ERR(0, "identity", "organization", "XXX");
+
+ /*
+ * parsing YIN
+ */
+ /* max subelems */
+ TEST_SCHEMA_OK(1, 1, "identityone-yin", "<identity name=\"ident-name\">"
+ "<if-feature name=\"iff\"/>"
+ "<base name=\"base-name\"/>"
+ "<status value=\"deprecated\"/>"
+ "<description><text>desc</text></description>"
+ "<reference><text>ref</text></reference>"
+ /* TODO yin-extension-prefix-compilation-bug "<myext:ext xmlns:myext=\"urn:libyang:test:identityone-yin\"/>" */
+ "</identity><extension name=\"ext\"/><identity name=\"base-name\"/><feature name=\"iff\"/>", mod);
+ assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->identities));
+ assert_string_equal(mod->parsed->identities[0].name, "ident-name");
+ assert_string_equal(mod->parsed->identities[0].bases[0], "base-name");
+ assert_string_equal(mod->parsed->identities[0].iffeatures[0].str, "iff");
+ assert_string_equal(mod->parsed->identities[0].dsc, "desc");
+ assert_string_equal(mod->parsed->identities[0].ref, "ref");
+ assert_true(mod->parsed->identities[0].flags & LYS_STATUS_DEPRC);
+ /*assert_string_equal(mod->parsed->identities[0].exts[0].name, "ext");
+ assert_non_null(mod->parsed->identities[0].exts[0].compiled);
+ assert_int_equal(mod->parsed->identities[0].exts[0].yin, 1);
+ assert_int_equal(mod->parsed->identities[0].exts[0].insubstmt_index, 0);
+ assert_int_equal(mod->parsed->identities[0].exts[0].insubstmt, LYEXT_SUBSTMT_SELF);*/
+
+ /* min subelems */
+ TEST_SCHEMA_OK(1, 1, "identitytwo-yin", "<identity name=\"ident-name\" />", mod);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->parsed->identities));
+ assert_string_equal(mod->parsed->identities[0].name, "ident-name");
+
+ /* invalid substatement */
+ TEST_SCHEMA_PARSE_ERR(0, 1, "inv", "<identity name=\"ident-name\"><if-feature name=\"iff\"/></identity>",
+ "Invalid sub-elemnt \"if-feature\" of \"identity\" element - "
+ "this sub-element is allowed only in modules with version 1.1 or newer.", "Line number 1.");
+
+ /*
+ * compiling
+ */
+ TEST_SCHEMA_OK(0, 0, "a", "identity a1;", mod_imp);
+ TEST_SCHEMA_OK(1, 0, "b", "import a {prefix a;}"
+ "identity b1; identity b2; identity b3 {base b1; base b:b2; base a:a1;}"
+ "identity b4 {base b:b1; base b3;}", mod);
+ assert_non_null(mod_imp->compiled);
+ assert_non_null(mod_imp->identities);
+ assert_non_null(mod->identities);
+ assert_non_null(mod_imp->identities[0].derived);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod_imp->identities[0].derived));
+ assert_ptr_equal(mod_imp->identities[0].derived[0], &mod->identities[2]);
+ assert_non_null(mod->identities[0].derived);
+ assert_int_equal(2, LY_ARRAY_COUNT(mod->identities[0].derived));
+ assert_ptr_equal(mod->identities[0].derived[0], &mod->identities[2]);
+ assert_ptr_equal(mod->identities[0].derived[1], &mod->identities[3]);
+ assert_non_null(mod->identities[1].derived);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived));
+ assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[2]);
+ assert_non_null(mod->identities[2].derived);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[2].derived));
+ assert_ptr_equal(mod->identities[2].derived[0], &mod->identities[3]);
+
+ TEST_SCHEMA_OK(1, 0, "c", "identity c2 {base c1;} identity c1;", mod);
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived));
+ assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[0]);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} identity i1;}");
+ TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}", "Unable to find base (i2) of identity \"i1\".", "/inv:{identity='i1'}");
+ TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i1;}", "Identity \"i1\" is derived from itself.", "/inv:{identity='i1'}");
+ TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}",
+ "Identity \"i1\" is indirectly derived from itself.", "/inv:{identity='i3'}");
+
+ /* base in non-implemented module */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
+ "module base {namespace \"urn\"; prefix b; identity i1; identity i2 {base i1;}}");
+ TEST_SCHEMA_OK(0, 0, "ident", "import base {prefix b;} identity ii {base b:i1;}", mod);
+
+ /* default value from non-implemented module */
+ TEST_SCHEMA_ERR(0, 0, "ident2", "import base {prefix b;} leaf l {type identityref {base b:i1;} default b:i2;}",
+ "Invalid default - value does not fit the type (Invalid identityref \"b:i2\" value"
+ " - identity found in non-implemented module \"base\".).", "Schema location \"/ident2:l\".");
+
+ /* default value in typedef from non-implemented module */
+ TEST_SCHEMA_ERR(0, 0, "ident2", "import base {prefix b;} typedef t1 {type identityref {base b:i1;} default b:i2;}"
+ "leaf l {type t1;}", "Invalid default - value does not fit the type (Invalid"
+ " identityref \"b:i2\" value - identity found in non-implemented module \"base\".).", "Schema location \"/ident2:l\".");
+
+ /*
+ * printing
+ */
+
+ /*
+ * cleanup
+ */
+}
+
+static void
+test_feature(void **state)
+{
+ struct lys_module *mod;
+ const struct lysp_feature *f;
+
+ /*
+ * parsing YANG
+ */
+
+ TEST_STMT_DUP(1, 0, "feature f", "description", "a", "b", "1");
+ TEST_STMT_DUP(1, 0, "feature f", "reference", "a", "b", "1");
+ TEST_STMT_DUP(1, 0, "feature f", "status", "current", "obsolete", "1");
+
+ /* full content */
+ TEST_SCHEMA_OK(1, 0, "featureone",
+ "feature test {description text;reference \'another text\';status current; if-feature x; if-feature y; featureone:ext;}"
+ "extension ext; feature x; feature y;", mod);
+ assert_non_null(mod->parsed->features);
+ assert_int_equal(3, LY_ARRAY_COUNT(mod->parsed->features));
+
+ /* invalid substatement */
+ TEST_STMT_SUBSTM_ERR(0, "feature", "organization", "XXX");
+
+ /*
+ * parsing YIN
+ */
+ /* max subelems */
+ TEST_SCHEMA_OK(0, 1, "featureone-yin", "<feature name=\"feature-name\">"
+ "<if-feature name=\"iff\"/>"
+ "<status value=\"deprecated\"/>"
+ "<description><text>desc</text></description>"
+ "<reference><text>ref</text></reference>"
+ /* TODO yin-extension-prefix-compilation-bug "<myext:ext xmlns:myext=\"urn:libyang:test:featureone-yin\"/>" */
+ "</feature><extension name=\"ext\"/><feature name=\"iff\"/>", mod);
+ assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features));
+ assert_string_equal(mod->parsed->features[0].name, "feature-name");
+ assert_string_equal(mod->parsed->features[0].dsc, "desc");
+ assert_true(mod->parsed->features[0].flags & LYS_STATUS_DEPRC);
+ assert_string_equal(mod->parsed->features[0].iffeatures[0].str, "iff");
+ assert_string_equal(mod->parsed->features[0].ref, "ref");
+ /*assert_string_equal(mod->parsed->features[0].exts[0].name, "ext");
+ assert_int_equal(mod->parsed->features[0].exts[0].insubstmt_index, 0);
+ assert_int_equal(mod->parsed->features[0].exts[0].insubstmt, LYEXT_SUBSTMT_SELF);*/
+
+ /* min subelems */
+ TEST_SCHEMA_OK(0, 1, "featuretwo-yin", "<feature name=\"feature-name\"/>", mod)
+ assert_int_equal(1, LY_ARRAY_COUNT(mod->parsed->features));
+ assert_string_equal(mod->parsed->features[0].name, "feature-name");
+
+ /* invalid substatement */
+ TEST_SCHEMA_PARSE_ERR(0, 1, "inv", "<feature name=\"feature-name\"><organization><text>org</text></organization></feature>",
+ "Unexpected sub-element \"organization\" of \"feature\" element.", "Line number 1.");
+
+ /*
+ * compiling
+ */
+
+ TEST_SCHEMA_OK(1, 0, "a", "feature f1 {description test1;reference test2;status current;} feature f2; feature f3;\n"
+ "feature orfeature {if-feature \"f1 or f2\";}\n"
+ "feature andfeature {if-feature \"f1 and f2\";}\n"
+ "feature f6 {if-feature \"not f1\";}\n"
+ "feature f7 {if-feature \"(f2 and f3) or (not f1)\";}\n"
+ "feature f8 {if-feature \"f1 or f2 or f3 or orfeature or andfeature\";}\n"
+ "feature f9 {if-feature \"not not f1\";}", mod);
+ assert_non_null(mod->parsed->features);
+ assert_int_equal(9, LY_ARRAY_COUNT(mod->parsed->features));
+
+ /* all features are disabled by default */
+ LY_ARRAY_FOR(mod->parsed->features, struct lysp_feature, f) {
+ assert_false(f->flags & LYS_FENABLED);
+ }
+
+ /* some invalid expressions */
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f{if-feature f1;}",
+ "Invalid value \"f1\" of if-feature - unable to find feature \"f1\".", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature 'f and';}",
+ "Invalid value \"f and\" of if-feature - unexpected end of expression.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f{if-feature 'or';}",
+ "Invalid value \"or\" of if-feature - unexpected end of expression.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature '(f1';}",
+ "Invalid value \"(f1\" of if-feature - non-matching opening and closing parentheses.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature 'f1)';}",
+ "Invalid value \"f1)\" of if-feature - non-matching opening and closing parentheses.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature ---;}",
+ "Invalid value \"---\" of if-feature - unable to find feature \"---\".", NULL);
+ TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1; feature f2{if-feature 'not f1';}",
+ "Invalid value \"not f1\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", NULL);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} feature f1;}");
+ TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1 {if-feature f2;} feature f2 {if-feature f1;}",
+ "Feature \"f1\" is indirectly referenced from itself.", NULL);
+ TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1 {if-feature f1;}",
+ "Feature \"f1\" is referenced from itself.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f {if-feature ();}",
+ "Invalid value \"()\" of if-feature - number of features in expression does not match the required number of operands for the operations.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1(';}",
+ "Invalid value \"f1(\" of if-feature - non-matching opening and closing parentheses.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'and f1';}",
+ "Invalid value \"and f1\" of if-feature - missing feature/expression before \"and\" operation.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1 not ';}",
+ "Invalid value \"f1 not \" of if-feature - unexpected end of expression.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1 not not ';}",
+ "Invalid value \"f1 not not \" of if-feature - unexpected end of expression.", NULL);
+ TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2; feature f {if-feature 'or f1 f2';}",
+ "Invalid value \"or f1 f2\" of if-feature - missing feature/expression before \"or\" operation.", NULL);
+
+ /*
+ * printing
+ */
+
+ /*
+ * cleanup
+ */
+}
+
+static void
+test_extension_argument(void **state)
+{
+ struct lys_module *mod;
+ const char *mod_def_yang = "module a {\n"
+ " namespace \"urn:a\";\n"
+ " prefix a;\n\n"
+ " extension e {\n"
+ " argument name;\n"
+ " }\n\n"
+ " a:e \"aaa\";\n"
+ "}\n";
+ const char *mod_def_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"a\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:a\"/>\n"
+ " <prefix value=\"a\"/>\n"
+ " <extension name=\"e\">\n"
+ " <argument name=\"name\"/>\n"
+ " </extension>\n"
+ " <a:e name=\"aaa\"/>\n"
+ "</module>\n";
+ const char *mod_test_yin, *mod_test_yang;
+ char *printed;
+
+ mod_test_yang = "module b {\n"
+ " namespace \"urn:b\";\n"
+ " prefix b;\n\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n\n"
+ " a:e \"xxx\";\n"
+ "}\n";
+ mod_test_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"b\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:b=\"urn:b\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:b\"/>\n"
+ " <prefix value=\"b\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n"
+ " <a:e name=\"xxx\"/>\n"
+ "</module>\n";
+
+ /* from YANG */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+
+ /* context reset */
+ ly_ctx_destroy(UTEST_LYCTX);
+ ly_ctx_new(NULL, 0, &UTEST_LYCTX);
+
+ /* from YIN */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+}
+
+static void
+test_extension_argument_element(void **state)
+{
+ struct lys_module *mod;
+ const char *mod_def_yang = "module a {\n"
+ " namespace \"urn:a\";\n"
+ " prefix a;\n\n"
+ " extension e {\n"
+ " argument name {\n"
+ " yin-element true;\n"
+ " }\n"
+ " }\n\n"
+ " a:e \"aaa\";\n"
+ "}\n";
+ const char *mod_def_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"a\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:a\"/>\n"
+ " <prefix value=\"a\"/>\n"
+ " <extension name=\"e\">\n"
+ " <argument name=\"name\">\n"
+ " <yin-element value=\"true\"/>\n"
+ " </argument>\n"
+ " </extension>\n"
+ " <a:e>\n"
+ " <a:name>aaa</a:name>\n"
+ " </a:e>\n"
+ "</module>\n";
+ const char *mod_test_yin, *mod_test_yang;
+ char *printed;
+
+ mod_test_yang = "module b {\n"
+ " namespace \"urn:b\";\n"
+ " prefix b;\n\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n\n"
+ " a:e \"xxx\";\n"
+ "}\n";
+ mod_test_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"b\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:b=\"urn:b\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:b\"/>\n"
+ " <prefix value=\"b\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n"
+ " <a:e>\n"
+ " <a:name>xxx</a:name>\n"
+ " </a:e>\n"
+ "</module>\n";
+
+ /* from YANG */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+
+ /* context reset */
+ ly_ctx_destroy(UTEST_LYCTX);
+ ly_ctx_new(NULL, 0, &UTEST_LYCTX);
+
+ /* from YIN */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+
+ /* invalid */
+ mod_test_yang = "module x { namespace \"urn:x\"; prefix x; import a { prefix a; } a:e; }";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
+ "Extension instance \"a:e\" missing argument element \"name\".", NULL);
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e/>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
+ "Extension instance \"a:e\" missing argument element \"name\".", NULL);
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e name=\"xxx\"/>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
+ "Extension instance \"a:e\" missing argument element \"name\".", NULL);
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e>\n"
+ " <x:name>xxx</x:name>\n"
+ " </a:e>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
+ "Extension instance \"a:e\" element and its argument element \"name\" are expected in the same namespace, but they differ.",
+ NULL);
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e>\n"
+ " <a:value>xxx</a:value>\n"
+ " </a:e>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
+ "Extension instance \"a:e\" expects argument element \"name\" as its first XML child, but \"value\" element found.",
+ NULL);
+
+}
+
+static void
+test_extension_compile(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ctx cctx = {0};
+ struct lysp_ext_instance ext_p = {0};
+ struct lysp_ext_substmt *substmtp;
+ struct lysp_stmt child = {0};
+ struct lysc_ext_instance ext_c = {0};
+ struct lysc_ext_substmt *substmt;
+ LY_ERR rc = LY_SUCCESS;
+
+ /* current module, whatever */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang");
+ assert_true(mod);
+
+ /* compile context */
+ cctx.ctx = UTEST_LYCTX;
+ cctx.cur_mod = mod;
+ cctx.pmod = mod->parsed;
+ cctx.path_len = 1;
+ cctx.path[0] = '/';
+
+ /* parsed ext instance */
+ lydict_insert(UTEST_LYCTX, "pref:my-ext", 0, &ext_p.name);
+ ext_p.format = LY_VALUE_JSON;
+ ext_p.parent_stmt = LY_STMT_MODULE;
+
+ LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_p.substmts, substmtp, rc, cleanup);
+
+ substmtp->stmt = LY_STMT_ERROR_MESSAGE;
+ substmtp->storage = &ext_p.parsed;
+ /* fake parse */
+ lydict_insert(UTEST_LYCTX, "my error", 0, (const char **)&ext_p.parsed);
+
+ /* compiled ext instance */
+ ext_c.parent_stmt = ext_p.parent_stmt;
+ LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_c.substmts, substmt, rc, cleanup);
+
+ substmt->stmt = LY_STMT_ERROR_MESSAGE;
+ substmt->storage = &ext_c.compiled;
+
+ /*
+ * error-message
+ */
+ ext_p.child = &child;
+ lydict_insert(UTEST_LYCTX, "error-message", 0, &child.stmt);
+ lydict_insert(UTEST_LYCTX, "my error", 0, &child.arg);
+ child.format = LY_VALUE_JSON;
+ child.kw = LY_STMT_ERROR_MESSAGE;
+
+ /* compile */
+ assert_int_equal(LY_SUCCESS, lyplg_ext_compile_extension_instance(&cctx, &ext_p, &ext_c));
+
+ /* check */
+ assert_string_equal(ext_c.compiled, "my error");
+
+cleanup:
+ lydict_remove(UTEST_LYCTX, ext_p.name);
+ lydict_remove(UTEST_LYCTX, child.stmt);
+ lydict_remove(UTEST_LYCTX, child.arg);
+ LY_ARRAY_FREE(ext_p.substmts);
+ lydict_remove(UTEST_LYCTX, ext_p.parsed);
+ LY_ARRAY_FREE(ext_c.substmts);
+ lydict_remove(UTEST_LYCTX, ext_c.compiled);
+ if (rc) {
+ fail();
+ }
+}
+
+static void
+test_ext_recursive(void **state)
+{
+ const char *mod_base_yang, *mod_imp_yang, *mod_base_yin, *mod_imp_yin;
+
+ mod_imp_yang = "module b {\n"
+ " namespace \"urn:b\";\n"
+ " prefix b;\n\n"
+ " extension use-in {\n"
+ " argument name {\n"
+ " b:arg-type {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " b:use-in \"extension\";\n"
+ " b:occurence \"*\";\n"
+ " }\n"
+ "\n"
+ " extension substatement {\n"
+ " argument name {\n"
+ " b:arg-type {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " b:use-in \"extension\";\n"
+ " b:occurence \"*\";\n"
+ " b:substatement \"b:occurence\";\n"
+ " }\n"
+ "\n"
+ " extension arg-type {\n"
+ " b:use-in \"argument\";\n"
+ " b:substatement \"type\" {\n"
+ " b:occurence \"1\";\n"
+ " }\n"
+ " b:substatement \"default\";\n"
+ " }\n"
+ "\n"
+ " extension occurence {\n"
+ " argument value {\n"
+ " b:arg-type {\n"
+ " type enumeration {\n"
+ " enum \"?\";\n"
+ " enum \"*\";\n"
+ " enum \"+\";\n"
+ " enum \"1\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " b:use-in \"extension\";\n"
+ " }\n"
+ "}\n";
+
+ mod_imp_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"b\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:b=\"urn:b\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:b\"/>\n"
+ " <prefix value=\"b\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e name=\"xxx\"/>\n"
+ "</module>\n";
+
+ mod_base_yang = "module a {\n"
+ " namespace \"urn:a\";\n"
+ " prefix a;\n\n"
+ " import b {\n"
+ " prefix b;\n"
+ " }\n"
+ "\n"
+ " extension abstract {\n"
+ " b:use-in \"identity\";\n"
+ " }\n"
+ "\n"
+ " identity mount-id;\n"
+ "\n"
+ " identity yang-lib-id {\n"
+ " base mount-id;\n"
+ " a:abstract;\n"
+ " }\n"
+ "}\n";
+
+ mod_base_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"a\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:a\"/>\n"
+ " <prefix value=\"a\"/>\n\n"
+ " <extension name=\"e\">\n"
+ " <argument name=\"name\"/>\n"
+ " </extension>\n\n"
+ " <a:e name=\"aaa\"/>\n"
+ "</module>\n";
+
+ /* from YANG */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yang);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_base_yang, LYS_IN_YANG, NULL));
+
+ /* context reset */
+ ly_ctx_destroy(UTEST_LYCTX);
+ ly_ctx_new(NULL, 0, &UTEST_LYCTX);
+
+ /* from YIN */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yin);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_base_yin, LYS_IN_YIN, NULL));
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_getnext),
+ UTEST(test_date),
+ UTEST(test_revisions),
+ UTEST(test_collision_typedef),
+ UTEST(test_collision_grouping),
+ UTEST(test_collision_identity),
+ UTEST(test_collision_feature),
+ UTEST(test_accessible_tree),
+ UTEST(test_includes),
+ UTEST(test_key_order),
+ UTEST(test_disabled_enum),
+ UTEST(test_identity),
+ UTEST(test_feature),
+ UTEST(test_extension_argument),
+ UTEST(test_extension_argument_element),
+ UTEST(test_extension_compile),
+ UTEST(test_ext_recursive),
+ };
+
+ 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
new file mode 100644
index 0000000..d6f0538
--- /dev/null
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -0,0 +1,3837 @@
+/*
+ * @file test_tree_schema_compile.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from parser_yang.c
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "common.h"
+#include "in.h"
+#include "parser_internal.h"
+#include "path.h"
+#include "plugins_types.h"
+#include "schema_compile.h"
+#include "xpath.h"
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS));
+
+ return 0;
+}
+
+static void
+test_imp_free_data(void *model_data, void *UNUSED(user_data))
+{
+ free(model_data);
+}
+
+static LY_ERR
+test_imp_clb(const char *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))
+{
+ char *nl;
+
+ if ((nl = strchr(user_data, '\n'))) {
+ /* more modules */
+ if (!strncmp((char *)user_data + 7, mod_name, strlen(mod_name))) {
+ *module_data = strndup(user_data, nl - (char *)user_data);
+ *format = LYS_IN_YANG;
+ *free_module_data = test_imp_free_data;
+ } else {
+ *module_data = strdup(nl + 1);
+ *format = LYS_IN_YANG;
+ *free_module_data = test_imp_free_data;
+ }
+ } else {
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ }
+ return LY_SUCCESS;
+}
+
+static void
+test_module(void **state)
+{
+ const char *str, *feats[] = {"invalid", NULL};
+ struct ly_in *in;
+ struct lys_module *mod = NULL;
+ struct lysp_feature *f;
+ struct lysc_iffeature *iff;
+ struct lys_glob_unres unres = {0};
+
+ 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));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ assert_int_equal(0, mod->implemented);
+ assert_int_equal(LY_EINVAL, lys_set_implemented(mod, feats));
+ assert_int_equal(LY_SUCCESS, lys_set_implemented(mod, NULL));
+ assert_non_null(mod->compiled);
+ assert_string_equal("test", mod->name);
+ assert_string_equal("urn:test", mod->ns);
+ assert_string_equal("t", mod->prefix);
+ /* features */
+ assert_non_null(mod->parsed->features);
+ assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features));
+ f = &mod->parsed->features[1];
+ assert_non_null(f->iffeatures);
+ assert_int_equal(1, LY_ARRAY_COUNT(f->iffeatures));
+ iff = &f->iffeatures_c[0];
+ assert_non_null(iff->expr);
+ assert_non_null(iff->features);
+ assert_int_equal(1, LY_ARRAY_COUNT(iff->features));
+ assert_ptr_equal(&mod->parsed->features[0], iff->features[0]);
+
+ /* 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));
+ 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);
+
+ /* data definition name collision in top level */
+ str = "module aa {namespace urn:aa;prefix aa; leaf a {type string;} container a{presence x;}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EEXIST, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod));
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/aa:a");
+}
+
+static void
+test_name_collisions(void **state)
+{
+ const char *yang_data;
+
+ /* top-level */
+ yang_data = "module a {namespace urn:a;prefix a;"
+ " container c;"
+ " leaf a {type empty;}"
+ " leaf c {type empty;}"
+ "}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:c");
+ UTEST_LOG_CLEAN;
+
+ yang_data = "module a {namespace urn:a;prefix a;"
+ " container c;"
+ " leaf a {type empty;}"
+ " notification c;"
+ "}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:c");
+ UTEST_LOG_CLEAN;
+
+ yang_data = "module a {namespace urn:a;prefix a;"
+ " container c;"
+ " leaf a {type empty;}"
+ " rpc c;"
+ "}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:c");
+ UTEST_LOG_CLEAN;
+
+ yang_data = "module a {namespace urn:a;prefix a;"
+ " container c;"
+ " leaf a {type empty;}"
+ " choice ch {"
+ " leaf c {type string;}"
+ " case c2 {"
+ " leaf aa {type empty;}"
+ " }"
+ " }"
+ "}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:ch/c/c");
+ UTEST_LOG_CLEAN;
+
+ /* nested */
+ yang_data = "module a {namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}"
+ "leaf-list a {type string;}"
+ "container a;"
+ "}}}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/a:c/l/a");
+ UTEST_LOG_CLEAN;
+
+ yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}"
+ "leaf-list a {type string;}"
+ "notification a;"
+ "}}}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/a:c/l/a");
+ UTEST_LOG_CLEAN;
+
+ yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}"
+ "leaf-list a {type string;}"
+ "action a;"
+ "}}}";
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/a:c/l/a");
+ UTEST_LOG_CLEAN;
+
+ /* grouping */
+}
+
+static void
+test_node_container(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_container *cont;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;container c;}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled);
+ assert_non_null((cont = (struct lysc_node_container *)mod->compiled->data));
+ assert_int_equal(LYS_CONTAINER, cont->nodetype);
+ assert_string_equal("c", cont->name);
+ assert_true(cont->flags & LYS_CONFIG_W);
+ assert_true(cont->flags & LYS_STATUS_CURR);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;container c {config false; status deprecated; container child;}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled);
+ assert_non_null((cont = (struct lysc_node_container *)mod->compiled->data));
+ assert_true(cont->flags & LYS_CONFIG_R);
+ assert_true(cont->flags & LYS_STATUS_DEPRC);
+ assert_non_null((cont = (struct lysc_node_container *)cont->child));
+ assert_int_equal(LYS_CONTAINER, cont->nodetype);
+ assert_true(cont->flags & LYS_CONFIG_R);
+ assert_true(cont->flags & LYS_STATUS_DEPRC);
+ assert_string_equal("child", cont->name);
+}
+
+static void
+test_node_leaflist(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+ struct lysc_node_leaflist *ll;
+ struct lysc_node_leaf *l;
+ const char *dflt;
+ uint8_t dynamic;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;"
+ "typedef mytype {type union {type leafref {path ../target;} type string;}}"
+ "leaf-list ll1 {type union {type decimal64 {fraction-digits 2;} type mytype;}}"
+ "leaf-list ll2 {type leafref {path ../target;}}"
+ "leaf target {type int8;}}",
+ LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union *)type)->types);
+ assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union *)type)->types[1]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype);
+ assert_non_null(((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype->basetype);
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype);
+
+ /* now test for string type is in file ./tests/utests/types/string.c */
+#if 0
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf-list ll {type string;}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data));
+ assert_int_equal(0, ll->min);
+ assert_int_equal((uint32_t)-1, ll->max);
+#endif
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;typedef mytype {type int8;default 10;}"
+ "leaf-list ll1 {type mytype;default 1; default 1; config false;}"
+ "leaf-list ll2 {type mytype; ordered-by user;}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data));
+ assert_non_null(ll->dflts);
+ assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference (typedef's default does not reference own type) */
+ assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts));
+ assert_string_equal("1", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("1", dflt = ll->dflts[1]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_DFLT | LYS_SET_CONFIG, ll->flags);
+ assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data->next));
+ assert_non_null(ll->dflts);
+ assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference */
+ assert_int_equal(1, LY_ARRAY_COUNT(ll->dflts));
+ assert_string_equal("10", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags);
+
+ /* ordered-by is ignored (with verbose message) for state data, RPC/action output parameters and notification content */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {yang-version 1.1;namespace urn:d;prefix d;"
+ "leaf-list ll {config false; type string; ordered-by user;}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data));
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_CONFIG, ll->flags);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {yang-version 1.1;namespace urn:e;prefix e;"
+ "rpc oper {output {leaf-list ll {type string; ordered-by user;}}}}", LYS_IN_YANG, &mod));
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1;namespace urn:f;prefix f;"
+ "notification event {leaf-list ll {type string; ordered-by user;}}}", LYS_IN_YANG, &mod));
+
+ /* forward reference in default */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;"
+ "leaf ref {type instance-identifier {require-instance true;} default \"/g:g[.='val']\";}"
+ "leaf-list g {type string;}}", LYS_IN_YANG, &mod));
+ assert_non_null(l = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("ref", l->name);
+ assert_non_null(l->dflt);
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/aa:ll");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location \"/bb:ll\".");
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;"
+ "leaf-list ll {config false;type string; default one;default two;default one;}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data));
+ assert_non_null(ll->dflts);
+ assert_int_equal(3, LY_ARRAY_COUNT(ll->dflts));
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;"
+ "leaf-list ll {type string; default one;default two;default one;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Configuration leaf-list has multiple defaults of the same value \"one\".", "/dd:ll");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;"
+ "leaf ref {type instance-identifier {require-instance true;} default \"/ee:g\";}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type "
+ "(Invalid instance-identifier \"/ee:g\" value - semantic error.).", "Schema location \"/ee:ref\".");
+}
+
+static void
+test_node_list(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_list *list;
+ struct lysc_node *child;
+ struct ly_in *in;
+ const char *data =
+ "module a {namespace urn:a;prefix a;feature f;"
+ "list l1 {key \"x y\"; ordered-by user; leaf y{type string;if-feature f;} leaf x {type string; when 1;}}"
+ "list l2 {config false;leaf value {type string;}}}";
+ const char *feats[] = {"f", NULL};
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats, &mod));
+ ly_in_free(in, 0);
+ list = (struct lysc_node_list *)mod->compiled->data;
+ assert_non_null(list);
+ assert_non_null(list->child);
+ assert_string_equal("x", list->child->name);
+ assert_true(list->child->flags & LYS_KEY);
+ assert_string_equal("y", list->child->next->name);
+ assert_true(list->child->next->flags & LYS_KEY);
+ assert_non_null(list->child);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, list->flags);
+ assert_true(list->child->flags & LYS_KEY);
+ assert_true(list->child->next->flags & LYS_KEY);
+ list = (struct lysc_node_list *)mod->compiled->data->next;
+ assert_non_null(list);
+ assert_non_null(list->child);
+ assert_false(list->child->flags & LYS_KEY);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_CONFIG | LYS_KEYLESS, list->flags);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;"
+ "list l {key a; unique \"a c/b:b\"; unique \"c/e d\";"
+ "leaf a {type string; default x;} leaf d {type string;config false;}"
+ "container c {leaf b {type string;}leaf e{type string;config false;}}}}",
+ LYS_IN_YANG, &mod));
+ list = (struct lysc_node_list *)mod->compiled->data;
+ assert_non_null(list);
+ assert_string_equal("l", list->name);
+ assert_string_equal("a", list->child->name);
+ assert_true(list->child->flags & LYS_KEY);
+ assert_null(((struct lysc_node_leaf *)list->child)->dflt);
+ assert_non_null(list->uniques);
+ assert_int_equal(2, LY_ARRAY_COUNT(list->uniques));
+ assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[0]));
+ assert_string_equal("a", list->uniques[0][0]->name);
+ assert_true(list->uniques[0][0]->flags & LYS_UNIQUE);
+ assert_string_equal("b", list->uniques[0][1]->name);
+ assert_true(list->uniques[0][1]->flags & LYS_UNIQUE);
+ assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[1]));
+ assert_string_equal("e", list->uniques[1][0]->name);
+ assert_true(list->uniques[1][0]->flags & LYS_UNIQUE);
+ assert_string_equal("d", list->uniques[1][1]->name);
+ assert_true(list->uniques[1][1]->flags & LYS_UNIQUE);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;"
+ "list l {key a;leaf a {type empty;}}}", LYS_IN_YANG, &mod));
+ list = (struct lysc_node_list *)mod->compiled->data;
+ assert_non_null(list);
+ assert_string_equal("l", list->name);
+ assert_string_equal("a", list->child->name);
+ assert_true(list->child->flags & LYS_KEY);
+ assert_int_equal(LY_TYPE_EMPTY, ((struct lysc_node_leaf *)list->child)->type->basetype);
+
+ /* keys order */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {yang-version 1.1;namespace urn:d;prefix d;"
+ "list l {key \"d b c\";leaf a {type string;} leaf b {type string;} leaf c {type string;} leaf d {type string;}}}", LYS_IN_YANG, &mod));
+ list = (struct lysc_node_list *)mod->compiled->data;
+ assert_non_null(list);
+ assert_string_equal("l", list->name);
+ assert_non_null(child = list->child);
+ assert_string_equal("d", child->name);
+ assert_true(child->flags & LYS_KEY);
+ assert_non_null(child = child->next);
+ assert_string_equal("b", child->name);
+ assert_true(child->flags & LYS_KEY);
+ assert_non_null(child = child->next);
+ assert_string_equal("c", child->name);
+ assert_true(child->flags & LYS_KEY);
+ assert_non_null(child = child->next);
+ assert_string_equal("a", child->name);
+ assert_false(child->flags & LYS_KEY);
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;list l;}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Missing key in list representing configuration data.", "/aa:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1; namespace urn:bb;prefix bb;"
+ "list l {key x; leaf x {type string; when 1;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("List's key must not have any \"when\" statement.", "/bb:l/x");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;"
+ "list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Key \"x\" is disabled.", "Schema location \"/cc:l/x\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;"
+ "list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Key of a configuration list must not be a state leaf.", "/dd:l/x");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;"
+ "list l {config false;key x; leaf x {type string; config true;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/ee:l/x");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;"
+ "list l {key x; leaf-list x {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("The list's key \"x\" not found.", "/ff:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;"
+ "list l {key x; unique y;leaf x {type string;} leaf-list y {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Unique's descendant-schema-nodeid \"y\" refers to leaf-list node instead of a leaf.", "/gg:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;"
+ "list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Unique statement \"x y\" refers to leaves with different config type.", "/hh:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;"
+ "list l {key x; unique a:x;leaf x {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"a:x\" - prefix \"a\" not defined in module \"ii\".", "/ii:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;"
+ "list l {key x; unique c/x;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c/x\" - target node not found.", "/jj:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;"
+ "list l {key x; unique c^y;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c^\" - missing \"/\" as node-identifier separator.", "/kk:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;"
+ "list l {key \"x y x\";leaf x {type string;}leaf y {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicated key identifier \"x\".", "/ll:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;"
+ "list l {key x;leaf x {type empty;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("List's key cannot be of \"empty\" type until it is in YANG 1.1 module.", "/mm:l/x");
+}
+
+static void
+test_node_choice(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_choice *ch;
+ struct lysc_node_case *cs;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;feature f;"
+ "choice ch {default a:b; when \"true()\"; case a {leaf a1 {type string;}leaf a2 {type string;}}"
+ "leaf b {type string;}}}", LYS_IN_YANG, &mod));
+ ch = (struct lysc_node_choice *)mod->compiled->data;
+ assert_non_null(ch);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, ch->flags);
+ assert_int_equal(1, LY_ARRAY_COUNT(ch->when));
+ assert_null(ch->when[0]->context);
+ cs = ch->cases;
+ assert_non_null(cs);
+ assert_string_equal("a", cs->name);
+ assert_ptr_equal(ch, cs->parent);
+ assert_non_null(cs->child);
+ assert_string_equal("a1", cs->child->name);
+ assert_non_null(cs->child->next);
+ assert_string_equal("a2", cs->child->next->name);
+ assert_ptr_equal(cs, cs->child->parent);
+ cs = (struct lysc_node_case *)cs->next;
+ assert_non_null(cs);
+ assert_string_equal("b", cs->name);
+ assert_int_equal(LYS_STATUS_CURR | LYS_SET_DFLT | LYS_CONFIG_W, cs->flags);
+ assert_ptr_equal(ch, cs->parent);
+ assert_non_null(cs->child);
+ assert_string_equal("b", cs->child->name);
+ assert_ptr_equal(cs, cs->child->parent);
+ assert_ptr_equal(ch->dflt, cs);
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
+ "choice ch {case a {leaf x {type string;}}leaf x {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "/aa:ch/x/x");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa;"
+ "choice ch {case a {leaf y {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "/aa2:ch/b/y");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;"
+ "choice ch {case a {leaf x {type string;}}leaf a {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of case statement.", "/bb:ch/a");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb2 {namespace urn:bb2;prefix bb;"
+ "choice ch {case b {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"b\" of case statement.", "/bb2:ch/b");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ca {namespace urn:ca;prefix ca;"
+ "choice ch {default c;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Default case \"c\" not found.", "/ca:ch");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cb {namespace urn:cb;prefix cb; import a {prefix a;}"
+ "choice ch {default a:a;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Default case \"a:a\" not found.", "/cb:ch");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;"
+ "choice ch {default a;case a {leaf x {mandatory true;type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Mandatory node \"x\" under the default case \"a\".", "/cc:ch");
+ /* TODO check with mandatory nodes from augment placed into the case */
+}
+
+static void
+test_node_anydata(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_anydata *any;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;"
+ "anydata any {config false;mandatory true;}}", LYS_IN_YANG, &mod));
+ any = (struct lysc_node_anydata *)mod->compiled->data;
+ assert_non_null(any);
+ assert_int_equal(LYS_ANYDATA, any->nodetype);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_SET_CONFIG, any->flags);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;"
+ "anyxml any;}", LYS_IN_YANG, &mod));
+ any = (struct lysc_node_anydata *)mod->compiled->data;
+ assert_non_null(any);
+ assert_int_equal(LYS_ANYXML, any->nodetype);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, any->flags);
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;anydata any;}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid keyword \"anydata\" as a child of \"module\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+}
+
+static void
+test_action(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node_action *rpc;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;"
+ "rpc a {input {leaf x {type int8;} leaf y {type int8;}} output {leaf result {type int16;}}}}", LYS_IN_YANG, &mod));
+ rpc = mod->compiled->rpcs;
+ assert_non_null(rpc);
+ assert_null(rpc->next);
+ assert_int_equal(LYS_RPC, rpc->nodetype);
+ assert_int_equal(LYS_STATUS_CURR, rpc->flags);
+ assert_string_equal("a", rpc->name);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {"
+ "action b {input {leaf x {type int8;} leaf y {type int8;}}"
+ "output {must \"result > 25\"; must \"/top\"; leaf result {type int16;}}}}"
+ "augment /top/b/output {leaf result2 {type string;}}}", LYS_IN_YANG, &mod));
+ rpc = lysc_node_actions(mod->compiled->data);
+ assert_non_null(rpc);
+ assert_null(rpc->next);
+ assert_int_equal(LYS_ACTION, rpc->nodetype);
+ assert_int_equal(LYS_STATUS_CURR, rpc->flags);
+ assert_string_equal("b", rpc->name);
+ assert_null(rpc->input.musts);
+ assert_int_equal(2, LY_ARRAY_COUNT(rpc->output.musts));
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {action x;}}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid keyword \"action\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} rpc x;}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "/bb:x");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} action y;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "/cc:c/y");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {action z; action z;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"z\" of data definition/RPC/action/notification statement.", "/dd:c/z");
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} notification w;}");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; rpc w;}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"w\" of data definition/RPC/action/notification statement.", "/ee:w");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
+ "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Action \"invalid\" is placed inside another RPC/action.", "/ff:{augment='/test/input/a'}/invalid");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
+ "augment /test/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/gg:{augment='/test/a'}/invalid");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; notification test {container a {uses grp;}}"
+ "grouping grp {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/hh:test/a/{uses='grp'}/invalid");
+}
+
+static void
+test_notification(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node_notif *notif;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;"
+ "notification a1 {leaf x {type int8;}} notification a2;}", LYS_IN_YANG, &mod));
+ notif = mod->compiled->notifs;
+ assert_non_null(notif);
+ assert_non_null(notif->next);
+ assert_null(notif->next->next);
+ assert_int_equal(LYS_NOTIF, notif->nodetype);
+ assert_int_equal(LYS_STATUS_CURR, notif->flags);
+ assert_string_equal("a1", notif->name);
+ assert_non_null(notif->child);
+ assert_string_equal("x", notif->child->name);
+ notif = notif->next;
+ assert_int_equal(LYS_NOTIF, notif->nodetype);
+ assert_int_equal(LYS_STATUS_CURR, notif->flags);
+ assert_string_equal("a2", notif->name);
+ assert_null(notif->child);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {"
+ "notification b1 {leaf x {type int8;}} notification b2 {must \"/top\";}}}", LYS_IN_YANG, &mod));
+ notif = lysc_node_notifs(mod->compiled->data);
+ assert_non_null(notif);
+ assert_non_null(notif->next);
+ assert_null(notif->next->next);
+ assert_int_equal(LYS_NOTIF, notif->nodetype);
+ assert_int_equal(LYS_STATUS_CURR, notif->flags);
+ assert_string_equal("b1", notif->name);
+ assert_non_null(notif->child);
+ assert_string_equal("x", notif->child->name);
+ notif = notif->next;
+ assert_int_equal(LYS_NOTIF, notif->nodetype);
+ assert_int_equal(LYS_STATUS_CURR, notif->flags);
+ assert_string_equal("b2", notif->name);
+ assert_null(notif->child);
+ assert_int_equal(1, LY_ARRAY_COUNT(notif->musts));
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {notification x;}}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} notification x;}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "/bb:x");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} notification y;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "/cc:c/y");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {notification z; notification z;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"z\" of data definition/RPC/action/notification statement.", "/dd:c/z");
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; notification w;}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Duplicate identifier \"w\" of data definition/RPC/action/notification statement.", "/ee:w");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
+ "augment /test/input/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/ff:{augment='/test/input/a'}/invalid");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
+ "augment /test/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Notification \"invalid\" is placed inside another notification.", "/gg:{augment='/test/a'}/invalid");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; rpc test {input {container a {uses grp;}}}"
+ "grouping grp {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/hh:test/input/a/{uses='grp'}/invalid");
+}
+
+/**
+ * actually the same as length restriction (tested in test_type_length()), so just check the correct handling in appropriate types,
+ * do not test the expression itself
+ */
+static void
+test_type_range(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+#if 0
+ /*test about int8 should be in tests/utests/types/int8.c*/
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type int8 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT8, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(-128, ((struct lysc_type_num *)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64);
+ assert_int_equal(127, ((struct lysc_type_num *)type)->range->parts[1].min_64);
+ assert_int_equal(127, ((struct lysc_type_num *)type)->range->parts[1].max_64);
+#endif
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type int16 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT16, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(-32768, ((struct lysc_type_num *)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64);
+ assert_int_equal(32767, ((struct lysc_type_num *)type)->range->parts[1].min_64);
+ assert_int_equal(32767, ((struct lysc_type_num *)type)->range->parts[1].max_64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;leaf l {type int32 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT32, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_num *)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64);
+ assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num *)type)->range->parts[1].min_64);
+ assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num *)type)->range->parts[1].max_64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type int64 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT64, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_num *)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64);
+ assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num *)type)->range->parts[1].min_64);
+ assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num *)type)->range->parts[1].max_64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;leaf l {type uint8 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT8, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64);
+ assert_int_equal(255, ((struct lysc_type_num *)type)->range->parts[1].min_u64);
+ assert_int_equal(255, ((struct lysc_type_num *)type)->range->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;leaf l {type uint16 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT16, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64);
+ assert_int_equal(65535, ((struct lysc_type_num *)type)->range->parts[1].min_u64);
+ assert_int_equal(65535, ((struct lysc_type_num *)type)->range->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;leaf l {type uint32 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT32, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64);
+ assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num *)type)->range->parts[1].min_u64);
+ assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num *)type)->range->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;leaf l {type uint64 {range min..10|max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT64, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num *)type)->range->parts[1].min_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num *)type)->range->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;typedef mytype {type uint8 {range 10..100;}}"
+ "typedef mytype2 {type mytype;} leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(3, type->refcount);
+ assert_int_equal(LY_TYPE_UINT8, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;"
+ "typedef mytype {type uint8 {range 1..100{description \"one to hundred\";reference A;}}}"
+ "leaf l {type mytype {range 1..10 {description \"one to ten\";reference B;}}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UINT8, type->basetype);
+ assert_non_null(((struct lysc_type_num *)type)->range);
+ assert_string_equal("one to ten", ((struct lysc_type_num *)type)->range->dsc);
+ assert_string_equal("B", ((struct lysc_type_num *)type)->range->ref);
+ assert_non_null(((struct lysc_type_num *)type)->range->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
+ assert_int_equal(1, ((struct lysc_type_num *)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64);
+}
+
+static void
+test_type_length(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type binary {length min {error-app-tag errortag;error-message error;}}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_string_equal("errortag", ((struct lysc_type_bin *)type)->length->eapptag);
+ assert_string_equal("error", ((struct lysc_type_bin *)type)->length->emsg);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type binary {length max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;leaf l {type binary {length min..max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type binary {length 5;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(5, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(5, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;leaf l {type binary {length 1..10;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(1, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;leaf l {type binary {length 1..10|20..30;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(1, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+ assert_int_equal(20, ((struct lysc_type_bin *)type)->length->parts[1].min_u64);
+ assert_int_equal(30, ((struct lysc_type_bin *)type)->length->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;leaf l {type binary {length \"16 | 32\";}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(16, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(16, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+ assert_int_equal(32, ((struct lysc_type_bin *)type)->length->parts[1].min_u64);
+ assert_int_equal(32, ((struct lysc_type_bin *)type)->length->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;typedef mytype {type binary {length 10;}}"
+ "leaf l {type mytype {length \"10\";}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length \"50\";}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(50, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(50, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length \"10..30|60..100\";}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(30, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+ assert_int_equal(60, ((struct lysc_type_bin *)type)->length->parts[1].min_u64);
+ assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[1].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length \"10..80\";}}leaf ll {type mytype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(80, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(2, type->refcount);
+ assert_non_null(((struct lysc_type_bin *)type)->length);
+ assert_non_null(((struct lysc_type_bin *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
+ assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
+
+ /* new string is tested in file ./tests/utests/types/string.c */
+#if 0
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;typedef mytype {type string {length 10..100;}}"
+ "typedef mytype2 {type mytype {pattern '[0-9]*';}} leaf l {type mytype2 {pattern '[0-4]*';}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str *)type)->length);
+ assert_non_null(((struct lysc_type_str *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_str *)type)->length->parts[0].min_u64);
+ assert_int_equal(100, ((struct lysc_type_str *)type)->length->parts[0].max_u64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;typedef mytype {type string {length 10;}}"
+ "leaf l {type mytype {length min..max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str *)type)->length);
+ assert_non_null(((struct lysc_type_str *)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_str *)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_str *)type)->length->parts[0].max_u64);
+#endif
+
+ /* invalid values */
+ assert_int_equal(LY_EDENIED, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf l {type binary {length -10;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - value \"-10\" does not fit the type limitations.", "/aa:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type binary {length 18446744073709551616;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/bb:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;leaf l {type binary {length \"max .. 10\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected data after max keyword (.. 10).", "/cc:l");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf l {type binary {length 50..10;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/dd:l");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type binary {length \"50 | 10\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/ee:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type binary {length \"x\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected data (x).", "/ff:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type binary {length \"50 | min\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected data before min keyword (50 | ).", "/gg:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;leaf l {type binary {length \"| 50\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected beginning of the expression (| 50).", "/hh:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;leaf l {type binary {length \"10 ..\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression after \"..\" (10 ..).", "/ii:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;leaf l {type binary {length \".. 10\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected \"..\" without a lower bound.", "/jj:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;leaf l {type binary {length \"10 |\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression (10 |).", "/kk:l");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module kl {namespace urn:kl;prefix kl;leaf l {type binary {length \"10..20 | 15..30\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (15).", "/kl:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;typedef mytype {type binary {length 10;}}"
+ "leaf l {type mytype {length 11;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (11) is not equally or more limiting.", "/ll:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length 1..11;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (1..11) is not equally or more limiting.", "/mm:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length 20..110;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..110) is not equally or more limiting.", "/nn:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length 20..30|110..120;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..30|110..120) is not equally or more limiting.", "/oo:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;typedef mytype {type binary {length 10..11;}}"
+ "leaf l {type mytype {length 15;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15) is not equally or more limiting.", "/pp:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;typedef mytype {type binary {length 10..20|30..40;}}"
+ "leaf l {type mytype {length 15..35;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15..35) is not equally or more limiting.", "/qq:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;typedef mytype {type binary {length 10;}}"
+ "leaf l {type mytype {length 10..35;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (10..35) is not equally or more limiting.", "/rr:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;leaf l {type binary {pattern '[0-9]*';}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid type restrictions for binary type.", "/ss:l");
+}
+
+static void
+test_type_pattern(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;leaf l {type string {"
+ "pattern .* {error-app-tag errortag;error-message error;}"
+ "pattern [0-9].*[0-9] {modifier invert-match;}}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_str *)type)->patterns);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->patterns));
+ assert_string_equal("errortag", ((struct lysc_type_str *)type)->patterns[0]->eapptag);
+ assert_string_equal("error", ((struct lysc_type_str *)type)->patterns[0]->emsg);
+ assert_string_equal(".*", ((struct lysc_type_str *)type)->patterns[0]->expr);
+ assert_int_equal(0, ((struct lysc_type_str *)type)->patterns[0]->inverted);
+ assert_null(((struct lysc_type_str *)type)->patterns[1]->eapptag);
+ assert_null(((struct lysc_type_str *)type)->patterns[1]->emsg);
+ assert_string_equal("[0-9].*[0-9]", ((struct lysc_type_str *)type)->patterns[1]->expr);
+ assert_int_equal(1, ((struct lysc_type_str *)type)->patterns[1]->inverted);
+
+ /* new string is tested in file ./tests/utests/types/string.c */
+#if 0
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;typedef mytype {type string {pattern '[0-9]*';}}"
+ "typedef mytype2 {type mytype {length 10;}} leaf l {type mytype2 {pattern '[0-4]*';}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str *)type)->patterns);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->patterns));
+ assert_string_equal("[0-9]*", ((struct lysc_type_str *)type)->patterns[0]->expr);
+ assert_int_equal(3, ((struct lysc_type_str *)type)->patterns[0]->refcount);
+ assert_string_equal("[0-4]*", ((struct lysc_type_str *)type)->patterns[1]->expr);
+ assert_int_equal(1, ((struct lysc_type_str *)type)->patterns[1]->refcount);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;typedef mytype {type string {pattern '[0-9]*';}}"
+ "leaf l {type mytype {length 10;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str *)type)->patterns);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->patterns));
+ assert_string_equal("[0-9]*", ((struct lysc_type_str *)type)->patterns[0]->expr);
+ assert_int_equal(2, ((struct lysc_type_str *)type)->patterns[0]->refcount);
+
+ /* test substitutions */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type string {"
+ "pattern '^\\p{IsLatinExtended-A}$';}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_str *)type)->patterns);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->patterns));
+ assert_string_equal("^\\p{IsLatinExtended-A}$", ((struct lysc_type_str *)type)->patterns[0]->expr);
+#endif
+
+ /* TODO check some data "^Å™$" */
+}
+
+static void
+test_type_enum(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;feature f; leaf l {type enumeration {"
+ "enum automin; enum min {value -2147483648;}enum one {if-feature f; value 1;}"
+ "enum two; enum seven {value 7;}enum eight;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_ENUM, type->basetype);
+ assert_non_null(((struct lysc_type_enum *)type)->enums);
+ assert_int_equal(5, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums));
+ assert_string_equal("automin", ((struct lysc_type_enum *)type)->enums[0].name);
+ assert_int_equal(0, ((struct lysc_type_enum *)type)->enums[0].value);
+ assert_string_equal("min", ((struct lysc_type_enum *)type)->enums[1].name);
+ assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_enum *)type)->enums[1].value);
+ assert_string_equal("two", ((struct lysc_type_enum *)type)->enums[2].name);
+ assert_int_equal(2, ((struct lysc_type_enum *)type)->enums[2].value);
+ assert_string_equal("seven", ((struct lysc_type_enum *)type)->enums[3].name);
+ assert_int_equal(7, ((struct lysc_type_enum *)type)->enums[3].value);
+ assert_string_equal("eight", ((struct lysc_type_enum *)type)->enums[4].name);
+ assert_int_equal(8, ((struct lysc_type_enum *)type)->enums[4].value);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b;feature f; typedef mytype {type enumeration {"
+ "enum 11; enum min {value -2147483648;}enum x$&;"
+ "enum two; enum seven {value 7;}enum eight;}} leaf l { type mytype {enum seven;enum eight;}}}",
+ LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_ENUM, type->basetype);
+ assert_non_null(((struct lysc_type_enum *)type)->enums);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums));
+ assert_string_equal("seven", ((struct lysc_type_enum *)type)->enums[0].name);
+ assert_int_equal(7, ((struct lysc_type_enum *)type)->enums[0].value);
+ assert_string_equal("eight", ((struct lysc_type_enum *)type)->enums[1].name);
+ assert_int_equal(8, ((struct lysc_type_enum *)type)->enums[1].value);
+
+ const char *new_module = "module moc_c {yang-version 1.1; namespace urn:moc_c;prefix moc_c;feature f; typedef mytype {type enumeration {"
+ "enum first{value -270;} enum second; enum third {value -400;} enum fourth;}} leaf l { type mytype;}}";
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, new_module, LYS_IN_YANG, &mod));
+
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_ENUM, type->basetype);
+ assert_non_null(((struct lysc_type_enum *)type)->enums);
+ assert_int_equal(4, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums));
+ assert_string_equal("first", ((struct lysc_type_enum *)type)->enums[0].name);
+ assert_int_equal(-270, ((struct lysc_type_enum *)type)->enums[0].value);
+ assert_string_equal("second", ((struct lysc_type_enum *)type)->enums[1].name);
+ assert_int_equal(-269, ((struct lysc_type_enum *)type)->enums[1].value);
+ assert_string_equal("third", ((struct lysc_type_enum *)type)->enums[2].name);
+ assert_int_equal(-400, ((struct lysc_type_enum *)type)->enums[2].value);
+ assert_string_equal("fourth", ((struct lysc_type_enum *)type)->enums[3].name);
+ assert_int_equal(-268, ((struct lysc_type_enum *)type)->enums[3].value);
+
+ new_module = "module moc_d {yang-version 1.1; namespace urn:moc_d;prefix moc_d;feature f; typedef mytype {type enumeration {"
+ "enum first; enum second;}} leaf l { type mytype;}}";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, new_module, LYS_IN_YANG, &mod));
+
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_ENUM, type->basetype);
+ assert_non_null(((struct lysc_type_enum *)type)->enums);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums));
+ assert_string_equal("first", ((struct lysc_type_enum *)type)->enums[0].name);
+ assert_int_equal(0, ((struct lysc_type_enum *)type)->enums[0].value);
+ assert_string_equal("second", ((struct lysc_type_enum *)type)->enums[1].name);
+ assert_int_equal(1, ((struct lysc_type_enum *)type)->enums[1].value);
+
+ /* invalid cases */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; feature f; leaf l {type enumeration {"
+ "enum one {if-feature f;}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid keyword \"if-feature\" as a child of \"enum\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum one {value -2147483649;}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid value \"-2147483649\" of \"value\".", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum one {value 2147483648;}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid value \"2147483648\" of \"value\".", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum one; enum one;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Duplicate identifier \"one\" of enum statement.", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum '';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Enum name must not be zero-length.", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum ' x';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Enum name must not have any leading or trailing whitespaces (\" x\").", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum 'x ';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Enum name must not have any leading or trailing whitespaces (\"x \").", "Line number 1.");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum 'inva\nlid';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Control characters in enum name should be avoided (\"inva\nlid\", character number 5).", NULL);
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type enumeration;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing enum substatement for enumeration type.", "/bb:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;typedef mytype {type enumeration {enum one;}}"
+ "leaf l {type mytype {enum two;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid enumeration - derived type adds new item \"two\".", "/cc:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;typedef mytype {type enumeration {enum one;}}"
+ "leaf l {type mytype {enum one {value 1;}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid enumeration - value of the item \"one\" has changed from 0 to 1 in the derived type.", "/dd:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type enumeration {enum x {value 2147483647;}enum y;}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid enumeration - it is not possible to auto-assign enum value for \"y\" since the highest value is already 2147483647.", "/ee:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type enumeration {enum x {value 1;}enum y {value 1;}}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid enumeration - value 1 collide in items \"y\" and \"x\".", "/ff:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;typedef mytype {type enumeration;}"
+ "leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing enum substatement for enumeration type mytype.", "/gg:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh; typedef mytype {type enumeration {enum one;}}"
+ "leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Enumeration type can be subtyped only in YANG 1.1 modules.", "/hh:l");
+}
+
+static void
+test_type_dec64(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type decimal64 {"
+ "fraction-digits 2;range min..max;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_DEC64, type->basetype);
+ assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits);
+ assert_non_null(((struct lysc_type_dec *)type)->range);
+ assert_non_null(((struct lysc_type_dec *)type)->range->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts));
+ assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_dec *)type)->range->parts[0].min_64);
+ assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_dec *)type)->range->parts[0].max_64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;typedef mytype {type decimal64 {"
+ "fraction-digits 2;range '3.14 | 5.1 | 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_DEC64, type->basetype);
+ assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits);
+ assert_non_null(((struct lysc_type_dec *)type)->range);
+ assert_non_null(((struct lysc_type_dec *)type)->range->parts);
+ assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts));
+ assert_int_equal(314, ((struct lysc_type_dec *)type)->range->parts[0].min_64);
+ assert_int_equal(314, ((struct lysc_type_dec *)type)->range->parts[0].max_64);
+ assert_int_equal(510, ((struct lysc_type_dec *)type)->range->parts[1].min_64);
+ assert_int_equal(510, ((struct lysc_type_dec *)type)->range->parts[1].max_64);
+ assert_int_equal(1000, ((struct lysc_type_dec *)type)->range->parts[2].min_64);
+ assert_int_equal(1000, ((struct lysc_type_dec *)type)->range->parts[2].max_64);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;typedef mytype {type decimal64 {"
+ "fraction-digits 2;range '1 .. 65535';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_int_equal(LY_TYPE_DEC64, type->basetype);
+ assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits);
+ assert_non_null(((struct lysc_type_dec *)type)->range);
+ assert_non_null(((struct lysc_type_dec *)type)->range->parts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts));
+ assert_int_equal(100, ((struct lysc_type_dec *)type)->range->parts[0].min_64);
+ assert_int_equal(6553500, ((struct lysc_type_dec *)type)->range->parts[0].max_64);
+
+ /* invalid cases */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 0;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid value \"0\" of \"fraction-digits\".", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits -1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid value \"-1\" of \"fraction-digits\".", "Line number 1.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 19;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Value \"19\" is out of \"fraction-digits\" bounds.", "Line number 1.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type.", "/aa:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ab {namespace urn:ab;prefix ab; typedef mytype {type decimal64;}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type mytype.", "/ab:l");
+
+ assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type decimal64 {fraction-digits 2;"
+ "range '3.142';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.", "/bb:l");
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; leaf l {type decimal64 {fraction-digits 2;"
+ "range '4 | 3.14';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (3.14).", "/cc:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; typedef mytype {type decimal64 {fraction-digits 2;}}"
+ "leaf l {type mytype {fraction-digits 3;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.", "/dd:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module de {namespace urn:de;prefix de; typedef mytype {type decimal64 {fraction-digits 2;}}"
+ "typedef mytype2 {type mytype {fraction-digits 3;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid fraction-digits substatement for type \"mytype2\" not directly derived from decimal64 built-in type.", "/de:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {"
+ "fraction-digits 18;range '-10 .. 0';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid range restriction - invalid value \"-10000000000000000000\".", "/ee:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {"
+ "fraction-digits 18;range '0 .. 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid range restriction - invalid value \"10000000000000000000\".", "/ee:l");
+}
+
+static void
+test_type_instanceid(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;typedef mytype {type instance-identifier {require-instance false;}}"
+ "leaf l1 {type instance-identifier {require-instance true;}}"
+ "leaf l2 {type mytype;} leaf l3 {type instance-identifier;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INST, type->basetype);
+ assert_int_equal(1, ((struct lysc_type_instanceid *)type)->require_instance);
+
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INST, type->basetype);
+ assert_int_equal(0, ((struct lysc_type_instanceid *)type)->require_instance);
+
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INST, type->basetype);
+ assert_int_equal(1, ((struct lysc_type_instanceid *)type)->require_instance);
+
+ /* invalid cases */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {require-instance yes;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
+ "Invalid value \"yes\" of \"require-instance\".", "Line number 1.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {fraction-digits 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid type restrictions for instance-identifier type.", "/aa:l");
+}
+
+static ly_bool
+identity_isderived(const struct lysc_ident *base, const char *der)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(base->derived, u) {
+ if (!strcmp(base->derived[u]->name, der)) {
+ return 1;
+ }
+ if (identity_isderived(base->derived[u], der)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static ly_bool
+contains_derived_identity(struct ly_ctx *ctx, char *module_name,
+ char *revision, char *identity_name, char *derived_name)
+{
+ LY_ARRAY_COUNT_TYPE u = 0;
+ struct lys_module *mod;
+ struct lysc_ident *identity = NULL;
+
+ if (!(mod = ly_ctx_get_module(ctx, module_name, revision))) {
+ return 0;
+ }
+
+ LY_ARRAY_FOR(mod->identities, u) {
+ if (!strcmp(identity_name, mod->identities[u].name)) {
+ identity = &mod->identities[u];
+ break;
+ }
+ }
+ if (!identity) {
+ return 0;
+ }
+
+ return identity_isderived(identity, derived_name);
+}
+
+static void
+test_identity(void **state)
+{
+ char *str;
+ const char *feats[2] = {NULL, NULL};
+ struct lyd_node *tree;
+ const char *data;
+
+#define RESET_CTX(CTX) \
+ ly_ctx_destroy(UTEST_LYCTX); \
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX));
+
+ /* It does not matter whether the base identity is in implemented
+ * module or not.
+ */
+
+ /* Implemented module's identity expand base identity located in unimplemented module. */
+ str = "module a {namespace urn:a; prefix a;"
+ "identity baseid;"
+ "identity id1 {base baseid;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b; import a {prefix a;}"
+ "identity id2 {base a:baseid;}"
+ "leaf lf {type identityref {base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\">id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".",
+ "Schema location \"/b:lf\", line number 1.");
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_false(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Unimplemented module (c) expand base identity located in unimplemented module. */
+ str = "module a {namespace urn:a; prefix a;"
+ "identity baseid;"
+ "identity id1 {base baseid;}"
+ "}\n"
+ "module c {namespace urn:c; prefix c; import a {prefix a;}"
+ "identity id3 {base a:baseid;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b; import a {prefix a;} import c {prefix c;}"
+ "identity id2 {base a:baseid;}"
+ "leaf lf {type identityref {base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\">id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".",
+ "Schema location \"/b:lf\", line number 1.");
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:c\">ids:id3</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG("Invalid identityref \"ids:id3\" value - identity found in non-implemented module \"c\".",
+ "Schema location \"/b:lf\", line number 1.");
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Unimplemented module expand base identity located in implemented module. */
+ str = "module b {namespace urn:b; prefix b;"
+ "identity baseid;"
+ "identity id2 {base baseid;}"
+ "leaf lf {type identityref {base baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module a {namespace urn:a; prefix a; import b {prefix b;}"
+ "identity id1 {base b:baseid;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ /* load (but don't implement) module (a) into context by module (c) */
+ str = "module c {namespace urn:c; prefix c; import a {prefix a;}}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\">id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".",
+ "Schema location \"/b:lf\", line number 1.");
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Transitivity of derived identity through unimplemented module. */
+ str = "module a {namespace urn:a; prefix a;"
+ "identity baseid;"
+ "identity id1 {base baseid;}"
+ "}\n"
+ "module c {namespace urn:c; prefix c; import a {prefix a;}"
+ "identity id3 {base a:baseid;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b; import c {prefix c;} import a {prefix a;}"
+ "identity id2 {base c:id3;}"
+ "leaf lf {type identityref {base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\">id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
+ data = "<lf xmlns=\"urn:b\">id3</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* The base reference must not refer to a non-existent module,
+ * even if the module is not implemented.
+ */
+ str = "module b {namespace urn:b; prefix b;"
+ "identity ident { base a:baseid;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ /* load (but don't implement) module (b) into context by module (c) */
+ str = "module c {namespace urn:c; prefix c; import b {prefix b;}}";
+ UTEST_INVALID_MODULE(str, LYS_IN_YANG, NULL, LY_EVALID);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Tests in which multiple revisions are available and the import
+ * does not specify an exact revision.
+ */
+
+ /* The old revision was soon implemented
+ * and therefore its "baseid" is used.
+ */
+ str = "module a {namespace urn:a; prefix a;"
+ "revision \"2014-05-08\";"
+ "identity baseid;"
+ "leaf alf { type identityref { base baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module a {namespace urn:a; prefix a;"
+ "revision \"2015-05-08\";"
+ "identity baseid;"
+ "leaf alf { type identityref { base baseid;}}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b;"
+ "import a {prefix a;}"
+ "identity baseref { base a:baseid;}"
+ "identity id1 { base baseref;}"
+ "identity id2 { base baseref;}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "baseref"));
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "id1"));
+ data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id1</alf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "id2"));
+ data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id2</alf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Even if a newer revision has been implemented, the old and
+ * unimplemented one will be used because it has already been
+ * imported. Therefore, if the user wants to use multiple revisions,
+ * he must choose one and implement it as soon as possible.
+ */
+ str = "module a {namespace urn:a; prefix a;"
+ "revision \"2014-05-08\";"
+ "identity baseid;"
+ "leaf alf { type identityref { base baseid;}}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b;"
+ "import a {prefix a;}"
+ "identity baseref { base a:baseid;}"
+ "identity id1 { base baseref;}"
+ "identity id2 { base baseref;}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module a {namespace urn:a; prefix a;"
+ "revision \"2015-05-08\";"
+ "identity baseid;"
+ "leaf alf { type identityref { base baseid;}}"
+ "}";
+ ly_log_level(LY_LLVRB);
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ CHECK_LOG("Implemented module \"a@2015-05-08\" was not and will not "
+ "be imported if the revision-date is missing in the import "
+ "statement. Instead, the revision \"2014-05-08\" is imported.", NULL);
+ ly_log_level(LY_LLWRN);
+ /* Data is inserted only to implemented revision. */
+ data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id1</alf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id2</alf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "baseref"));
+ assert_false(contains_derived_identity(UTEST_LYCTX, "a", "2015-05-08", "baseid", "baseref"));
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Identity testing with if-features. */
+
+ /* The if-feature has no effect if the module is imported. */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
+ "feature f;"
+ "identity baseid { if-feature \"f\";}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b; import a { prefix a;}"
+ "identity id1 { base a:baseid;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Even if the identity in the implemented module is disabled,
+ * it can be used as a base.
+ */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
+ "feature f;"
+ "identity baseid { if-feature \"f\";}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module b {namespace urn:b; prefix b; import a { prefix a;}"
+ "identity id1 { base a:baseid;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Identity derivation cannot be instantiated if it is disabled.
+ * Conversely, if the identity is enabled, it can be instantiated.
+ */
+ str = "module a {namespace urn:a; prefix a;"
+ "identity baseid;"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}"
+ "feature f2;"
+ "feature f3;"
+ "identity id1 { base a:baseid;}"
+ "identity id2 { if-feature \"f2\"; base a:baseid;}"
+ "identity id3 { if-feature \"f3\"; base a:baseid;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ feats[0] = "f2";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\">id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
+ data = "<lf xmlns=\"urn:b\">id3</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Invalid identityref \"id3\" value - identity is disabled by if-feature.",
+ "Schema location \"/b:lf\", line number 1.");
+ RESET_CTX(UTEST_LYCTX);
+
+ /* The derived identities are enabled and disabled in submodule. */
+ str = "submodule asub {yang-version 1.1; belongs-to a {prefix a;}"
+ "feature f2;"
+ "feature f3;"
+ "identity id1 { base a:baseid;}"
+ "identity id2 { if-feature \"f2\"; base a:baseid;}"
+ "identity id3 { if-feature \"f3\"; base a:baseid;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module a {namespace urn:a; prefix a; include asub;"
+ "identity baseid;"
+ "}";
+ feats[0] = "f2";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL);
+ str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
+ data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Invalid identityref \"ids:id3\" value - identity is disabled by if-feature.",
+ "Schema location \"/b:lf\", line number 1.");
+ RESET_CTX(UTEST_LYCTX);
+
+#undef RESET_CTX
+}
+
+static void
+test_type_identityref(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;identity i; identity j; identity k {base i;}"
+ "typedef mytype {type identityref {base i;}}"
+ "leaf l1 {type mytype;} leaf l2 {type identityref {base a:k; base j;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_IDENT, type->basetype);
+ assert_non_null(((struct lysc_type_identityref *)type)->bases);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases));
+ assert_string_equal("i", ((struct lysc_type_identityref *)type)->bases[0]->name);
+
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_IDENT, type->basetype);
+ assert_non_null(((struct lysc_type_identityref *)type)->bases);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases));
+ assert_string_equal("k", ((struct lysc_type_identityref *)type)->bases[0]->name);
+ assert_string_equal("j", ((struct lysc_type_identityref *)type)->bases[1]->name);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b;import a {prefix a;}"
+ "leaf l {type identityref {base a:k; base a:j;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_IDENT, type->basetype);
+ assert_non_null(((struct lysc_type_identityref *)type)->bases);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases));
+ assert_string_equal("k", ((struct lysc_type_identityref *)type)->bases[0]->name);
+ assert_string_equal("j", ((struct lysc_type_identityref *)type)->bases[1]->name);
+
+ /* invalid cases */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type identityref;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing base substatement for identityref type.", "/aa:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; typedef mytype {type identityref;}"
+ "leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing base substatement for identityref type mytype.", "/bb:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; identity i; typedef mytype {type identityref {base i;}}"
+ "leaf l {type mytype {base i;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid base substatement for the type not directly derived from identityref built-in type.", "/cc:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; identity i; typedef mytype {type identityref {base i;}}"
+ "typedef mytype2 {type mytype {base i;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid base substatement for the type \"mytype2\" not directly derived from identityref built-in type.", "/dd:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; identity i; identity j;"
+ "leaf l {type identityref {base i;base j;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Multiple bases in identityref type are allowed only in YANG 1.1 modules.", "/ee:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; identity i;leaf l {type identityref {base j;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Unable to find base (j) of identityref.", "/ff:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type identityref {base x:j;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid prefix used for base (x:j) of identityref.", "/gg:l");
+}
+
+static void
+test_type_leafref(void **state)
+{
+ char *str;
+ struct lys_module *mod;
+ struct lysc_type *type;
+ const char *path;
+ struct lyxp_expr *expr;
+
+ /* lys_path_parse() */
+ path = "invalid_path";
+ assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ path = "..";
+ assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ path = "..[";
+ assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ path = "../";
+ assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ path = "/";
+ assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+
+ path = "../../pref:id/xxx[predicate]/invalid!!!";
+ assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ CHECK_LOG_CTX("Invalid character 0x21 ('!'), perhaps \"invalid\" is supposed to be a function call.", NULL);
+
+ path = "/absolute/prefix:path";
+ assert_int_equal(LY_SUCCESS, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ assert_int_equal(4, expr->used);
+ assert_int_equal(LYXP_TOKEN_OPER_PATH, expr->tokens[0]);
+ assert_int_equal(LYXP_TOKEN_NAMETEST, expr->tokens[1]);
+ assert_int_equal(LYXP_TOKEN_OPER_PATH, expr->tokens[2]);
+ assert_int_equal(LYXP_TOKEN_NAMETEST, expr->tokens[3]);
+ lyxp_expr_free(UTEST_LYCTX, expr);
+
+ /* complete leafref paths */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;"
+ "leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}"
+ "leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/a:target1", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_ptr_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "a", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance);
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/a/target2", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_UINT8, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_int_equal(0, ((struct lysc_type_leafref *)type)->require_instance);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; typedef mytype {type leafref {path /b:target;}}"
+ "typedef mytype2 {type mytype;} typedef mytype3 {type leafref {path /target;}} leaf ref {type mytype2;}"
+ "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type string;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_ptr_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance);
+
+ /* prefixes are reversed to check using correct context of the path! */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix b; import b {prefix c;}"
+ "typedef mytype3 {type c:mytype {require-instance false;}}"
+ "leaf ref1 {type b:mytype3;}leaf ref2 {type c:mytype2;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_ptr_not_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_int_equal(0, ((struct lysc_type_leafref *)type)->require_instance);
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_ptr_not_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes));
+ assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance);
+
+ /* non-prefixed nodes in path are supposed to be from the module where the leafref type is instantiated */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; import b {prefix b;}"
+ "leaf ref {type b:mytype3;}leaf target {type int8;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/target", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance);
+
+ /* conditional leafrefs */
+ str = "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1;"
+ "leaf ref1 {type leafref {path /target;}}"
+ "leaf target {if-feature 'f1'; type boolean;}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Target of leafref \"ref1\" cannot be referenced because it is disabled.", "Schema location \"/e:ref1\".");
+
+ str = "module en {yang-version 1.1;namespace urn:en;prefix en;feature f1;"
+ "leaf ref1 {if-feature 'f1'; type leafref {path /target;}}"
+ "leaf target {type boolean;}}";
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod));
+
+ str = "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1;"
+ "leaf ref1 {if-feature 'f1'; type leafref {path /target;}}}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Not found node \"target\" in path.", "Schema location \"/e:ref1\".");
+
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED);
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module cl {namespace urn:cl;prefix cl;feature f1;"
+ "leaf f {type string; if-feature 'f1';}"
+ "leaf g {type leafref {path \"/cl:f\";}}"
+ "leaf h {type uint16; default 1;}}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module im {namespace urn:im;prefix im;import cl {prefix cl;}"
+ "leaf ref {must \"/cl:h > 0\"; type uint16;}}", LYS_IN_YANG, &mod));
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED);
+ CHECK_LOG_CTX("Target of leafref \"g\" cannot be referenced because it is disabled.", "Schema location \"/cl:g\".");
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;"
+ "list interface{key name;leaf name{type string;}list address {key ip;leaf ip {type string;}}}"
+ "container default-address{leaf ifname{type leafref{ path \"../../interface/name\";}}"
+ "leaf address {type leafref{ path \"../../interface[ name = current()/../ifname ]/address/ip\";}}}}",
+ LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)(*lysc_node_child_p(mod->compiled->data->prev))->prev)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("../../interface[ name = current()/../ifname ]/address/ip",
+ ((struct lysc_type_leafref *)type)->path->expr);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;"
+ "leaf source {type leafref {path \"/endpoint-parent[id=current()/../field]/endpoint/name\";}}"
+ "leaf field {type int32;}list endpoint-parent {key id;leaf id {type int32;}"
+ "list endpoint {key name;leaf name {type string;}}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/endpoint-parent[id=current()/../field]/endpoint/name", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes));
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype);
+
+ /* leafref to imported (not yet implemented) module */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h-imp {namespace urn:h-imp;prefix h-imp;"
+ "leaf l {type string;}}", LYS_IN_YANG, NULL));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module h {namespace urn:h;prefix h;import h-imp {prefix hi;}"
+ "leaf h {type uint16;}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;import h {prefix h;}"
+ "leaf i {type leafref {path /h:h;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "h"));
+ assert_int_equal(1, mod->implemented);
+ assert_non_null(mod->compiled->data);
+ assert_string_equal("h", mod->compiled->data->name);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module j {namespace urn:j;prefix j; leaf j {type string;}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}"
+ "leaf i {type leafref {path \"/ilist[name = current()/../j:j]/value\";}}"
+ "list ilist {key name; leaf name {type string;} leaf value {type uint16;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "j"));
+ assert_int_equal(1, mod->implemented);
+ assert_non_null(mod->compiled->data);
+ assert_string_equal("j", mod->compiled->data->name);
+
+ /* leafref with a default value */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;"
+ "leaf source {type leafref {path \"../target\";}default true;}"
+ "leaf target {type boolean;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("../target", ((struct lysc_type_leafref *)type)->path->expr);
+ assert_non_null(((struct lysc_type_leafref *)type)->realtype);
+ assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref *)type)->realtype->basetype);
+ assert_non_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt);
+ assert_int_equal(LY_TYPE_BOOL, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->boolean);
+
+ /* invalid paths */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Not found node \"invalid\" in path.", "Schema location \"/aa:ref1\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path ../../toohigh;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Too many parent references in path.", "Schema location \"/bb:ref1\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a:invalid;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("No module connected with the prefix \"a\" found (prefix format schema stored mapping).", "Schema location \"/cc:ref1\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf target1 {type string;}"
+ "container a {leaf target2 {type uint8;}} leaf ref1 {type leafref {"
+ "path '/a[target2 = current()/../target1]/target2';}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("List predicate defined for container \"a\" in path.", "Schema location \"/dd:ref1\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;\n container a {leaf target2 {type uint8;}}\n"
+ "leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"ee\" failed.", NULL,
+ "Invalid character 0x21 ('!'), perhaps \"a\" is supposed to be a function call.", "Line number 3.");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.", "Schema location \"/ff:ref1\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;container a {leaf target2 {type uint8;"
+ "status deprecated;}} leaf ref1 {type leafref {path /a/target2;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("A current definition \"ref1\" is not allowed to reference deprecated definition \"target2\".",
+ "Schema location \"/gg:ref1\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;"
+ "leaf ref1 {type leafref;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing path substatement for leafref type.", "/hh:ref1");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;typedef mytype {type leafref;}"
+ "leaf ref1 {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing path substatement for leafref type mytype.", "/ii:ref1");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;"
+ "leaf ref {type leafref {path /target;}}leaf target {type string;config false;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not.", "Schema location \"/kk:ref\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;"
+ "leaf ref {type leafref {path /target; require-instance true;}}leaf target {type string;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.", "/ll:ref");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type leafref {path /target;require-instance false;}}"
+ "leaf ref {type mytype;}leaf target {type string;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.", "/mm:ref");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}\n"
+ "leaf address {type leafref{\n path \"/interface[name is current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"nn\" failed.", NULL,
+ "Invalid character 0x69 ('i'), perhaps \"name\" is supposed to be a function call.", "Line number 5.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}\n"
+ "leaf address {type leafref{\n path \"/interface[name=current()/../ifname/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"oo\" failed.", NULL,
+ "Unexpected XPath expression end.", "Line number 5.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[x:name=current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("No module connected with the prefix \"x\" found (prefix format schema stored mapping).",
+ "Schema location \"/pp:address\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[id=current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Not found node \"id\" in path.", "Schema location \"/qq:address\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name=current() / .. / ifname][name=current()/../test]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"rr\" failed.", NULL,
+ "Duplicate predicate key \"name\" in path.", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name = ../ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"ss\" failed.", NULL,
+ "Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"FunctionName\".", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module tt {namespace urn:tt;prefix tt;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name = current()../ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"tt\" failed.", NULL,
+ "Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"]\".", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module uu {namespace urn:uu;prefix uu;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name = current()/..ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"uu\" failed.", NULL,
+ "Invalid character 'i'[31] of expression '/interface[name = current()/..ifname]/ip'.", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module vv {namespace urn:vv;prefix vv;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name = current()/ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"vv\" failed.", NULL,
+ "Unexpected XPath token \"NameTest\" (\"ifname]/ip\"), expected \"..\".", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ww {namespace urn:ww;prefix ww;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name = current()/../]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"ww\" failed.", NULL,
+ "Unexpected XPath token \"]\" (\"]/ip\").", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module xx {namespace urn:xx;prefix xx;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
+ "leaf address {type leafref{ path \"/interface[name = current()/../#node]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Parsing module \"xx\" failed.", NULL,
+ "Invalid character '#'[32] of expression '/interface[name = current()/../#node]/ip'.", "Line number 4.");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module yy {namespace urn:yy;prefix yy;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}\n"
+ "leaf address {type leafref{ path \"/interface[name=current()/../x:ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("No module connected with the prefix \"x\" found (prefix format schema stored mapping).",
+ "Schema location \"/yy:address\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zz {namespace urn:zz;prefix zz;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}\n"
+ "leaf address {type leafref{ path \"/interface[name=current()/../xxx]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Not found node \"xxx\" in path.", "Schema location \"/zz:address\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zza {namespace urn:zza;prefix zza;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}container c;\n"
+ "leaf address {type leafref{ path \"/interface[name=current()/../c]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Leaf expected instead of container \"c\" in leafref predicate in path.", "Schema location \"/zza:address\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzb {namespace urn:zzb;prefix zzb;\n"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}container c;}\n"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}\n"
+ "leaf address {type leafref{ path \"/interface[c=current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Key expected instead of container \"c\" in path.", "Schema location \"/zzb:address\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzc {namespace urn:zzc;prefix zzc;\n"
+ "leaf source {type leafref {path \"../target\";}default true;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Not found node \"target\" in path.", "Schema location \"/zzc:source\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzd {namespace urn:zzd;prefix zzd;\n"
+ "leaf source {type leafref {path \"../target\";}default true;}\n"
+ "leaf target {type uint8;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid type uint8 value \"true\".).",
+ "Schema location \"/zzd:source\".");
+
+ /* circular chain */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aaa {namespace urn:aaa;prefix aaa;\n"
+ "leaf ref1 {type leafref {path /ref2;}}\n"
+ "leaf ref2 {type leafref {path /ref3;}}\n"
+ "leaf ref3 {type leafref {path /ref4;}}\n"
+ "leaf ref4 {type leafref {path /ref1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid leafref path \"/ref1\" - circular chain of leafrefs detected.", "Schema location \"/aaa:ref4\".");
+}
+
+static void
+test_type_empty(void **state)
+{
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
+ "leaf l {type empty; default x;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location \"/aa:l\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}"
+ "leaf l {type mytype;}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Invalid type \"mytype\" - \"empty\" type must not have a default value (x).", "/bb:l");
+}
+
+static void
+test_type_union(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a; typedef mybasetype {type string;}"
+ "typedef mytype {type union {type int8; type mybasetype;}}"
+ "leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(2, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union *)type)->types);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types));
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union *)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[1]->basetype);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b; typedef mybasetype {type string;}"
+ "typedef mytype {type union {type int8; type mybasetype;}}"
+ "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union *)type)->types);
+ assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union *)type)->types[1]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c; typedef mybasetype {type string;}"
+ "typedef mytype {type union {type leafref {path ../target;} type mybasetype;}}"
+ "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}"
+ "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type int8;}}",
+ LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union *)type)->types);
+ assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union *)type)->types[1]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype);
+ assert_non_null(((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype->basetype);
+
+ /* invalid unions */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;typedef mytype {type union;}"
+ "leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing type substatement for union type mytype.", "/aa:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type union;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Missing type substatement for union type.", "/bb:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;typedef mytype {type union{type int8; type string;}}"
+ "leaf l {type mytype {type string;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid type substatement for the type not directly derived from union built-in type.", "/cc:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;typedef mytype {type union{type int8; type string;}}"
+ "typedef mytype2 {type mytype {type string;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.", "/dd:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type union{type mytype; type string;}}"
+ "leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ee:l");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;typedef mytype {type mytype2;}"
+ "typedef mytype2 {type mytype;} leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ef:l");
+}
+
+static void
+test_type_dflt(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_type *type;
+ struct lysc_node_leaf *leaf;
+ uint8_t dynamic;
+
+ /* default is not inherited from union's types */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a; typedef mybasetype {type string;default hello;units xxx;}"
+ "leaf l {type union {type decimal64 {fraction-digits 2;} type mybasetype;}}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union *)type)->types);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[1]->basetype);
+ assert_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt);
+ assert_null(((struct lysc_node_leaf *)mod->compiled->data)->units);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; typedef mybasetype {type string;default hello;units xxx;}"
+ "leaf l {type mybasetype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(3, type->refcount); /* 2x type reference, 1x default value's reference (typedf's default does not reference own type)*/
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("xxx", leaf->units);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; typedef mybasetype {type string;default hello;units xxx;}"
+ "leaf l {type mybasetype; default goodbye;units yyy;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(3, type->refcount); /* 2x type reference, 1x default value's reference */
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("yyy", leaf->units);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; typedef mybasetype {type string;default hello;units xxx;}"
+ "typedef mytype {type mybasetype;}leaf l1 {type mytype; default goodbye;units yyy;}"
+ "leaf l2 {type mytype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(6, type->refcount); /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("yyy", leaf->units);
+ type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(6, type->refcount); /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ leaf = (struct lysc_node_leaf *)mod->compiled->data->next;
+ assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("xxx", leaf->units);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e; typedef mybasetype {type string;}"
+ "typedef mytype {type mybasetype; default hello;units xxx;}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(4, type->refcount); /* 3x type reference, 1x default value's reference (typedef's default does not reference own type) */
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("xxx", leaf->units);
+
+ /* mandatory leaf does not takes default value from type */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;typedef mytype {type string; default hello;units xxx;}"
+ "leaf l {type mytype; mandatory true;}}", LYS_IN_YANG, &mod));
+ type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt);
+ assert_string_equal("xxx", ((struct lysc_node_leaf *)mod->compiled->data)->units);
+}
+
+static void
+test_status(void **state)
+{
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
+ "container c {status deprecated; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"deprecated\" status of parent \"c\".", "/aa:c/l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;"
+ "container c {status obsolete; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/bb:c/l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;"
+ "container c {status obsolete; leaf l {status deprecated; type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Status \"deprecated\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/cc:c/l");
+
+ /* just a warning */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:dd;prefix d;"
+ "container c {leaf l {status obsolete; type string;}}"
+ "container d {leaf m {when \"../../c/l\"; type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition \"../../c/l\" may be referencing deprecated node \"l\".", NULL);
+}
+
+static void
+test_grouping(void **state)
+{
+ /* result ok, but a warning about not used locally scoped grouping printed */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a; grouping grp1 {leaf a1 {type string;}}"
+ "container a {leaf x {type string;} grouping grp2 {leaf a2 {type string;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Locally scoped grouping \"grp2\" not used.", NULL);
+ UTEST_LOG_CLEAN;
+
+ /* result ok - when statement or leafref target must be checked only at the place where the grouping is really instantiated */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; grouping grp {"
+ "leaf ref {type leafref {path \"../name\";}}"
+ "leaf cond {type string; when \"../name = 'specialone'\";}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX(NULL, NULL);
+
+ /* invalid - error in a non-instantiated grouping */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
+ "grouping grp {leaf x {type leafref;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:{grouping='grp'}/x");
+ UTEST_LOG_CLEAN;
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
+ "container a {grouping grp {leaf x {type leafref;}}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:a/{grouping='grp'}/x");
+
+ /* config check */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module z1 {namespace urn:z1;prefix z1;"
+ "container root;}\n"
+ "module z2 {namespace urn:z2;prefix z2;"
+ "grouping leafs_group {"
+ " leaf name {type string; config true;}"
+ " leaf value {type uint32; config true;}"
+ "}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module z3 {namespace urn:z3;prefix z3;"
+ "import z1 {prefix z1;} import z2 {prefix z2;}"
+ "grouping grp_a_top {leaf a1 {type int8;}}"
+ "grouping list_group {"
+ " list mylist {key \"name\"; unique \"value\"; uses z2:leafs_group;}"
+ "}"
+ "augment /z1:root { uses list_group;} }", LYS_IN_YANG, NULL));
+
+ /* identity */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module y1 {namespace urn:y1;prefix y1;"
+ "identity base_identity;"
+ "identity id1 {base \"base_identity\";}"
+ "grouping attrs_group {"
+ " leaf name {type identityref {base \"base_identity\";} default \"id1\";}"
+ "}}", LYS_IN_YANG, NULL));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module y2 {namespace urn:y2;prefix y2;"
+ "import y1 {prefix y1;}"
+ "container root {uses y1:attrs_group;}}", LYS_IN_YANG, NULL));
+}
+
+static void
+test_uses(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node *parent, *child;
+ const struct lysc_node_container *cont;
+ const struct lysc_node_leaf *leaf;
+ const struct lysc_node_choice *choice;
+ const struct lysc_node_case *cs;
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module grp {namespace urn:grp;prefix g; typedef mytype {type string;} feature f;"
+ "grouping grp {leaf x {type mytype;} leaf y {type string; if-feature f;}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;import grp {prefix g;}"
+ "grouping grp_a_top {leaf a1 {type int8;}}"
+ "container a {uses grp_a; uses grp_a_top; uses g:grp; grouping grp_a {leaf a2 {type uint8;}}}}", LYS_IN_YANG, &mod));
+ assert_non_null((parent = mod->compiled->data));
+ assert_int_equal(LYS_CONTAINER, parent->nodetype);
+ assert_non_null((child = ((struct lysc_node_container *)parent)->child));
+ assert_string_equal("a2", child->name);
+ assert_ptr_equal(mod, child->module);
+ assert_non_null((child = child->next));
+ assert_string_equal("a1", child->name);
+ assert_ptr_equal(mod, child->module);
+ assert_non_null((child = child->next));
+ assert_string_equal("x", child->name);
+ assert_ptr_equal(mod, child->module);
+ assert_null((child = child->next));
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {when 1; type string;} leaf c {type string;}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;include bsub;uses grp {when 2;}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+ assert_string_equal("b", leaf->name);
+ assert_int_equal(2, LY_ARRAY_COUNT(leaf->when));
+ assert_int_equal(1, leaf->when[0]->refcount);
+ assert_non_null(leaf->when[0]->context);
+ assert_string_equal("b", leaf->when[0]->context->name);
+ assert_int_equal(2, leaf->when[1]->refcount);
+ assert_null(leaf->when[1]->context);
+
+ leaf = (struct lysc_node_leaf *)leaf->next;
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+ assert_string_equal("c", leaf->name);
+ assert_int_equal(1, LY_ARRAY_COUNT(leaf->when));
+ assert_int_equal(2, leaf->when[0]->refcount);
+ assert_null(leaf->when[0]->context);
+
+ UTEST_LOG_CLEAN;
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;"
+ "grouping grp {leaf l {type string;}leaf k {type string; status obsolete;}}"
+ "uses grp {status deprecated;}}", LYS_IN_YANG, &mod));
+ assert_int_equal(LYS_LEAF, mod->compiled->data->nodetype);
+ assert_string_equal("l", mod->compiled->data->name);
+ assert_true(LYS_STATUS_DEPRC & mod->compiled->data->flags);
+ assert_int_equal(LYS_LEAF, mod->compiled->data->next->nodetype);
+ assert_string_equal("k", mod->compiled->data->next->name);
+ assert_true(LYS_STATUS_OBSLT & mod->compiled->data->next->flags);
+ CHECK_LOG(NULL, NULL); /* no warning about inheriting deprecated flag from uses */
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; grouping grp {container g;}"
+ "container top {uses grp {augment g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+ assert_non_null(child = lysc_node_child(mod->compiled->data));
+ assert_string_equal("g", child->name);
+ assert_non_null(child = lysc_node_child(child));
+ assert_string_equal("x", child->name);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {yang-version 1.1;namespace urn:e;prefix e; grouping grp {action g { description \"super g\";}}"
+ "container top {action e; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+ cont = (const struct lysc_node_container *)mod->compiled->data;
+ assert_non_null(cont->actions);
+ assert_non_null(cont->actions->next);
+ assert_null(cont->actions->next->next);
+ assert_string_equal("e", cont->actions->next->name);
+ assert_string_equal("g", cont->actions->name);
+ assert_string_equal("ultra g", cont->actions->dsc);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1;namespace urn:f;prefix f; grouping grp {notification g { description \"super g\";}}"
+ "container top {notification f; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+ cont = (const struct lysc_node_container *)mod->compiled->data;
+ assert_non_null(cont->notifs);
+ assert_non_null(cont->notifs->next);
+ assert_null(cont->notifs->next->next);
+ assert_string_equal("f", cont->notifs->next->name);
+ assert_string_equal("g", cont->notifs->name);
+ assert_string_equal("ultra g", cont->notifs->dsc);
+
+ /* empty grouping */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g; grouping grp; uses grp;}", LYS_IN_YANG, &mod));
+ assert_null(mod->compiled->data);
+
+ /* choice in uses */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1;namespace urn:h;prefix h; grouping grp {choice gch {case gc1 { leaf y { type string;}} case gc2 {leaf z {type string;}}}}"
+ "choice ch {case one { leaf x {type string;}} case two { uses grp;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+ choice = (const struct lysc_node_choice *)mod->compiled->data;
+ assert_string_equal("ch", choice->name);
+ cs = choice->cases;
+ assert_non_null(cs);
+ assert_string_equal("one", cs->name);
+ assert_non_null(cs->child);
+ assert_string_equal("x", cs->child->name);
+
+ cs = (struct lysc_node_case *)cs->next;
+ assert_non_null(cs);
+ assert_string_equal("two", cs->name);
+ assert_non_null(cs->child);
+ assert_string_equal("gch", cs->child->name);
+
+ /* top-level uses with augment and refine */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i; grouping grp {container g;}"
+ "uses grp {augment g {leaf x {type int8;}} refine g {description \"dsc\";}}}",
+ LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+ child = mod->compiled->data;
+ assert_string_equal("g", child->name);
+ cont = (const struct lysc_node_container *)child;
+ assert_string_equal("dsc", cont->dsc);
+ assert_non_null(child = lysc_node_child(child));
+ assert_string_equal("x", child->name);
+
+ /* unique */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module j {namespace urn:j;prefix j;"
+ "grouping grp {list l {key \"k\"; unique \"l\"; leaf k {type string;} leaf l {type string;}}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}"
+ "container a {uses j:grp;}}", LYS_IN_YANG, NULL));
+
+ /* if-features */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;"
+ "feature f;"
+ "grouping grp {container g; leaf l{type string;}}"
+ "uses grp {if-feature f;}}",
+ LYS_IN_YANG, &mod));
+ assert_null(mod->compiled->data);
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Grouping \"missinggrp\" referenced by a uses statement not found.", "/aa:{uses='missinggrp'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;uses grp;"
+ "grouping grp {leaf a{type string;}uses grp1;}"
+ "grouping grp1 {leaf b {type string;}uses grp2;}"
+ "grouping grp2 {leaf c {type string;}uses grp;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Grouping \"grp\" references itself through a uses statement.", "/bb:{uses='grp'}/{uses='grp1'}/{uses='grp2'}/{uses='grp'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;uses a:missingprefix;}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid prefix used for grouping \"a:missingprefix\" reference.", "/cc:{uses='a:missingprefix'}");
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;grouping grp{leaf a{type string;}}"
+ "leaf a {type string;}uses grp;}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/dd:{uses='grp'}/dd:a");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;grouping grp {leaf l {type string; status deprecated;}}"
+ "uses grp {status obsolete;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".",
+ "/ee:{uses='grp'}/ee:l");
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;grouping grp {leaf l {type string;}}"
+ "leaf l {type int8;}uses grp;}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Duplicate identifier \"l\" of data definition/RPC/action/notification statement.", "/ff:{uses='grp'}/ff:l");
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module fg {namespace urn:fg;prefix fg;grouping grp {leaf m {type string;}}"
+ "uses grp;leaf m {type int8;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Duplicate identifier \"m\" of data definition/RPC/action/notification statement.", "/fg:m");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; grouping grp {container g;}"
+ "leaf g {type string;}"
+ "container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"/g\" - name test expected instead of \"/\".", "/gg:top/{uses='grp'}/{augment='/g'}");
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1;namespace urn:hh;prefix hh;"
+ "grouping grp {notification g { description \"super g\";}}"
+ "container top {notification h; uses grp {refine h {description \"ultra h\";}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Refine(s) target node \"h\" in grouping \"grp\" was not found.", "/hh:top/{uses='grp'}");
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module ii {yang-version 1.1;namespace urn:ii;prefix ii;"
+ "grouping grp {action g { description \"super g\";}}"
+ "container top {action i; uses grp {refine i {description \"ultra i\";}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Refine(s) target node \"i\" in grouping \"grp\" was not found.", "/ii:top/{uses='grp'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {yang-version 1.1;namespace urn:jj;prefix jj;"
+ "grouping grp {leaf j { when \"1\"; type invalid;}}"
+ "container top {uses grp;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Referenced type \"invalid\" not found.", "/jj:top/{uses='grp'}/j");
+}
+
+static void
+test_refine(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node *parent, *child;
+ struct lysc_node_leaf *leaf;
+ struct lysc_node_leaflist *llist;
+ uint8_t dynamic;
+ struct ly_in *in;
+ const char *data, *feats1[] = {"f", NULL}, *feats2[] = {"fa", NULL};
+
+ data = "module grp {yang-version 1.1;namespace urn:grp;prefix g; feature f;typedef mytype {type string; default cheers!;}"
+ "grouping grp {container c {leaf l {type mytype; default goodbye;}"
+ "leaf-list ll {type mytype; default goodbye; max-elements 6;}"
+ "choice ch {default ca; leaf ca {type int8;}leaf cb{type uint8;}}"
+ "leaf x {type mytype; mandatory true; must 1;}"
+ "anydata a {mandatory false; if-feature f; description original; reference original;}"
+ "container c {config false; leaf l {type string;}}}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats1, NULL));
+
+ data = "module a {yang-version 1.1;namespace urn:a;prefix a;import grp {prefix g;}feature fa;"
+ "uses g:grp {refine c/l {default hello; config false;}"
+ "refine c/ll {default hello;default world;}"
+ "refine c/ch {default cb;config true; if-feature fa;}"
+ "refine c/x {mandatory false; must ../ll;description refined; reference refined;}"
+ "refine c/a {mandatory true; must 1; description refined; reference refined;}"
+ "refine c/ll {max-elements 5;}"
+ "refine c/c {config true;presence indispensable;}}}";
+ ly_in_memory(in, data);
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats2, &mod));
+ ly_in_free(in, 0);
+
+ assert_non_null((parent = mod->compiled->data));
+ assert_int_equal(LYS_CONTAINER, parent->nodetype);
+ assert_string_equal("c", parent->name);
+ assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)parent)->child));
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+ assert_string_equal("l", leaf->name);
+ assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(LYS_CONFIG_R, leaf->flags & LYS_CONFIG_MASK);
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_int_equal(LYS_LEAFLIST, llist->nodetype);
+ assert_string_equal("ll", llist->name);
+ assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts));
+ assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("world", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(5, llist->max);
+ assert_non_null(child = llist->next);
+ assert_int_equal(LYS_CHOICE, child->nodetype);
+ assert_string_equal("ch", child->name);
+ assert_string_equal("cb", ((struct lysc_node_choice *)child)->dflt->name);
+ assert_true(LYS_SET_DFLT & ((struct lysc_node_choice *)child)->dflt->flags);
+ assert_false(LYS_SET_DFLT & ((struct lysc_node_choice *)child)->cases[0].flags);
+ assert_non_null(leaf = (struct lysc_node_leaf *)child->next);
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+ assert_string_equal("x", leaf->name);
+ assert_false(LYS_MAND_TRUE & leaf->flags);
+ assert_string_equal("cheers!", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_non_null(leaf->musts);
+ assert_int_equal(2, LY_ARRAY_COUNT(leaf->musts));
+ assert_string_equal("refined", leaf->dsc);
+ assert_string_equal("refined", leaf->ref);
+ assert_non_null(child = leaf->next);
+ assert_int_equal(LYS_ANYDATA, child->nodetype);
+ assert_string_equal("a", child->name);
+ assert_true(LYS_MAND_TRUE & child->flags);
+ assert_non_null(((struct lysc_node_anydata *)child)->musts);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_anydata *)child)->musts));
+ assert_string_equal("refined", child->dsc);
+ assert_string_equal("refined", child->ref);
+ assert_non_null(child = child->next);
+ assert_int_equal(LYS_CONTAINER, child->nodetype);
+ assert_string_equal("c", child->name);
+ assert_true(LYS_PRESENCE & child->flags);
+ assert_true(LYS_CONFIG_W & child->flags);
+ assert_true(LYS_CONFIG_W & ((struct lysc_node_container *)child)->child->flags);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b;import grp {prefix g;}"
+ "uses g:grp {status deprecated; refine c/x {default hello; mandatory false;}}}", LYS_IN_YANG, &mod));
+ assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)mod->compiled->data)->child->prev->prev->prev));
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+ assert_string_equal("x", leaf->name);
+ assert_false(LYS_MAND_TRUE & leaf->flags);
+ assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;"
+ "grouping alg {leaf alg2 {type bits {"
+ "bit ftp;bit h323-q931;bit h323-ras;bit pptp;bit rtsp;bit sip-tcp;bit sip-udp;bit tftp;bit dns-udp;}}}"
+ "container conf {uses alg {refine alg2 {default \"dns-udp\";}}}}", LYS_IN_YANG, &mod));
+
+ /* invalid */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;import grp {prefix g;}"
+ "uses g:grp {refine c {default hello;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of container node - it is not possible to replace \"default\" property.", "/aa:{uses='g:grp'}/aa:c/{refine='c'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import grp {prefix g;}"
+ "uses g:grp {refine c/l {default hello; default world;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of leaf with too many (2) default properties.", "/bb:{uses='g:grp'}/bb:c/l/{refine='c/l'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;import grp {prefix g;}"
+ "uses g:grp {refine c/ll {default hello; default world;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.", "/cc:{uses='g:grp'}/cc:c/ll/{refine='c/ll'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;import grp {prefix g;}"
+ "uses g:grp {refine c/ll {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of leaf-list node - it is not possible to replace \"mandatory\" property.", "/dd:{uses='g:grp'}/dd:c/ll/{refine='c/ll'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;import grp {prefix g;}"
+ "uses g:grp {refine c/l {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ee:{uses='g:grp'}/ee:c/l",
+ "Invalid mandatory leaf with a default value.", "/ee:{uses='g:grp'}/ee:c/l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;import grp {prefix g;}"
+ "uses g:grp {refine c/ch {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ef:{uses='g:grp'}/ef:c/ch",
+ "Invalid mandatory choice with a default case.", "/ef:{uses='g:grp'}/ef:c/ch");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;import grp {prefix g;}"
+ "uses g:grp {refine c/ch/ca/ca {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Mandatory node \"ca\" under the default case \"ca\".", "/ff:{uses='g:grp'}/ff:c/ch");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;import grp {prefix g;}"
+ "uses g:grp {refine c/x {default hello;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg:{uses='g:grp'}/gg:c/x",
+ "Invalid mandatory leaf with a default value.", "/gg:{uses='g:grp'}/gg:c/x");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import grp {prefix g;}"
+ "uses g:grp {refine c/c/l {config true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/hh:{uses='g:grp'}/hh:c/c/l",
+ "Configuration node cannot be child of any state data node.", "/hh:{uses='g:grp'}/hh:c/c/l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;grouping grp {leaf l {type string; status deprecated;}}"
+ "uses grp {status obsolete;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".",
+ "/ii:{uses='grp'}/ii:l");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;import grp {prefix g;}"
+ "uses g:grp {refine c/x {presence nonsence;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"presence\" property.", "/jj:{uses='g:grp'}/jj:c/x/{refine='c/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;import grp {prefix g;}"
+ "uses g:grp {refine c/ch {must 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of choice node - it is not possible to add \"must\" property.", "/kk:{uses='g:grp'}/kk:c/ch/{refine='c/ch'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;import grp {prefix g;}"
+ "uses g:grp {refine c/x {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"min-elements\" property.", "/ll:{uses='g:grp'}/ll:c/x/{refine='c/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;import grp {prefix g;}"
+ "uses g:grp {refine c/ll {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm:{uses='g:grp'}/mm:c/ll",
+ "The default statement is present on leaf-list with a nonzero min-elements.", "/mm:{uses='g:grp'}/mm:c/ll");
+}
+
+static void
+test_augment(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node *node;
+ const struct lysc_node_choice *ch;
+ const struct lysc_node_case *c;
+ const struct lysc_node_container *cont;
+ const struct lysc_node_action *rpc;
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a;prefix a; typedef atype {type string;}"
+ "container top {leaf a {type string;}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}"
+ "leaf b {type a:atype;}}", LYS_IN_YANG, &mod));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module c {namespace urn:c;prefix c; import a {prefix a;}"
+ "augment /a:top { container c {leaf c {type a:atype;}}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;import a {prefix a;} import c {prefix c;}"
+ "augment /a:top/c:c { leaf d {type a:atype;} leaf c {type string;}}}", LYS_IN_YANG, &mod));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a")));
+ assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "b"));
+ assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "c"));
+ assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "d"));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal(node->name, "top");
+ assert_non_null(node = lysc_node_child(node));
+ assert_string_equal(node->name, "a");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "c");
+ assert_non_null(node = lysc_node_child(node));
+ assert_string_equal(node->name, "c");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "d");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "c");
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;choice ch {leaf a {type string;}}"
+ "augment /ch/c {when 1; leaf lc2 {type uint16;}}"
+ "augment /ch { when 1; leaf b {type int8;} case c {leaf lc1 {type uint8;}}}}", LYS_IN_YANG, &mod));
+ assert_non_null((ch = (const struct lysc_node_choice *)mod->compiled->data));
+ assert_null(mod->compiled->data->next);
+ assert_string_equal("ch", ch->name);
+
+ assert_non_null(c = (const struct lysc_node_case *)ch->cases);
+ assert_string_equal("a", c->name);
+ assert_null(c->when);
+ assert_string_equal("a", c->child->name);
+
+ assert_non_null(c = (const struct lysc_node_case *)c->next);
+ assert_string_equal("b", c->name);
+ assert_non_null(c->when);
+ assert_string_equal("b", c->child->name);
+
+ assert_non_null(c = (const struct lysc_node_case *)c->next);
+ assert_string_equal("c", c->name);
+ assert_non_null(c->when);
+ assert_string_equal("lc1", ((const struct lysc_node_case *)c)->child->name);
+ assert_null(lysc_node_when(((const struct lysc_node_case *)c)->child));
+ assert_string_equal("lc2", ((const struct lysc_node_case *)c)->child->next->name);
+ assert_non_null(lysc_node_when(((const struct lysc_node_case *)c)->child->next));
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;grouping g {leaf a {type string;}}"
+ "container c;"
+ "augment /c {uses g;}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = lysc_node_child(mod->compiled->data));
+ assert_string_equal(node->name, "a");
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule gsub {belongs-to g {prefix g;}"
+ "augment /c {container sub;}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;include gsub; container c;"
+ "augment /c/sub {leaf main {type string;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(mod->compiled->data);
+ assert_string_equal("c", mod->compiled->data->name);
+ assert_non_null(node = ((struct lysc_node_container *)mod->compiled->data)->child);
+ assert_string_equal("sub", node->name);
+ assert_non_null(node = ((struct lysc_node_container *)node)->child);
+ assert_string_equal("main", node->name);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module himp {namespace urn:hi;prefix hi;container top; rpc func;}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;import himp {prefix hi;}container top;"
+ "augment /hi:top {container p {presence XXX; leaf x {mandatory true;type string;}}}"
+ "augment /hi:top {list ll {key x;leaf x {type string;}leaf y {mandatory true; type string;}}}"
+ "augment /hi:top {leaf l {type string; mandatory true; config false;}}"
+ "augment /top {leaf l {type string; mandatory true;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_non_null(node = ((struct lysc_node_container *)node)->child);
+ assert_string_equal("l", node->name);
+ assert_true(node->flags & LYS_MAND_TRUE);
+ assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "himp"));
+ assert_non_null(node = mod->compiled->data);
+ assert_non_null(node = ((struct lysc_node_container *)node)->child);
+ assert_string_equal("p", node->name);
+ assert_non_null(node = node->next);
+ assert_string_equal("l", node->name);
+ assert_true(node->flags & LYS_CONFIG_R);
+ assert_non_null(node = node->next);
+ assert_string_equal("ll", node->name);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;import himp {prefix hi;}"
+ "augment /hi:func/hi:input {leaf x {type string;}}"
+ "augment /hi:func/hi:output {leaf y {type string;}}}", LYS_IN_YANG, NULL));
+ assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "himp"));
+ assert_non_null(rpc = mod->compiled->rpcs);
+ assert_null(rpc->next);
+ assert_non_null(rpc->input.child);
+ assert_string_equal("x", rpc->input.child->name);
+ assert_null(rpc->input.child->next);
+ assert_non_null(rpc->output.child);
+ assert_string_equal("y", rpc->output.child->name);
+ assert_null(rpc->output.child->next);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;yang-version 1.1; container root;"
+ "grouping grp {notification grp-notif;}"
+ "augment /root {uses grp;}}", LYS_IN_YANG, &mod));
+ assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data);
+ assert_null(cont->child);
+ assert_non_null(cont->notifs);
+ assert_null(cont->notifs->next);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL);
+ UTEST_LOG_CLEAN;
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k; prefix k;yang-version 1.1;"
+ "feature f;"
+ "container c {if-feature f; leaf a {type string;}}}", LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l; prefix l; yang-version 1.1;"
+ "import k {prefix k;}"
+ "augment /k:c {leaf b {type string;}}"
+ "leaf c {when \"/k:c/l:b\"; type string;}}", LYS_IN_YANG, NULL));
+ /* no xpath warning expected */
+ CHECK_LOG(NULL, NULL);
+ assert_null(mod->compiled->data);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;yang-version 1.1;"
+ "feature f;"
+ "container root{container cont{if-feature f;}}"
+ "augment /root/cont {if-feature f; leaf l{type string;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data);
+ assert_null(cont->child);
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}"
+ "augment /x/ {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"/x/\" - unexpected end of expression.", "/aa:{augment='/x/'}");
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}"
+ "augment /x {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Augment target node \"/x\" from module \"aa\" was not found.", "/aa:{augment='/x'}");
+
+ assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; container c {leaf a {type string;}}"
+ "augment /c {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/bb:{augment='/c'}/a");
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c {leaf a {type string;}}"
+ "augment /c/a {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Augment target node \"/c/a\" from module \"cc\" was not found.", "/cc:{augment='/c/a'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; container c {leaf a {type string;}}"
+ "augment /c {case b {leaf d {type int8;}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid augment of container node which is not allowed to contain case node \"b\".", "/dd:{augment='/c'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; import himp {prefix hi;}"
+ "augment /hi:top {container c {leaf d {mandatory true; type int8;}}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid augment adding mandatory node \"c\" without making it conditional via when statement.", "/ee:{augment='/hi:top'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; container top;"
+ "augment ../top {leaf x {type int8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"../top\" - \"/\" expected instead of \"..\".", "/ff:{augment='../top'}");
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; rpc func;"
+ "augment /func {leaf x {type int8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Augment target node \"/func\" from module \"gg\" was not found.", "/gg:{augment='/func'}");
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import himp {prefix hi;}"
+ "augment /hi:func/input {leaf x {type string;}}"
+ "augment /hi:func/output {leaf y {type string;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Augment target node \"/hi:func/input\" from module \"hh\" was not found.", "/hh:{augment='/hi:func/input'}");
+}
+
+static void
+test_deviation(void **state)
+{
+ struct lys_module *mod;
+ const struct lysc_node *node;
+ const struct lysc_node_list *list;
+ const struct lysc_node_leaflist *llist;
+ const struct lysc_node_leaf *leaf;
+ const char *str;
+ uint8_t dynamic;
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a;prefix a;"
+ "container top {leaf a {type string;} leaf b {type string;} leaf c {type string;}}"
+ "choice ch {default c; case b {leaf b{type string;}} case a {leaf a{type string;} leaf x {type string;}}"
+ " case c {leaf c{type string;}}}"
+ "rpc func1 { input { leaf x {type int8;}} output {leaf y {type int8;}}}"
+ "rpc func2;}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}"
+ "deviation /a:top/a:b {deviate not-supported;}"
+ "deviation /a:ch/a:a/a:x {deviate not-supported;}"
+ "deviation /a:ch/a:c {deviate not-supported;}"
+ "deviation /a:ch/a:b {deviate not-supported;}"
+ "deviation /a:ch/a:a/a:a {deviate not-supported;}"
+ "deviation /a:ch {deviate replace {default a;}}"
+ "deviation /a:func1/a:input {deviate not-supported;}"
+ "deviation /a:func1/a:output {deviate not-supported;}"
+ "deviation /a:func2 {deviate not-supported;}}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a")));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal(node->name, "top");
+ assert_non_null(node = lysc_node_child(node));
+ assert_string_equal(node->name, "a");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "c");
+ assert_null(node = node->next);
+ assert_non_null(node = mod->compiled->data->next);
+ assert_string_equal("ch", node->name);
+ assert_non_null(((struct lysc_node_choice *)node)->dflt);
+ assert_non_null(((struct lysc_node_choice *)node)->cases);
+ assert_null(((struct lysc_node_choice *)node)->cases->next);
+ assert_non_null(mod->compiled->rpcs);
+ assert_null(mod->compiled->rpcs->next);
+ assert_null(mod->compiled->rpcs->input.child);
+ assert_null(mod->compiled->rpcs->output.child);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; typedef mytype {type string; units kilometers;}"
+ "leaf c1 {type mytype;} leaf c2 {type mytype; units meters;} leaf c3 {type mytype; units meters;}"
+ "deviation /c1 {deviate add {units meters;}}"
+ "deviation /c2 {deviate delete {units meters;}}"
+ "deviation /c3 {deviate replace {units centimeters;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("c1", node->name);
+ assert_string_equal("meters", ((struct lysc_node_leaf *)node)->units);
+ assert_non_null(node = node->next);
+ assert_string_equal("c2", node->name);
+ assert_string_equal("kilometers", ((struct lysc_node_leaf *)node)->units);
+ assert_non_null(node = node->next);
+ assert_string_equal("c3", node->name);
+ assert_string_equal("centimeters", ((struct lysc_node_leaf *)node)->units);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; leaf c1 {type string; must 1;}"
+ "container c2 {presence yes; must 1; must 2;} leaf c3 {type string; must 1; must 3;}"
+ "deviation /c1 {deviate add {must 3;}}"
+ "deviation /c2 {deviate delete {must 2;}}"
+ "deviation /c3 {deviate delete {must 3; must 1;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("c1", node->name);
+ assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_node_leaf *)node)->musts));
+ assert_string_equal("3", ((struct lysc_node_leaf *)node)->musts[1].cond->expr);
+ assert_non_null(node = node->next);
+ assert_string_equal("c2", node->name);
+ assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_container *)node)->musts));
+ assert_string_equal("1", ((struct lysc_node_container *)node)->musts[0].cond->expr);
+ assert_non_null(node = node->next);
+ assert_string_equal("c3", node->name);
+ assert_null(((struct lysc_node_leaf *)node)->musts);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module e {yang-version 1.1; namespace urn:e;prefix e; typedef mytype {type string; default nothing;}"
+ "choice a {default aa;leaf aa {type string;} leaf ab {type string;} leaf ac {type string; mandatory true;}}"
+ "choice b {default ba;leaf ba {type string;} leaf bb {type string;}}"
+ "leaf c {default hello; type string;}"
+ "leaf-list d {default hello; default world; type string;}"
+ "leaf c2 {type mytype;} leaf-list d2 {type mytype;}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1; namespace urn:f;prefix f;import e {prefix x;}"
+ "deviation /x:a {deviate delete {default aa;}}"
+ "deviation /x:b {deviate delete {default ba;}}"
+ "deviation /x:c {deviate delete {default hello;}}"
+ "deviation /x:d {deviate delete {default world;}}}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e")));
+ assert_non_null(node = mod->compiled->data);
+ assert_null(((struct lysc_node_choice *)node)->dflt);
+ assert_non_null(node = node->next);
+ assert_null(((struct lysc_node_choice *)node)->dflt);
+ assert_non_null(leaf = (struct lysc_node_leaf *)node->next);
+ assert_null(leaf->dflt);
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts));
+ assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_non_null(leaf = (struct lysc_node_leaf *)llist->next);
+ assert_string_equal("nothing", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(5, leaf->dflt->realtype->refcount); /* 3x type reference, 2x default value reference (typedef's default does not reference own type) */
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts));
+ assert_string_equal("nothing", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;import e {prefix x;}"
+ "deviation /x:b {deviate add {default x:ba;}}"
+ "deviation /x:c {deviate add {default bye;}}"
+ "deviation /x:d {deviate add {default all; default people;}}"
+ "deviation /x:c2 {deviate add {default hi; must 1;}}"
+ "deviation /x:d2 {deviate add {default hi; default all;}}}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e")));
+ assert_non_null(node = mod->compiled->data);
+ assert_null(((struct lysc_node_choice *)node)->dflt);
+ assert_non_null(node = node->next);
+ assert_non_null(((struct lysc_node_choice *)node)->dflt);
+ assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name);
+ assert_non_null(leaf = (struct lysc_node_leaf *)node->next);
+ assert_non_null(leaf->dflt);
+ assert_string_equal("bye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_int_equal(3, LY_ARRAY_COUNT(llist->dflts));
+ assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("people", llist->dflts[2]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[2], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_non_null(leaf = (struct lysc_node_leaf *)llist->next);
+ assert_non_null(leaf->dflt);
+ assert_string_equal("hi", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(6, leaf->dflt->realtype->refcount); /* 3x type reference, 3x default value reference
+ - previous type's default values were replaced by node's default values where d2 now has 2 default values */
+ assert_int_equal(1, LY_ARRAY_COUNT(leaf->musts));
+ assert_int_equal(1, LY_ARRAY_COUNT(leaf->musts[0].prefixes));
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts));
+ assert_string_equal("hi", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1; namespace urn:h;prefix h;import e {prefix x;}"
+ "deviation /x:b {deviate replace {default x:ba;}}"
+ "deviation /x:c {deviate replace {default hello;}}}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e")));
+ assert_non_null(node = mod->compiled->data);
+ assert_null(((struct lysc_node_choice *)node)->dflt);
+ assert_non_null(node = node->next);
+ assert_non_null(((struct lysc_node_choice *)node)->dflt);
+ assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name);
+ assert_non_null(leaf = (struct lysc_node_leaf *)node->next);
+ assert_non_null(leaf->dflt);
+ assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module i {namespace urn:i;prefix i;"
+ "list l1 {key a; leaf a {type string;} leaf b {type string;} leaf c {type string;}}"
+ "list l2 {key a; unique \"b c\"; unique \"d\"; leaf a {type string;} leaf b {type string;}"
+ " leaf c {type string;} leaf d {type string;}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;import i {prefix i;}"
+ "augment /i:l1 {leaf j_c {type string;}}"
+ "deviation /i:l1 {deviate add {unique \"b j:j_c\"; }}"
+ "deviation /i:l1 {deviate add {unique \"i:c\";}}"
+ "deviation /i:l2 {deviate delete {unique \"d\"; unique \"b c\";}}}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "i")));
+ assert_non_null(list = (struct lysc_node_list *)mod->compiled->data);
+ assert_string_equal("l1", list->name);
+ assert_int_equal(2, LY_ARRAY_COUNT(list->uniques));
+ assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[0]));
+ assert_string_equal("b", list->uniques[0][0]->name);
+ assert_string_equal("j_c", list->uniques[0][1]->name);
+ assert_int_equal(1, LY_ARRAY_COUNT(list->uniques[1]));
+ assert_string_equal("c", list->uniques[1][0]->name);
+ assert_non_null(list = (struct lysc_node_list *)list->next);
+ assert_string_equal("l2", list->name);
+ assert_null(list->uniques);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k; leaf a {type string;}"
+ "container top {leaf x {type string;} leaf y {type string; config false;}}"
+ "deviation /a {deviate add {config false; }}"
+ "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_true(node->flags & LYS_CONFIG_R);
+ assert_non_null(node = node->next);
+ assert_string_equal("top", node->name);
+ assert_true(node->flags & LYS_CONFIG_R);
+ assert_non_null(node = lysc_node_child(node));
+ assert_string_equal("x", node->name);
+ assert_true(node->flags & LYS_CONFIG_R);
+ assert_non_null(node = node->next);
+ assert_string_equal("y", node->name);
+ assert_true(node->flags & LYS_CONFIG_R);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l; leaf a {config false; type string;}"
+ "container top {config false; leaf x {type string;}}"
+ "deviation /a {deviate replace {config true;}}"
+ "deviation /top {deviate replace {config true;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_true(node->flags & LYS_CONFIG_W);
+ assert_non_null(node = node->next);
+ assert_string_equal("top", node->name);
+ assert_true(node->flags & LYS_CONFIG_W);
+ assert_non_null(node = lysc_node_child(node));
+ assert_string_equal("x", node->name);
+ assert_true(node->flags & LYS_CONFIG_W);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;"
+ "container a {leaf a {type string;}}"
+ "container b {leaf b {mandatory true; type string;}}"
+ "deviation /a/a {deviate add {mandatory true;}}"
+ "deviation /b/b {deviate replace {mandatory false;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+ assert_true((lysc_node_child(node)->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+ assert_non_null(node = node->next);
+ assert_string_equal("b", node->name);
+ assert_false(node->flags & LYS_MAND_MASK); /* just unset on container */
+ assert_true((lysc_node_child(node)->flags & LYS_MAND_MASK) == LYS_MAND_FALSE);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module n {yang-version 1.1; namespace urn:n;prefix n;"
+ "leaf a {default test; type string;}"
+ "leaf b {mandatory true; type string;}"
+ "deviation /a {deviate add {mandatory true;} deviate delete {default test;}}"
+ "deviation /b {deviate add {default test;} deviate replace {mandatory false;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_null(((struct lysc_node_leaf *)node)->dflt);
+ assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+ assert_non_null(node = node->next);
+ assert_string_equal("b", node->name);
+ assert_non_null(((struct lysc_node_leaf *)node)->dflt);
+ assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_FALSE);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module o {namespace urn:o;prefix o;"
+ "leaf-list a {type string;}"
+ "list b {config false;}"
+ "leaf-list c {min-elements 1; max-elements 10; type string;}"
+ "list d {min-elements 10; max-elements 100; config false;}"
+ "deviation /a {deviate add {min-elements 1; max-elements 10;}}"
+ "deviation /b {deviate add {min-elements 10; max-elements 100;}}"
+ "deviation /c {deviate replace {min-elements 10; max-elements 100;}}"
+ "deviation /d {deviate replace {min-elements 1; max-elements 10;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_int_equal(1, ((struct lysc_node_leaflist *)node)->min);
+ assert_int_equal(10, ((struct lysc_node_leaflist *)node)->max);
+ assert_non_null(node = node->next);
+ assert_string_equal("b", node->name);
+ assert_int_equal(10, ((struct lysc_node_list *)node)->min);
+ assert_int_equal(100, ((struct lysc_node_list *)node)->max);
+ assert_non_null(node = node->next);
+ assert_string_equal("c", node->name);
+ assert_int_equal(10, ((struct lysc_node_leaflist *)node)->min);
+ assert_int_equal(100, ((struct lysc_node_leaflist *)node)->max);
+ assert_non_null(node = node->next);
+ assert_string_equal("d", node->name);
+ assert_int_equal(1, ((struct lysc_node_list *)node)->min);
+ assert_int_equal(10, ((struct lysc_node_list *)node)->max);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module p {yang-version 1.1; namespace urn:p;prefix p; typedef mytype {type int8; default 1;}"
+ "leaf a {type string; default 10;} leaf-list b {type string;}"
+ "deviation /a {deviate replace {type mytype;}}"
+ "deviation /b {deviate replace {type mytype;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("a", leaf->name);
+ assert_int_equal(LY_TYPE_INT8, leaf->type->basetype);
+ assert_string_equal("10", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(10, leaf->dflt->uint8);
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_string_equal("b", llist->name);
+ assert_int_equal(LY_TYPE_INT8, llist->type->basetype);
+ assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts));
+ assert_string_equal("1", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
+ assert_int_equal(0, dynamic);
+ assert_int_equal(1, llist->dflts[0]->uint8);
+
+ /* instance-identifiers with NULL canonical are changed to string types with a canonical value equal to the original value */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module q {yang-version 1.1; namespace urn:q;prefix q; import e {prefix e;}"
+ "leaf q {type instance-identifier; default \"/e:d2[.='a']\";}"
+ "leaf-list ql {type instance-identifier; default \"/e:d[.='b']\"; default \"/e:d2[.='c']\";}}", LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module qdev {yang-version 1.1; namespace urn:qdev;prefix qd; import q {prefix q;}"
+ "deviation /q:q { deviate replace {type string;}}"
+ "deviation /q:ql { deviate replace {type string;}}}", LYS_IN_YANG, NULL));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_int_equal(LY_TYPE_STRING, leaf->dflt->realtype->basetype);
+ assert_non_null(leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_CANON, NULL, NULL, NULL));
+ assert_string_equal("/e:d2[.='a']", leaf->dflt->_canonical);
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts));
+ assert_int_equal(LY_TYPE_STRING, llist->dflts[0]->realtype->basetype);
+ assert_string_equal("/e:d[.='b']", llist->dflts[0]->_canonical);
+ assert_int_equal(LY_TYPE_STRING, llist->dflts[0]->realtype->basetype);
+ assert_string_equal("/e:d2[.='c']", llist->dflts[1]->_canonical);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module r {yang-version 1.1; namespace urn:r;prefix r;"
+ "typedef mytype {type uint8; default 200;}"
+ "leaf r {type mytype;} leaf-list lr {type mytype;}"
+ "deviation /r:r {deviate replace {type string;}}"
+ "deviation /r:lr {deviate replace {type string;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("r", leaf->name);
+ assert_null(leaf->dflt);
+ assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
+ assert_string_equal("lr", llist->name);
+ assert_null(llist->dflts);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module s {yang-version 1.1; namespace urn:s;prefix s;"
+ "leaf s {type instance-identifier {require-instance true;} default /s:x;}"
+ "leaf x {type string;} leaf y {type string;}"
+ "deviation /s:s {deviate replace {default /s:y;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("s", leaf->name);
+ assert_non_null(leaf->dflt);
+ assert_non_null(str = leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, mod->parsed, &dynamic, NULL));
+ assert_string_equal("/s:y", str);
+ if (dynamic) {
+ free((char *)str);
+ }
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module t {namespace urn:t;prefix t;"
+ "leaf l {type string;}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module u {namespace urn:u;prefix u;import t {prefix t;}"
+ "identity ident;"
+ "deviation /t:l {deviate replace {type identityref {base ident;}}}"
+ "}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "t")));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("l", leaf->name);
+ assert_int_equal(LY_TYPE_IDENT, leaf->type->basetype);
+ assert_string_equal("ident", ((struct lysc_type_identityref *)leaf->type)->bases[0]->name);
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module v {namespace urn:v;prefix v;"
+ "identity ident; identity ident2 { base ident; }"
+ "}", LYS_IN_YANG, NULL));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule w-sub { belongs-to w { prefix w; }"
+ "import v { prefix v_pref; }"
+ "leaf l { type string; default \"v_pref:ident2\"; }"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module w {namespace urn:w;prefix w;"
+ "include w-sub;"
+ "}", LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module x {namespace urn:x;prefix x;"
+ "import w { prefix w_pref; } import v { prefix v_pref; }"
+ "deviation /w_pref:l { deviate replace { type identityref { base v_pref:ident; } } }"
+ "}", LYS_IN_YANG, NULL));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("l", leaf->name);
+ assert_int_equal(LY_TYPE_IDENT, leaf->type->basetype);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module y {namespace urn:y;prefix y;"
+ "leaf l1 {type string;}"
+ "leaf l2 {type string;}"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module z {namespace urn:z;prefix z;"
+ "import y {prefix y;}"
+ "deviation \"/y:l1\" {deviate replace {type leafref {path \"/l2\";}}}"
+ "deviation \"/y:l2\" {deviate replace {type leafref {path \"/z:al2\";}}}"
+ "leaf al2 {type string;}"
+ "}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "y")));
+ assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
+ assert_string_equal("l1", leaf->name);
+ assert_int_equal(LY_TYPE_LEAFREF, leaf->type->basetype);
+ leaf = (struct lysc_node_leaf *)leaf->next;
+ assert_string_equal("l2", leaf->name);
+ assert_int_equal(LY_TYPE_LEAFREF, leaf->type->basetype);
+
+ /* complex dependencies */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m-base {namespace urn:m-base;prefix mb;"
+ "container cont {leaf l {type string;} leaf l2 {type string;}}}", LYS_IN_YANG, NULL));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module m-base-aug {namespace urn:m-base-aug;prefix mba;"
+ "import m-base {prefix mb;}"
+ "augment /mb:cont {leaf l {type string;} leaf l2 {type string;}}"
+ "container cont2 {leaf l {type string;}}}"
+ "\n"
+ "module m-base-aug2 {namespace urn:m-base-aug2;prefix mba2;"
+ "import m-base {prefix mb;} import m-base-aug {prefix mba;}"
+ "augment /mb:cont {leaf augl1 {type string;}}"
+ "augment /mba:cont2 {leaf augl2 {type string;}}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m-dev {namespace urn:m-dev;prefix md;"
+ "import m-base-aug {prefix mba;} import m-base-aug2 {prefix mba2;}"
+ "deviation /mba:cont2/mba2:augl2 {deviate not-supported;}}", LYS_IN_YANG, NULL));
+ assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "m-base-aug")));
+ node = mod->compiled->data;
+ assert_string_equal(node->name, "cont2");
+ assert_non_null(node = lysc_node_child(node));
+ assert_string_equal(node->name, "l");
+ assert_null(node->next);
+
+ /* default identity referencing deprecated */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a1-imp {namespace urn:a1-imp;prefix a1i;"
+ "identity id-base;"
+ "identity id1 {base id-base; status deprecated;}"
+ "leaf l {type identityref {base \"id-base\";}}"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a1 {namespace urn:a1;prefix a1;"
+ "import a1-imp {prefix a1i;}"
+ "deviation \"/a1i:l\" {deviate add {default \"a1i:id1\";}}"
+ "}", LYS_IN_YANG, NULL));
+
+ /* default instance-identifier referencing deprecated */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a2-imp {namespace urn:a2-imp;prefix a2i;"
+ "leaf l {type instance-identifier;}"
+ "leaf k {type string; status deprecated;}"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a2 {namespace urn:a2;prefix a2;"
+ "import a2-imp {prefix a2i;}"
+ "deviation \"/a2i:l\" {deviate add {default \"/a2i:k\";}}"
+ "}", LYS_IN_YANG, NULL));
+
+ /* must referencing deprecated */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a3-imp {namespace urn:a3-imp;prefix a3i;"
+ "leaf l {type string;}"
+ "leaf k {type string; status deprecated;}"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a3 {namespace urn:a3;prefix a3;"
+ "import a3-imp {prefix a3i;}"
+ "deviation \"/a3i:l\" {deviate add {must \"string-length(/a3i:k) > 0\";}}"
+ "}", LYS_IN_YANG, NULL));
+
+ /* type leafref referencing deprecated */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a4-imp {namespace urn:a4-imp;prefix a4i;"
+ "leaf l {type string;}"
+ "leaf k {type string; status deprecated;}"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a4 {namespace urn:a4;prefix a4;"
+ "import a4-imp {prefix a4i;}"
+ "deviation \"/a4i:l\" {deviate replace {type leafref {path \"/a4i:k\";}}}"
+ "}", LYS_IN_YANG, NULL));
+
+ /* unique referencing deprecated */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a5-imp {namespace urn:a5-imp;prefix a5i;"
+ "list l1 {key \"k\";"
+ " leaf k {type string;}"
+ " leaf l {type string; status deprecated;}"
+ "}}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a5 {namespace urn:a5;prefix a5;"
+ "import a5-imp {prefix a5i;}"
+ "deviation \"/a5i:l1\" {deviate add {unique \"a5i:l\";}}"
+ "}", LYS_IN_YANG, NULL));
+
+ assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa1 {namespace urn:aa1;prefix aa1;import a {prefix a;}"
+ "deviation /a:top/a:z {deviate not-supported;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Deviation(s) target node \"/a:top/a:z\" from module \"aa1\" was not found.", "/a:{deviation='/a:top/a:z'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa2;import a {prefix a;}"
+ "deviation /a:top/a:a {deviate not-supported;}"
+ "deviation /a:top/a:a {deviate add {default error;}}}", LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Multiple deviations of \"/a:top/a:a\" with one of them being \"not-supported\".", "/aa2:{deviation='/a:top/a:a'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import a {prefix a;}"
+ "deviation a:top/a:a {deviate not-supported;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"a:top/a:a\" - \"/\" expected instead of \"a:top\".", "/bb:{deviation='a:top/a:a'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c;"
+ "deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"units\" property.", "/cc:{deviation='/c'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cd {namespace urn:cd;prefix cd; leaf c {type string; units centimeters;}"
+ "deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"units\" property which already exists (with value \"centimeters\").", "/cd:{deviation='/c'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd1 {namespace urn:dd1;prefix dd1; container c;"
+ "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to delete \"units\" property.", "/dd1:{deviation='/c'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd2 {namespace urn:dd2;prefix dd2; leaf c {type string;}"
+ "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which is not present.", "/dd2:{deviation='/c'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd3 {namespace urn:dd3;prefix dd3; leaf c {type string; units centimeters;}"
+ "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which does not match the target's property value \"centimeters\".",
+ "/dd3:{deviation='/c'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee1 {namespace urn:ee1;prefix ee1; container c;"
+ "deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"units\" property.", "/ee1:{deviation='/c'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee2 {namespace urn:ee2;prefix ee2; leaf c {type string;}"
+ "deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"units\" property \"meters\" which is not present.", "/ee2:{deviation='/c'}");
+
+ /* the default is already deleted in /e:a byt module f */
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff1 {namespace urn:ff1;prefix ff1; import e {prefix e;}"
+ "deviation /e:a {deviate delete {default x:aa;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"x:aa\" which is not present.", "/ff1:{deviation='/e:a'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff3 {namespace urn:ff3;prefix ff3; import e {prefix e;}"
+ "deviation /e:b {deviate delete {default e:b;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"e:b\" which does not match the target's property value \"x:ba\".", "/ff3:{deviation='/e:b'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff5 {namespace urn:ff5;prefix ff5; anyxml a;"
+ "deviation /a {deviate delete {default x;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to delete \"default\" property.", "/ff5:{deviation='/a'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff6 {namespace urn:ff6;prefix ff6; import e {prefix e;}"
+ "deviation /e:c {deviate delete {default hi;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match the target's property value \"hello\".", "/ff6:{deviation='/e:c'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff7 {namespace urn:ff7;prefix ff7; import e {prefix e;}"
+ "deviation /e:d {deviate delete {default hi;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match any of the target's property values.", "/ff7:{deviation='/e:d'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg1 {namespace urn:gg1;prefix gg1; import e {prefix e;}"
+ "deviation /e:b {deviate add {default e:a;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"x:ba\").", "/gg1:{deviation='/e:b'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg2 {namespace urn:gg2;prefix gg2; import e {prefix e;}"
+ "deviation /e:a {deviate add {default x:a;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a",
+ "Default case prefix \"x\" not found in imports of \"gg2\".", "/e:a");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg3 {namespace urn:gg3;prefix gg3; import e {prefix e;}"
+ "deviation /e:a {deviate add {default a;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a",
+ "Default case \"a\" not found.", "/e:a");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}"
+ "deviation /e:c {deviate add {default hi;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"hello\").", "/gg4:{deviation='/e:c'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}"
+ "deviation /e:a {deviate add {default e:ac;}}}", LYS_IN_YANG, &mod));
+ /*CHECK_LOG_CTX("Invalid deviation adding \"default\" property \"e:ac\" of choice - mandatory node \"ac\" under the default case.", "/gg4:{deviation='/e:a'}");*/
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg5 {namespace urn:gg5;prefix gg5; leaf x {type string; mandatory true;}"
+ "deviation /x {deviate add {default error;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg5:{deviation='/x'}",
+ "Invalid mandatory leaf with a default value.", "/gg5:{deviation='/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh1 {yang-version 1.1; namespace urn:hh1;prefix hh1; import e {prefix e;}"
+ "deviation /e:d {deviate replace {default hi;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of leaf-list node - it is not possible to replace \"default\" property.", "/hh1:{deviation='/e:d'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii1 {namespace urn:ii1;prefix ii1; import i {prefix i;}"
+ "deviation /i:l1 {deviate delete {unique x;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"x\" which does not match any of the target's property values.", "/ii1:{deviation='/i:l1'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii2 {namespace urn:ii2;prefix ii2; import i {prefix i;} leaf x { type string;}"
+ "deviation /i:l2 {deviate delete {unique d;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"d\" which does not match any of the target's property values.", "/ii2:{deviation='/i:l2'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii3 {namespace urn:ii3;prefix ii3; leaf x { type string;}"
+ "deviation /x {deviate delete {unique d;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to delete \"unique\" property.", "/ii3:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii4 {namespace urn:ii4;prefix ii4; leaf x { type string;}"
+ "deviation /x {deviate add {unique d;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to add \"unique\" property.", "/ii4:{deviation='/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj1 {namespace urn:jj1;prefix jj1; choice ch {case a {leaf a{type string;}}}"
+ "deviation /ch/a {deviate add {config false;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to add \"config\" property.", "/jj1:{deviation='/ch/a'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj2 {namespace urn:jj2;prefix jj2; container top {config false; leaf x {type string;}}"
+ "deviation /top/x {deviate add {config true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj2:{deviation='/top/x'}",
+ "Configuration node cannot be child of any state data node.", "/jj2:{deviation='/top/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj3 {namespace urn:jj3;prefix jj3; container top {leaf x {type string;}}"
+ "deviation /top/x {deviate replace {config false;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"config\" property \"config false\" which is not present.", "/jj3:{deviation='/top/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj4 {namespace urn:jj4;prefix jj4; choice ch {case a {leaf a{type string;}}}"
+ "deviation /ch/a {deviate replace {config false;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to replace \"config\" property.", "/jj4:{deviation='/ch/a'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}"
+ "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj5:top",
+ "Configuration node cannot be child of any state data node.", "/jj5:top/x");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}"
+ "deviation /x {deviate add {config true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"config\" property which already exists (with value \"config false\").", "/jj6:{deviation='/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk1 {namespace urn:kk1;prefix kk1; container top {leaf a{type string;}}"
+ "deviation /top {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"mandatory\" property.", "/kk1:{deviation='/top'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk2 {namespace urn:kk2;prefix kk2; container top {leaf a{type string;}}"
+ "deviation /top {deviate replace {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"mandatory\" property.", "/kk2:{deviation='/top'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk3 {namespace urn:kk3;prefix kk3; container top {leaf x {type string;}}"
+ "deviation /top/x {deviate replace {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"mandatory\" property \"mandatory true\" which is not present.", "/kk3:{deviation='/top/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk4 {namespace urn:kk4;prefix kk4; leaf x {mandatory true; type string;}"
+ "deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory true\").", "/kk4:{deviation='/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll1 {namespace urn:ll1;prefix ll1; leaf x {default test; type string;}"
+ "deviation /x {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll1:{deviation='/x'}",
+ "Invalid mandatory leaf with a default value.", "/ll1:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {yang-version 1.1; namespace urn:ll2;prefix ll2; leaf-list x {default test; type string;}"
+ "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:{deviation='/x'}",
+ "The default statement is present on leaf-list with a nonzero min-elements.", "/ll2:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {namespace urn:ll2;prefix ll2; choice ch {default a; leaf a {type string;} leaf b {type string;}}"
+ "deviation /ch {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:ch",
+ "Invalid mandatory choice with a default case.", "/ll2:ch");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm1 {namespace urn:mm1;prefix mm1; leaf-list x {min-elements 10; type string;}"
+ "deviation /x {deviate add {max-elements 5;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm1:{deviation='/x'}",
+ "Leaf-list min-elements 10 is bigger than max-elements 5.", "/mm1:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm2 {namespace urn:mm2;prefix mm2; leaf-list x {max-elements 10; type string;}"
+ "deviation /x {deviate add {min-elements 20;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm2:{deviation='/x'}",
+ "Leaf-list min-elements 20 is bigger than max-elements 10.", "/mm2:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm3 {namespace urn:mm3;prefix mm3; list x {min-elements 5; max-elements 10; config false;}"
+ "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm3:{deviation='/x'}",
+ "List min-elements 5 is bigger than max-elements 1.", "/mm3:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm4 {namespace urn:mm4;prefix mm4; list x {min-elements 5; max-elements 10; config false;}"
+ "deviation /x {deviate replace {min-elements 20;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm4:{deviation='/x'}",
+ "List min-elements 20 is bigger than max-elements 10.", "/mm4:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm5 {namespace urn:mm5;prefix mm5; leaf-list x {type string; min-elements 5;}"
+ "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", "/mm5:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm6 {namespace urn:mm6;prefix mm6; list x {config false; min-elements 5;}"
+ "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", "/mm6:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm7 {namespace urn:mm7;prefix mm7; leaf-list x {type string; max-elements 5;}"
+ "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", "/mm7:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm8 {namespace urn:mm8;prefix mm8; list x {config false; max-elements 5;}"
+ "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", "/mm8:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm9 {namespace urn:mm9;prefix mm9; leaf-list x {type string;}"
+ "deviation /x {deviate replace {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"min-elements\" property which is not present.", "/mm9:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm10 {namespace urn:mm10;prefix mm10; list x {config false;}"
+ "deviation /x {deviate replace {min-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"min-elements\" property which is not present.", "/mm10:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm11 {namespace urn:mm11;prefix mm11; leaf-list x {type string;}"
+ "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"max-elements\" property which is not present.", "/mm11:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm12 {namespace urn:mm12;prefix mm12; list x {config false; }"
+ "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation replacing \"max-elements\" property which is not present.", "/mm12:{deviation='/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn1 {namespace urn:nn1;prefix nn1; anyxml x;"
+ "deviation /x {deviate replace {type string;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to replace \"type\" property.", "/nn1:{deviation='/x'}");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn2 {namespace urn:nn2;prefix nn2; leaf-list x {type string;}"
+ "deviation /x {deviate replace {type empty;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/nn2:{deviation='/x'}",
+ "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/nn2:{deviation='/x'}");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo1 {namespace urn:oo1;prefix oo1; leaf x {type uint16; default 300;}"
+ "deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type "
+ "(Value \"300\" is out of type uint8 min/max bounds.).", "Schema location \"/oo1:x\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo2 {yang-version 1.1;namespace urn:oo2;prefix oo2; leaf-list x {type uint16; default 10; default 300;}"
+ "deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type "
+ "(Value \"300\" is out of type uint8 min/max bounds.).", "Schema location \"/oo2:x\".");
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo3 {namespace urn:oo3;prefix oo3; leaf x {type uint8;}"
+ "deviation /x {deviate add {default 300;}}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Invalid default - value does not fit the type "
+ "(Value \"300\" is out of type uint8 min/max bounds.).", "Schema location \"/oo3:x\".");
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp; leaf l { type leafref {path /c/x;}}"
+ "container c {leaf x {type string;} leaf y {type string;}}}", LYS_IN_YANG, &mod));
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp1 {namespace urn:pp1;prefix pp1; import pp {prefix pp;}"
+ "deviation /pp:c/pp:x {deviate not-supported;}}", LYS_IN_YANG, &mod));
+ CHECK_LOG_CTX("Target of leafref \"l\" cannot be referenced because it is disabled.", "Schema location \"/pp:l\".");
+}
+
+static void
+test_when(void **state)
+{
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX,
+ "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " container cont {\n"
+ " leaf l {\n"
+ " when \"/cont/lst[val='25']\";\n"
+ " type empty;\n"
+ " }\n"
+ " list lst {\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf val {\n"
+ " when /cont2;\n"
+ " type int32;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container cont2 {\n"
+ " presence \"a\";\n"
+ " when ../cont/l;\n"
+ " }\n"
+ "}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition cyclic dependency on the node \"cont2\".", "Schema location \"/a:cont/lst/val\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX,
+ "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " container cont {\n"
+ " leaf l {\n"
+ " when \"/cont/lst[val='25']\";\n"
+ " type empty;\n"
+ " }\n"
+ " list lst {\n"
+ " key \"k\";\n"
+ " leaf k {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf val {\n"
+ " when /cont2;\n"
+ " type int32;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container cont2 {\n"
+ " presence \"a\";\n"
+ " when ../cont/lst/val;\n"
+ " }\n"
+ "}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition cyclic dependency on the node \"cont2\".", "Schema location \"/a:cont/lst/val\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX,
+ "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " leaf val {\n"
+ " type int64;\n"
+ " when \"../val='25'\";\n"
+ " }\n"
+ "}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "Schema location \"/a:val\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX,
+ "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " grouping grp {\n"
+ " leaf val {\n"
+ " type int64;\n"
+ " }\n"
+ " }\n"
+ " uses grp {\n"
+ " when \"val='25'\";\n"
+ " }\n"
+ "}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "Schema location \"/a:val\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX,
+ "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " augment /cont {\n"
+ " when \"val='25'\";\n"
+ " leaf val {\n"
+ " type int64;\n"
+ " }\n"
+ " }\n"
+ " container cont;\n"
+ "}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "Schema location \"/a:cont/val\".");
+
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX,
+ "module a {\n"
+ " namespace urn:a;\n"
+ " prefix a;\n"
+ " augment /cont {\n"
+ " when \"aug-cont/aug-l\";\n"
+ " container aug-cont {\n"
+ " leaf aug-l {\n"
+ " type int64;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container cont;\n"
+ "}",
+ LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("When condition is accessing its own conditional node children.", "Schema location \"/a:cont/aug-cont\".");
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX,
+ "module b {\n"
+ " namespace urn:b;\n"
+ " prefix b;\n"
+ " container c {\n"
+ " list l {\n"
+ " key name;\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " container cond-data {\n"
+ " when \"/c/l2[name = current()/../name]/val = 'value'\";\n"
+ " leaf cond-leaf {\n"
+ " type string;\n"
+ " default \"default_val\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " list l2 {\n"
+ " key name;\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " container c2 {\n"
+ " leaf val {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}",
+ LYS_IN_YANG, NULL));
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
+ "module c1 {"
+ " namespace urn:c1;"
+ " prefix c1;"
+ " container my-container {"
+ " leaf my-type {"
+ " type string;"
+ " }"
+ " }"
+ "}\n"
+ "module c2 {"
+ " namespace \"urn:c2\";"
+ " prefix c2;"
+ " grouping my-group {"
+ " leaf my-leaf {"
+ " type string;"
+ " }"
+ " }"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX,
+ "module c3 {"
+ " namespace \"urn:c3\";"
+ " prefix c3;"
+ " import c1 {"
+ " prefix c1;"
+ " }"
+ " import c2 {"
+ " prefix c2;"
+ " }"
+ " augment \"/c1:my-container\" {"
+ " uses c2:my-group {"
+ " when \"./c1:my-type = '42'\";"
+ " }"
+ " }"
+ "}",
+ LYS_IN_YANG, NULL));
+}
+
+static void
+test_must(void **state)
+{
+ /* "*" must not be restricted to any module */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
+ "module a {"
+ " namespace urn:a;"
+ " prefix a;"
+ " container cont {"
+ " leaf l {"
+ " type empty;"
+ " }"
+ " list lst {"
+ " key \"k\";"
+ " leaf k {"
+ " type uint8;"
+ " }"
+ " }"
+ " }"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX,
+ "module a-aug {"
+ " namespace urn:aa;"
+ " prefix aa;"
+ " import a {"
+ " prefix a;"
+ " }"
+ " augment /a:cont {"
+ " container cont2 {"
+ " must \"/a:cont/*/a:k\";"
+ " leaf aug {"
+ " type empty;"
+ " }"
+ " }"
+ " }"
+ "}",
+ LYS_IN_YANG, NULL));
+ /* no warnings */
+ CHECK_LOG_CTX(NULL, NULL);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_module, setup),
+ UTEST(test_name_collisions, setup),
+ UTEST(test_type_length, setup),
+ UTEST(test_type_range, setup),
+ UTEST(test_type_pattern, setup),
+ UTEST(test_type_enum, setup),
+ UTEST(test_type_dec64, setup),
+ UTEST(test_type_instanceid, setup),
+ UTEST(test_identity, setup),
+ UTEST(test_type_identityref, setup),
+ UTEST(test_type_leafref, setup),
+ UTEST(test_type_empty, setup),
+ UTEST(test_type_union, setup),
+ UTEST(test_type_dflt, setup),
+ UTEST(test_status, setup),
+ UTEST(test_node_container, setup),
+ UTEST(test_node_leaflist, setup),
+ UTEST(test_node_list, setup),
+ UTEST(test_node_choice, setup),
+ UTEST(test_node_anydata, setup),
+ UTEST(test_action, setup),
+ UTEST(test_notification, setup),
+ UTEST(test_grouping, setup),
+ UTEST(test_uses, setup),
+ UTEST(test_refine, setup),
+ UTEST(test_augment, setup),
+ UTEST(test_deviation, setup),
+ UTEST(test_when, setup),
+ UTEST(test_must, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/schema/test_yang.c b/tests/utests/schema/test_yang.c
new file mode 100644
index 0000000..78b1798
--- /dev/null
+++ b/tests/utests/schema/test_yang.c
@@ -0,0 +1,1750 @@
+/**
+ * @file test_yang.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for YANG module parser and printer
+ *
+ * Copyright (c) 2018 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "in_internal.h"
+#include "parser_internal.h"
+#include "schema_compile.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_free.h"
+
+/* originally static functions from parser_yang.c and parser_yin.c */
+LY_ERR buf_add_char(struct ly_ctx *ctx, struct ly_in *in, size_t len, char **buf, size_t *buf_len, size_t *buf_used);
+LY_ERR buf_store_char(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p,
+ size_t *word_len, char **word_b, size_t *buf_len, uint8_t need_buf, uint8_t *prefix);
+LY_ERR get_keyword(struct lysp_yang_ctx *ctx, enum ly_stmt *kw, char **word_p, size_t *word_len);
+LY_ERR get_argument(struct lysp_yang_ctx *ctx, enum yang_arg arg,
+ uint16_t *flags, char **word_p, char **word_b, size_t *word_len);
+LY_ERR skip_comment(struct lysp_yang_ctx *ctx, uint8_t comment);
+
+LY_ERR parse_action(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_action **actions);
+LY_ERR parse_any(struct lysp_yang_ctx *ctx, enum ly_stmt kw, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_augment(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_augment **augments);
+LY_ERR parse_case(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_container(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_deviate(struct lysp_yang_ctx *ctx, struct lysp_deviate **deviates);
+LY_ERR parse_deviation(struct lysp_yang_ctx *ctx, struct lysp_deviation **deviations);
+LY_ERR parse_grouping(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_grp **groupings);
+LY_ERR parse_choice(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_leaf(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_leaflist(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_list(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts);
+LY_ERR parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts);
+LY_ERR parse_module(struct lysp_yang_ctx *ctx, struct lysp_module *mod);
+LY_ERR parse_notif(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_notif **notifs);
+LY_ERR parse_submodule(struct lysp_yang_ctx *ctx, struct lysp_submodule *submod);
+LY_ERR parse_uses(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_when(struct lysp_yang_ctx *ctx, struct lysp_when **when_p);
+LY_ERR parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct lysp_type_enum *enm);
+
+struct lysp_yang_ctx *YCTX;
+struct lysf_ctx fctx;
+
+struct ly_in in = {0};
+
+#define YCTX_INIT \
+ in.line = 1; \
+ YCTX->in = &in; \
+ LOG_LOCINIT(UTEST_LYCTX, NULL, NULL, NULL, &in)
+
+static int
+setup(void **state)
+{
+ struct lysp_module *pmod;
+
+ UTEST_SETUP;
+
+ /* allocate parser context */
+ YCTX = calloc(1, sizeof(*YCTX));
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+ YCTX->format = LYS_IN_YANG;
+ ly_set_new(&YCTX->parsed_mods);
+
+ /* allocate new parsed module */
+ pmod = calloc(1, sizeof *pmod);
+ ly_set_add(YCTX->parsed_mods, pmod, 1, NULL);
+
+ /* allocate new module */
+ pmod->mod = calloc(1, sizeof *pmod->mod);
+ pmod->mod->ctx = UTEST_LYCTX;
+ pmod->mod->parsed = pmod;
+
+ /* initilize and use the global easily available and customizable input handler */
+ in.line = 1;
+ YCTX->in = &in;
+ LOG_LOCSET(NULL, NULL, NULL, &in);
+
+ fctx.ctx = PARSER_CTX(YCTX);
+ fctx.mod = pmod->mod;
+
+ return 0;
+}
+
+static int
+teardown(void **state)
+{
+ lys_module_free(&fctx, PARSER_CUR_PMOD(YCTX)->mod, 0);
+ LOG_LOCBACK(0, 0, 0, 1);
+
+ ly_set_free(YCTX->parsed_mods, NULL);
+ ly_set_erase(&YCTX->ext_inst, NULL);
+ free(YCTX);
+ YCTX = NULL;
+
+ lysf_ctx_erase(&fctx);
+
+ UTEST_TEARDOWN;
+
+ return 0;
+}
+
+#define TEST_DUP_GENERIC(PREFIX, MEMBER, VALUE1, VALUE2, FUNC, RESULT, LINE, CLEANUP) \
+ in.current = PREFIX MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, FUNC(YCTX, RESULT)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number "LINE".");\
+ CLEANUP
+static void
+test_helpers(void **state)
+{
+ char *buf, *p;
+ size_t len, size;
+ uint8_t prefix = 0;
+
+ /* storing into buffer */
+ in.current = "abcd";
+ buf = NULL;
+ size = len = 0;
+ assert_int_equal(LY_SUCCESS, buf_add_char(NULL, &in, 2, &buf, &size, &len));
+ assert_int_not_equal(0, size);
+ assert_int_equal(2, len);
+ assert_string_equal("cd", in.current);
+ assert_false(strncmp("ab", buf, 2));
+ free(buf);
+ buf = NULL;
+
+ /* invalid first characters */
+ len = 0;
+ in.current = "2invalid";
+ assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ in.current = ".invalid";
+ assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ in.current = "-invalid";
+ assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ /* invalid following characters */
+ len = 3; /* number of characters read before the str content */
+ in.current = "!";
+ assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ in.current = ":";
+ assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ /* valid colon for prefixed identifiers */
+ len = size = 0;
+ p = NULL;
+ prefix = 0;
+ in.current = "x:id";
+ assert_int_equal(LY_SUCCESS, buf_store_char(YCTX, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 0, &prefix));
+ assert_int_equal(1, len);
+ assert_null(buf);
+ assert_string_equal(":id", in.current);
+ assert_int_equal('x', p[len - 1]);
+ assert_int_equal(LY_SUCCESS, buf_store_char(YCTX, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ assert_int_equal(2, len);
+ assert_string_equal("id", in.current);
+ assert_int_equal(':', p[len - 1]);
+ free(buf);
+ prefix = 0;
+
+ /* checking identifiers */
+ assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, NULL));
+ CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", "Line number 1.");
+ assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, '#', 1, NULL));
+ CHECK_LOG_CTX("Invalid identifier first character '#' (0x0023).", "Line number 1.");
+
+ assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'a', 1, &prefix));
+ assert_int_equal(0, prefix);
+ assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix));
+ assert_int_equal(1, prefix);
+ assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix));
+ assert_int_equal(1, prefix);
+ assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'b', 0, &prefix));
+ assert_int_equal(2, prefix);
+ /* second colon is invalid */
+ assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix));
+ CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", "Line number 1.");
+}
+
+#define TEST_GET_ARGUMENT_SUCCESS(INPUT_TEXT, CTX, ARG_TYPE, EXPECT_WORD, EXPECT_LEN, EXPECT_CURRENT, EXPECT_LINE)\
+ {\
+ const char * text = INPUT_TEXT;\
+ in.line = 1;\
+ in.current = text;\
+ assert_int_equal(LY_SUCCESS, get_argument(CTX, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len));\
+ assert_string_equal(word, EXPECT_WORD);\
+ assert_int_equal(len, EXPECT_LEN);\
+ assert_string_equal(EXPECT_CURRENT, in.current);\
+ assert_int_equal(EXPECT_LINE, in.line);\
+ }
+
+static void
+test_comments(void **state)
+{
+ char *word, *buf;
+ size_t len;
+
+ TEST_GET_ARGUMENT_SUCCESS(" // this is a text of / one * line */ comment\nargument;",
+ YCTX, Y_STR_ARG, "argument;", 8, ";", 2);
+ assert_null(buf);
+
+ TEST_GET_ARGUMENT_SUCCESS("/* this is a \n * text // of / block * comment */\"arg\" + \"ume\" \n + \n \"nt\";",
+ YCTX, Y_STR_ARG, "argument", 8, ";", 4);
+ assert_ptr_equal(buf, word);
+ free(word);
+
+ in.line = 1;
+ in.current = " this is one line comment on last line";
+ assert_int_equal(LY_SUCCESS, skip_comment(YCTX, 1));
+ assert_true(in.current[0] == '\0');
+
+ in.line = 1;
+ in.current = " this is a not terminated comment x";
+ assert_int_equal(LY_EVALID, skip_comment(YCTX, 2));
+ CHECK_LOG_CTX("Unexpected end-of-input, non-terminated comment.", "Line number 1.");
+ assert_true(in.current[0] == '\0');
+}
+
+static void
+test_arg(void **state)
+{
+ char *word, *buf;
+ size_t len;
+
+ /* missing argument */
+ in.current = ";";
+ assert_int_equal(LY_SUCCESS, get_argument(YCTX, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len));
+ assert_null(word);
+
+ in.current = "{";
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Invalid character sequence \"{\", expected an argument.", "Line number 1.");
+
+ /* invalid escape sequence */
+ in.current = "\"\\s\"";
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Double-quoted string unknown special character \'\\s\'.", "Line number 1.");
+
+ TEST_GET_ARGUMENT_SUCCESS("\'\\s\'", YCTX, Y_STR_ARG, "\\s\'", 2, "", 1);
+
+ /* invalid character after the argument */
+ in.current = "hello\"";
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Invalid character sequence \"\"\", expected unquoted string character, optsep, semicolon or opening brace.", "Line number 1.");
+
+ in.current = "hello}";
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Invalid character sequence \"}\", expected unquoted string character, optsep, semicolon or opening brace.", "Line number 1.");
+ /* invalid identifier-ref-arg-str */
+ in.current = "pre:pre:value";
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", "Line number 1.");
+
+ in.current = "\"\";"; /* empty identifier is not allowed */
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_IDENTIF_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Statement argument is required.", "Line number 1.");
+
+ in.current = "\"\";"; /* empty reference identifier is not allowed */
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Statement argument is required.", "Line number 1.");
+
+ /* slash is not an invalid character */
+ TEST_GET_ARGUMENT_SUCCESS("hello/x\t", YCTX, Y_STR_ARG, "hello/x\t", 7, "\t", 1);
+ assert_null(buf);
+
+ /* different quoting */
+ TEST_GET_ARGUMENT_SUCCESS("hello/x\t", YCTX, Y_STR_ARG, "hello/x\t", 7, "\t", 1);
+
+ TEST_GET_ARGUMENT_SUCCESS("hello ", YCTX, Y_STR_ARG, "hello ", 5, " ", 1);
+
+ TEST_GET_ARGUMENT_SUCCESS("hello/*comment*/\n", YCTX, Y_STR_ARG, "hello/*comment*/\n", 5, "\n", 1);
+
+ TEST_GET_ARGUMENT_SUCCESS("\"hello\\n\\t\\\"\\\\\";", YCTX, Y_STR_ARG, "hello\n\t\"\\", 9, ";", 1);
+ free(buf);
+
+ YCTX->indent = 14;
+ /* - space and tabs before newline are stripped out
+ * - space and tabs after newline (indentation) are stripped out
+ */
+ TEST_GET_ARGUMENT_SUCCESS("\"hello \t\n\t\t world!\"", YCTX, Y_STR_ARG, "hello\n world!", 14, "", 2);
+ free(buf);
+
+/* In contrast to previous, the backslash-escaped tabs are expanded after trimming, so they are preserved */
+ YCTX->indent = 14;
+ TEST_GET_ARGUMENT_SUCCESS("\"hello \\t\n\t\\t world!\"", YCTX, Y_STR_ARG, "hello \t\n\t world!", 16, "", 2);
+ assert_ptr_equal(word, buf);
+ free(buf);
+
+ /* Do not handle whitespaces after backslash-escaped newline as indentation */
+ YCTX->indent = 14;
+ TEST_GET_ARGUMENT_SUCCESS("\"hello\\n\t\t world!\"", YCTX, Y_STR_ARG, "hello\n\t\t world!", 15, "", 1);
+ assert_ptr_equal(word, buf);
+ free(buf);
+
+ YCTX->indent = 14;
+ TEST_GET_ARGUMENT_SUCCESS("\"hello\n \tworld!\"", YCTX, Y_STR_ARG, "hello\nworld!", 12, "", 2);
+ assert_ptr_equal(word, buf);
+ free(buf);
+
+ TEST_GET_ARGUMENT_SUCCESS("\'hello\'", YCTX, Y_STR_ARG, "hello'", 5, "", 1);
+
+ TEST_GET_ARGUMENT_SUCCESS("\"hel\" +\t\n\"lo\"", YCTX, Y_STR_ARG, "hello", 5, "", 2);
+ assert_ptr_equal(word, buf);
+ free(buf);
+
+ in.line = 1;
+ in.current = "\"hel\" +\t\nlo"; /* unquoted the second part */
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Both string parts divided by '+' must be quoted.", "Line number 2.");
+
+ TEST_GET_ARGUMENT_SUCCESS("\'he\'\t\n+ \"llo\"", YCTX, Y_STR_ARG, "hello", 5, "", 2);
+ free(buf);
+
+ TEST_GET_ARGUMENT_SUCCESS(" \t\n\"he\"+\'llo\'", YCTX, Y_STR_ARG, "hello", 5, "", 2);
+ free(buf);
+
+ /* missing argument */
+ in.line = 1;
+ in.current = ";";
+ assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len));
+ CHECK_LOG_CTX("Invalid character sequence \";\", expected an argument.", "Line number 1.");
+}
+
+#define TEST_STMS_SUCCESS(INPUT_TEXT, CTX, ACTION, EXPECT_WORD)\
+ in.current = INPUT_TEXT;\
+ assert_int_equal(LY_SUCCESS, get_keyword(CTX, &kw, &word, &len));\
+ assert_int_equal(ACTION, kw);\
+ assert_int_equal(strlen(EXPECT_WORD), len);\
+ assert_true(0 == strncmp(EXPECT_WORD, word, len))
+
+static void
+test_stmts(void **state)
+{
+ const char *p;
+ enum ly_stmt kw;
+ char *word;
+ size_t len;
+
+ in.current = "\n// comment\n\tinput\t{";
+ assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len));
+ assert_int_equal(LY_STMT_INPUT, kw);
+ assert_int_equal(5, len);
+ assert_string_equal("input\t{", word);
+ assert_string_equal("\t{", in.current);
+
+ in.current = "\t /* comment */\t output\n\t{";
+ assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len));
+ assert_int_equal(LY_STMT_OUTPUT, kw);
+ assert_int_equal(6, len);
+ assert_string_equal("output\n\t{", word);
+ assert_string_equal("\n\t{", in.current);
+ assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len));
+ assert_int_equal(LY_STMT_SYNTAX_LEFT_BRACE, kw);
+ assert_int_equal(1, len);
+ assert_string_equal("{", word);
+ assert_string_equal("", in.current);
+
+ in.current = "/input { "; /* invalid slash */
+ assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len));
+ CHECK_LOG_CTX("Invalid identifier first character '/'.", "Line number 4.");
+
+ in.current = "not-a-statement-nor-extension { "; /* invalid identifier */
+ assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len));
+ CHECK_LOG_CTX("Invalid character sequence \"not-a-statement-nor-extension\", expected a keyword.", "Line number 4.");
+
+ in.current = "path;"; /* missing sep after the keyword */
+ assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len));
+ CHECK_LOG_CTX("Invalid character sequence \"path;\", expected a keyword followed by a separator.", "Line number 4.");
+
+ TEST_STMS_SUCCESS("action ", YCTX, LY_STMT_ACTION, "action");
+
+ TEST_STMS_SUCCESS("anydata ", YCTX, LY_STMT_ANYDATA, "anydata");
+ TEST_STMS_SUCCESS("anyxml ", YCTX, LY_STMT_ANYXML, "anyxml");
+ TEST_STMS_SUCCESS("argument ", YCTX, LY_STMT_ARGUMENT, "argument");
+ TEST_STMS_SUCCESS("augment ", YCTX, LY_STMT_AUGMENT, "augment");
+ TEST_STMS_SUCCESS("base ", YCTX, LY_STMT_BASE, "base");
+ TEST_STMS_SUCCESS("belongs-to ", YCTX, LY_STMT_BELONGS_TO, "belongs-to");
+ TEST_STMS_SUCCESS("bit ", YCTX, LY_STMT_BIT, "bit");
+ TEST_STMS_SUCCESS("case ", YCTX, LY_STMT_CASE, "case");
+ TEST_STMS_SUCCESS("choice ", YCTX, LY_STMT_CHOICE, "choice");
+ TEST_STMS_SUCCESS("config ", YCTX, LY_STMT_CONFIG, "config");
+ TEST_STMS_SUCCESS("contact ", YCTX, LY_STMT_CONTACT, "contact");
+ TEST_STMS_SUCCESS("container ", YCTX, LY_STMT_CONTAINER, "container");
+ TEST_STMS_SUCCESS("default ", YCTX, LY_STMT_DEFAULT, "default");
+ TEST_STMS_SUCCESS("description ", YCTX, LY_STMT_DESCRIPTION, "description");
+ TEST_STMS_SUCCESS("deviate ", YCTX, LY_STMT_DEVIATE, "deviate");
+ TEST_STMS_SUCCESS("deviation ", YCTX, LY_STMT_DEVIATION, "deviation");
+ TEST_STMS_SUCCESS("enum ", YCTX, LY_STMT_ENUM, "enum");
+ TEST_STMS_SUCCESS("error-app-tag ", YCTX, LY_STMT_ERROR_APP_TAG, "error-app-tag");
+ TEST_STMS_SUCCESS("error-message ", YCTX, LY_STMT_ERROR_MESSAGE, "error-message");
+ TEST_STMS_SUCCESS("extension ", YCTX, LY_STMT_EXTENSION, "extension");
+ TEST_STMS_SUCCESS("feature ", YCTX, LY_STMT_FEATURE, "feature");
+ TEST_STMS_SUCCESS("fraction-digits ", YCTX, LY_STMT_FRACTION_DIGITS, "fraction-digits");
+ TEST_STMS_SUCCESS("grouping ", YCTX, LY_STMT_GROUPING, "grouping");
+ TEST_STMS_SUCCESS("identity ", YCTX, LY_STMT_IDENTITY, "identity");
+ TEST_STMS_SUCCESS("if-feature ", YCTX, LY_STMT_IF_FEATURE, "if-feature");
+ TEST_STMS_SUCCESS("import ", YCTX, LY_STMT_IMPORT, "import");
+ TEST_STMS_SUCCESS("include ", YCTX, LY_STMT_INCLUDE, "include");
+ TEST_STMS_SUCCESS("input{", YCTX, LY_STMT_INPUT, "input");
+ TEST_STMS_SUCCESS("key ", YCTX, LY_STMT_KEY, "key");
+ TEST_STMS_SUCCESS("leaf ", YCTX, LY_STMT_LEAF, "leaf");
+ TEST_STMS_SUCCESS("leaf-list ", YCTX, LY_STMT_LEAF_LIST, "leaf-list");
+ TEST_STMS_SUCCESS("length ", YCTX, LY_STMT_LENGTH, "length");
+ TEST_STMS_SUCCESS("list ", YCTX, LY_STMT_LIST, "list");
+ TEST_STMS_SUCCESS("mandatory ", YCTX, LY_STMT_MANDATORY, "mandatory");
+ TEST_STMS_SUCCESS("max-elements ", YCTX, LY_STMT_MAX_ELEMENTS, "max-elements");
+ TEST_STMS_SUCCESS("min-elements ", YCTX, LY_STMT_MIN_ELEMENTS, "min-elements");
+ TEST_STMS_SUCCESS("modifier ", YCTX, LY_STMT_MODIFIER, "modifier");
+ TEST_STMS_SUCCESS("module ", YCTX, LY_STMT_MODULE, "module");
+ TEST_STMS_SUCCESS("must ", YCTX, LY_STMT_MUST, "must");
+ TEST_STMS_SUCCESS("namespace ", YCTX, LY_STMT_NAMESPACE, "namespace");
+ TEST_STMS_SUCCESS("notification ", YCTX, LY_STMT_NOTIFICATION, "notification");
+ TEST_STMS_SUCCESS("ordered-by ", YCTX, LY_STMT_ORDERED_BY, "ordered-by");
+ TEST_STMS_SUCCESS("organization ", YCTX, LY_STMT_ORGANIZATION, "organization");
+ TEST_STMS_SUCCESS("output ", YCTX, LY_STMT_OUTPUT, "output");
+ TEST_STMS_SUCCESS("path ", YCTX, LY_STMT_PATH, "path");
+ TEST_STMS_SUCCESS("pattern ", YCTX, LY_STMT_PATTERN, "pattern");
+ TEST_STMS_SUCCESS("position ", YCTX, LY_STMT_POSITION, "position");
+ TEST_STMS_SUCCESS("prefix ", YCTX, LY_STMT_PREFIX, "prefix");
+ TEST_STMS_SUCCESS("presence ", YCTX, LY_STMT_PRESENCE, "presence");
+ TEST_STMS_SUCCESS("range ", YCTX, LY_STMT_RANGE, "range");
+ TEST_STMS_SUCCESS("reference ", YCTX, LY_STMT_REFERENCE, "reference");
+ TEST_STMS_SUCCESS("refine ", YCTX, LY_STMT_REFINE, "refine");
+ TEST_STMS_SUCCESS("require-instance ", YCTX, LY_STMT_REQUIRE_INSTANCE, "require-instance");
+ TEST_STMS_SUCCESS("revision ", YCTX, LY_STMT_REVISION, "revision");
+ TEST_STMS_SUCCESS("revision-date ", YCTX, LY_STMT_REVISION_DATE, "revision-date");
+ TEST_STMS_SUCCESS("rpc ", YCTX, LY_STMT_RPC, "rpc");
+ TEST_STMS_SUCCESS("status ", YCTX, LY_STMT_STATUS, "status");
+ TEST_STMS_SUCCESS("submodule ", YCTX, LY_STMT_SUBMODULE, "submodule");
+ TEST_STMS_SUCCESS("type ", YCTX, LY_STMT_TYPE, "type");
+ TEST_STMS_SUCCESS("typedef ", YCTX, LY_STMT_TYPEDEF, "typedef");
+ TEST_STMS_SUCCESS("unique ", YCTX, LY_STMT_UNIQUE, "unique");
+ TEST_STMS_SUCCESS("units ", YCTX, LY_STMT_UNITS, "units");
+ TEST_STMS_SUCCESS("uses ", YCTX, LY_STMT_USES, "uses");
+ TEST_STMS_SUCCESS("value ", YCTX, LY_STMT_VALUE, "value");
+ TEST_STMS_SUCCESS("when ", YCTX, LY_STMT_WHEN, "when");
+ TEST_STMS_SUCCESS("yang-version ", YCTX, LY_STMT_YANG_VERSION, "yang-version");
+ TEST_STMS_SUCCESS("yin-element ", YCTX, LY_STMT_YIN_ELEMENT, "yin-element");
+ TEST_STMS_SUCCESS(";config false;", YCTX, LY_STMT_SYNTAX_SEMICOLON, ";");
+ assert_string_equal("config false;", in.current);
+ TEST_STMS_SUCCESS("{ config false;", YCTX, LY_STMT_SYNTAX_LEFT_BRACE, "{");
+ assert_string_equal(" config false;", in.current);
+ TEST_STMS_SUCCESS("}", YCTX, LY_STMT_SYNTAX_RIGHT_BRACE, "}");
+ assert_string_equal("", in.current);
+
+ /* geenric extension */
+ in.current = p = "nacm:default-deny-write;";
+ assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len));
+ assert_int_equal(LY_STMT_EXTENSION_INSTANCE, kw);
+ assert_int_equal(23, len);
+ assert_ptr_equal(p, word);
+}
+
+static void
+test_minmax(void **state)
+{
+ uint16_t flags = 0;
+ uint32_t value = 0;
+ struct lysp_ext_instance *ext = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ in.current = " 1invalid; ...";
+ assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Invalid value \"1invalid\" of \"min-elements\".", "Line number 1.");
+
+ flags = value = 0;
+ in.current = " -1; ...";
+ assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"min-elements\".", "Line number 1.");
+
+ /* implementation limit */
+ flags = value = 0;
+ in.current = " 4294967296; ...";
+ assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Value \"4294967296\" is out of \"min-elements\" bounds.", "Line number 1.");
+
+ flags = value = 0;
+ in.current = " 1 {config true;} ...";
+ assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"min-elements\".", "Line number 1.");
+
+ in.current = " 1invalid; ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Invalid value \"1invalid\" of \"max-elements\".", "Line number 1.");
+
+ flags = value = 0;
+ in.current = " -1; ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"max-elements\".", "Line number 1.");
+
+ /* implementation limit */
+ flags = value = 0;
+ in.current = " 4294967296; ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Value \"4294967296\" is out of \"max-elements\" bounds.", "Line number 1.");
+
+ flags = value = 0;
+ in.current = " 1 {config true;} ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext));
+ CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"max-elements\".", "Line number 1.");
+}
+
+static void
+test_valid_module(void **state)
+{
+ struct lys_module *mod;
+ char *printed;
+ const char *links_yang =
+ "module links {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:module2\";\n"
+ " prefix mod2;\n"
+ "\n"
+ " identity just-another-identity;\n"
+ "\n"
+ " leaf one-leaf {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " list list-for-augment {\n"
+ " key keyleaf;\n"
+ "\n"
+ " leaf keyleaf {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf just-leaf {\n"
+ " type int32;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf rleaf {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf-list llist {\n"
+ " type string;\n"
+ " min-elements 0;\n"
+ " max-elements 100;\n"
+ " ordered-by user;\n"
+ " }\n"
+ "\n"
+ " grouping rgroup {\n"
+ " leaf rg1 {\n"
+ " type string;\n"
+ " }\n"
+ "\n"
+ " leaf rg2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ const char *statements_yang =
+ "module statements {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:module\";\n"
+ " prefix mod;\n"
+ "\n"
+ " import links {\n"
+ " prefix mod2;\n"
+ " }\n"
+ "\n"
+ " extension ext;\n"
+ "\n"
+ " identity random-identity {\n"
+ " base mod2:just-another-identity;\n"
+ " base another-identity;\n"
+ " }\n"
+ "\n"
+ " identity another-identity {\n"
+ " base mod2:just-another-identity;\n"
+ " }\n"
+ "\n"
+ " typedef percent {\n"
+ " type uint8 {\n"
+ " range \"0 .. 100\";\n"
+ " }\n"
+ " units \"percent\";\n"
+ " }\n"
+ "\n"
+ " list list1 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf x {\n"
+ " type string;\n"
+ " }\n"
+ " leaf y {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " container ice-cream-shop {\n"
+ " container employees {\n"
+ " when \"/list1/x\";\n"
+ " list employee {\n"
+ " key \"id\";\n"
+ " unique \"name\";\n"
+ " config true;\n"
+ " min-elements 0 {\n"
+ " mod:ext;\n"
+ " }\n"
+ " max-elements unbounded;\n"
+ " leaf id {\n"
+ " type uint64;\n"
+ " mandatory true;\n"
+ " }\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " leaf age {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container random {\n"
+ " grouping group {\n"
+ " leaf g1 {\n"
+ " type percent;\n"
+ " mandatory false;\n"
+ " }\n"
+ " leaf g2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " choice switch {\n"
+ " case a {\n"
+ " leaf aleaf {\n"
+ " type string;\n"
+ " default \"aaa\";\n"
+ " }\n"
+ " }\n"
+ " case c {\n"
+ " leaf cleaf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " anyxml xml-data;\n"
+ " anydata any-data;\n"
+ " leaf-list leaflist {\n"
+ " type string;\n"
+ " min-elements 0;\n"
+ " max-elements 20;\n"
+ " }\n"
+ " uses group;\n"
+ " uses mod2:rgroup;\n"
+ " leaf lref {\n"
+ " type leafref {\n"
+ " path \"/mod2:one-leaf\";\n"
+ " }\n"
+ " }\n"
+ " leaf iref {\n"
+ " type identityref {\n"
+ " base mod2:just-another-identity;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " augment \"/random\" {\n"
+ " leaf aug-leaf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " notification notif;\n"
+ "\n"
+ " deviation \"/mod:ice-cream-shop/mod:employees/mod:employee/mod:age\" {\n"
+ " deviate not-supported {\n"
+ " mod:ext;\n"
+ " }\n"
+ " }\n"
+ " deviation \"/mod:list1\" {\n"
+ " deviate add {\n"
+ " mod:ext;\n"
+ " must \"1\";\n"
+ " must \"2\";\n"
+ " unique \"x\";\n"
+ " unique \"y\";\n"
+ " config true;\n"
+ " min-elements 1;\n"
+ " max-elements 2;\n"
+ " }\n"
+ " }\n"
+ " deviation \"/mod:ice-cream-shop/mod:employees/mod:employee\" {\n"
+ " deviate delete {\n"
+ " unique \"name\";\n"
+ " }\n"
+ " }\n"
+ " deviation \"/mod:random/mod:leaflist\" {\n"
+ " deviate replace {\n"
+ " type uint32;\n"
+ " min-elements 10;\n"
+ " max-elements 15;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, &mod);
+ lys_print_mem(&printed, mod, LYS_OUT_YANG, 0);
+ assert_string_equal(printed, statements_yang);
+ free(printed);
+}
+
+static struct lysp_module *
+mod_renew(struct lysp_yang_ctx *ctx)
+{
+ struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
+ struct lysp_module *pmod;
+
+ lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0);
+ pmod = calloc(1, sizeof *pmod);
+ ctx->parsed_mods->objs[0] = pmod;
+ pmod->mod = calloc(1, sizeof *pmod->mod);
+ pmod->mod->parsed = pmod;
+ pmod->mod->ctx = ly_ctx;
+
+ ctx->in->line = 1;
+ fctx.mod = pmod->mod;
+
+ return pmod;
+}
+
+static struct lysp_submodule *
+submod_renew(struct lysp_yang_ctx *ctx)
+{
+ struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
+ struct lysp_submodule *submod;
+
+ lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0);
+ submod = calloc(1, sizeof *submod);
+ ctx->parsed_mods->objs[0] = submod;
+ submod->mod = calloc(1, sizeof *submod->mod);
+ lydict_insert(ly_ctx, "name", 0, &submod->mod->name);
+ submod->mod->parsed = (struct lysp_module *)submod;
+ submod->mod->ctx = ly_ctx;
+
+ fctx.mod = submod->mod;
+
+ return submod;
+}
+
+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))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_module(void **state)
+{
+ struct lysp_module *mod = NULL;
+ struct lysp_submodule *submod = NULL;
+ struct lys_module *m;
+ struct lysp_yang_ctx *ctx_p;
+
+ mod = mod_renew(YCTX);
+
+ /* missing mandatory substatements */
+ in.current = " name {}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ assert_string_equal("name", mod->mod->name);
+ CHECK_LOG_CTX("Missing mandatory keyword \"namespace\" as a child of \"module\".", "Line number 1.");
+
+ mod = mod_renew(YCTX);
+ in.current = " name {namespace urn:name;}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ assert_string_equal("urn:name", mod->mod->ns);
+ CHECK_LOG_CTX("Missing mandatory keyword \"prefix\" as a child of \"module\".", "Line number 1.");
+ mod = mod_renew(YCTX);
+
+ in.current = " name {namespace urn:name;prefix \"n\";}";
+ assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod));
+ assert_string_equal("n", mod->mod->prefix);
+ mod = mod_renew(YCTX);
+
+#define SCHEMA_BEGINNING " name {yang-version 1.1;namespace urn:x;prefix \"x\";"
+#define SCHEMA_BEGINNING2 " name {namespace urn:x;prefix \"x\";"
+#define TEST_NODE(NODETYPE, INPUT, NAME) \
+ in.current = SCHEMA_BEGINNING INPUT; \
+ assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); \
+ assert_non_null(mod->data); \
+ assert_int_equal(NODETYPE, mod->data->nodetype); \
+ assert_string_equal(NAME, mod->data->name); \
+ mod = mod_renew(YCTX);
+#define TEST_GENERIC(INPUT, TARGET, TEST) \
+ in.current = SCHEMA_BEGINNING INPUT; \
+ assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); \
+ assert_non_null(TARGET); \
+ TEST; \
+ mod = mod_renew(YCTX);
+#define TEST_DUP(MEMBER, VALUE1, VALUE2, LINE) \
+ TEST_DUP_GENERIC(SCHEMA_BEGINNING, MEMBER, VALUE1, VALUE2, \
+ parse_module, mod, LINE, mod = mod_renew(YCTX))
+
+ /* duplicated namespace, prefix */
+ TEST_DUP("namespace", "y", "z", "1");
+ TEST_DUP("prefix", "y", "z", "1");
+ TEST_DUP("contact", "a", "b", "1");
+ TEST_DUP("description", "a", "b", "1");
+ TEST_DUP("organization", "a", "b", "1");
+ TEST_DUP("reference", "a", "b", "1");
+
+ /* not allowed in module (submodule-specific) */
+ in.current = SCHEMA_BEGINNING "belongs-to master {prefix m;}}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ CHECK_LOG_CTX("Invalid keyword \"belongs-to\" as a child of \"module\".", "Line number 1.");
+ mod = mod_renew(YCTX);
+
+ /* anydata */
+ TEST_NODE(LYS_ANYDATA, "anydata test;}", "test");
+ /* anyxml */
+ TEST_NODE(LYS_ANYXML, "anyxml test;}", "test");
+ /* augment */
+ TEST_GENERIC("augment /somepath;}", mod->augments,
+ assert_string_equal("/somepath", mod->augments[0].nodeid));
+ /* choice */
+ TEST_NODE(LYS_CHOICE, "choice test;}", "test");
+ /* contact 0..1 */
+ TEST_GENERIC("contact \"firstname\" + \n\t\" surname\";}", mod->mod->contact,
+ assert_string_equal("firstname surname", mod->mod->contact));
+ /* container */
+ TEST_NODE(LYS_CONTAINER, "container test;}", "test");
+ /* description 0..1 */
+ TEST_GENERIC("description \'some description\';}", mod->mod->dsc,
+ assert_string_equal("some description", mod->mod->dsc));
+ /* deviation */
+ TEST_GENERIC("deviation /somepath {deviate not-supported;}}", mod->deviations,
+ assert_string_equal("/somepath", mod->deviations[0].nodeid));
+ /* extension */
+ TEST_GENERIC("extension test;}", mod->extensions,
+ assert_string_equal("test", mod->extensions[0].name));
+ /* feature */
+ TEST_GENERIC("feature test;}", mod->features,
+ assert_string_equal("test", mod->features[0].name));
+ /* grouping */
+ TEST_GENERIC("grouping grp;}", mod->groupings,
+ assert_string_equal("grp", mod->groupings[0].name));
+ /* identity */
+ TEST_GENERIC("identity test;}", mod->identities,
+ assert_string_equal("test", mod->identities[0].name));
+ /* import */
+ ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "module zzz { namespace urn:zzz; prefix z;}");
+ TEST_GENERIC("import zzz {prefix z;}}", mod->imports,
+ assert_string_equal("zzz", mod->imports[0].name));
+
+ /* import - prefix collision */
+ in.current = SCHEMA_BEGINNING "import zzz {prefix x;}}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ CHECK_LOG_CTX("Prefix \"x\" already used as module prefix.", "Line number 1.");
+ mod = mod_renew(YCTX);
+
+ in.current = SCHEMA_BEGINNING "import zzz {prefix y;}import zzz {prefix y;}}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ CHECK_LOG_CTX("Prefix \"y\" already used to import \"zzz\" module.", "Line number 1.");
+
+ mod = mod_renew(YCTX);
+ LOG_LOCBACK(0, 0, 0, 1);
+
+ in.current = "module name10 {yang-version 1.1;namespace urn:name10;prefix \"n10\";import zzz {prefix y;}import zzz {prefix z;}}";
+ assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_SUCCESS);
+ CHECK_LOG_CTX("Single revision of the module \"zzz\" imported twice.", NULL);
+
+ /* 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);
+ CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, "Including \"xxx\" submodule into \"name\" failed.", NULL);
+
+ ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to wrong-name {prefix w;}}");
+ in.current = "module" SCHEMA_BEGINNING "include xxx;}";
+ assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, "Including \"xxx\" submodule into \"name\" failed.", NULL);
+
+ ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to name {prefix x;}}");
+ TEST_GENERIC("include xxx;}", mod->includes,
+ assert_string_equal("xxx", mod->includes[0].name));
+
+ /* leaf */
+ TEST_NODE(LYS_LEAF, "leaf test {type string;}}", "test");
+ /* leaf-list */
+ TEST_NODE(LYS_LEAFLIST, "leaf-list test {type string;}}", "test");
+ /* list */
+ TEST_NODE(LYS_LIST, "list test {key a;leaf a {type string;}}}", "test");
+ /* notification */
+ TEST_GENERIC("notification test;}", mod->notifs,
+ assert_string_equal("test", mod->notifs[0].name));
+ /* organization 0..1 */
+ TEST_GENERIC("organization \"CESNET a.l.e.\";}", mod->mod->org,
+ assert_string_equal("CESNET a.l.e.", mod->mod->org));
+ /* reference 0..1 */
+ TEST_GENERIC("reference RFC7950;}", mod->mod->ref,
+ assert_string_equal("RFC7950", mod->mod->ref));
+ /* revision */
+ TEST_GENERIC("revision 2018-10-12;}", mod->revs,
+ assert_string_equal("2018-10-12", mod->revs[0].date));
+ /* rpc */
+ TEST_GENERIC("rpc test;}", mod->rpcs,
+ assert_string_equal("test", mod->rpcs[0].name));
+ /* typedef */
+ TEST_GENERIC("typedef test{type string;}}", mod->typedefs,
+ assert_string_equal("test", mod->typedefs[0].name));
+ /* uses */
+ TEST_NODE(LYS_USES, "uses test;}", "test");
+ /* yang-version */
+ in.current = SCHEMA_BEGINNING2 "\n\tyang-version 10;}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ CHECK_LOG_CTX("Invalid value \"10\" of \"yang-version\".", NULL);
+ mod = mod_renew(YCTX);
+ in.current = SCHEMA_BEGINNING2 "yang-version 1;yang-version 1.1;}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ CHECK_LOG_CTX("Duplicate keyword \"yang-version\".", NULL);
+ mod = mod_renew(YCTX);
+ in.current = SCHEMA_BEGINNING2 "yang-version 1;}";
+ assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod));
+ assert_int_equal(1, mod->version);
+ mod = mod_renew(YCTX);
+ in.current = SCHEMA_BEGINNING2 "yang-version \"1.1\";}";
+ assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod));
+ assert_int_equal(2, mod->version);
+ mod = mod_renew(YCTX);
+
+ in.current = "module " SCHEMA_BEGINNING "} module q {namespace urn:q;prefixq;}";
+ m = calloc(1, sizeof *m);
+ m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx;
+ assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m));
+ CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after module, expected end-of-input.", "Line number 1.");
+ lysp_yang_ctx_free(ctx_p);
+ lys_module_free(&fctx, m, 0);
+
+ in.current = "prefix " SCHEMA_BEGINNING "}";
+ m = calloc(1, sizeof *m);
+ m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx;
+ assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m));
+ CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", "Line number 1.");
+ lysp_yang_ctx_free(ctx_p);
+ lys_module_free(&fctx, m, 0);
+
+ in.current = "module " SCHEMA_BEGINNING "leaf enum {type enumeration {enum seven { position 7;}}}}";
+ m = calloc(1, sizeof *m);
+ m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx;
+ assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m));
+ CHECK_LOG_CTX("Invalid keyword \"position\" as a child of \"enum\".", "Line number 1.");
+ lysp_yang_ctx_free(ctx_p);
+ lys_module_free(&fctx, m, 0);
+
+ /* extensions */
+ TEST_GENERIC("prefix:test;}", mod->exts,
+ assert_string_equal("prefix:test", mod->exts[0].name);
+ assert_int_equal(LY_STMT_MODULE, mod->exts[0].parent_stmt));
+ mod = mod_renew(YCTX);
+
+ /* invalid substatement */
+ in.current = SCHEMA_BEGINNING "must false;}";
+ assert_int_equal(LY_EVALID, parse_module(YCTX, mod));
+ CHECK_LOG_CTX("Invalid keyword \"must\" as a child of \"module\".", NULL);
+
+ /* submodule */
+ submod = submod_renew(YCTX);
+
+ /* missing mandatory substatements */
+ in.current = " subname {}";
+ assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod));
+ CHECK_LOG_CTX("Missing mandatory keyword \"belongs-to\" as a child of \"submodule\".", NULL);
+ assert_string_equal("subname", submod->name);
+
+ submod = submod_renew(YCTX);
+
+ in.current = " subname {belongs-to name {prefix x;}}";
+ assert_int_equal(LY_SUCCESS, parse_submodule(YCTX, submod));
+ assert_string_equal("name", submod->mod->name);
+ submod = submod_renew(YCTX);
+
+#undef SCHEMA_BEGINNING
+#define SCHEMA_BEGINNING " subname {belongs-to name {prefix x;}"
+
+ /* duplicated namespace, prefix */
+ in.current = " subname {belongs-to name {prefix x;}belongs-to module1;belongs-to module2;} ...";
+ assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod));
+ CHECK_LOG_CTX("Duplicate keyword \"belongs-to\".", NULL);
+ submod = submod_renew(YCTX);
+
+ /* not allowed in submodule (module-specific) */
+ in.current = SCHEMA_BEGINNING "namespace \"urn:z\";}";
+ assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod));
+ CHECK_LOG_CTX("Invalid keyword \"namespace\" as a child of \"submodule\".", NULL);
+ submod = submod_renew(YCTX);
+ in.current = SCHEMA_BEGINNING "prefix m;}}";
+ assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod));
+ CHECK_LOG_CTX("Invalid keyword \"prefix\" as a child of \"submodule\".", NULL);
+ submod = submod_renew(YCTX);
+
+ in.current = "submodule " SCHEMA_BEGINNING "} module q {namespace urn:q;prefixq;}";
+ assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx_p, PARSER_CUR_PMOD(YCTX)->mod->ctx, (struct lysp_ctx *)YCTX, YCTX->in, &submod));
+ CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after submodule, expected end-of-input.", "Line number 1.");
+ lysp_yang_ctx_free(ctx_p);
+
+ in.current = "prefix " SCHEMA_BEGINNING "}";
+ assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx_p, PARSER_CUR_PMOD(YCTX)->mod->ctx, (struct lysp_ctx *)YCTX, YCTX->in, &submod));
+ CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", "Line number 1.");
+ lysp_yang_ctx_free(ctx_p);
+ submod = submod_renew(YCTX);
+
+#undef TEST_GENERIC
+#undef TEST_NODE
+#undef TEST_DUP
+#undef SCHEMA_BEGINNING
+}
+
+static void
+test_deviation(void **state)
+{
+ struct lysp_deviation *d = NULL;
+
+ /* invalid cardinality */
+ TEST_DUP_GENERIC(" test {deviate not-supported;", "description", "a", "b", parse_deviation, &d, "1", );
+ TEST_DUP_GENERIC(" test {deviate not-supported;", "reference", "a", "b", parse_deviation, &d, "1", );
+
+ /* missing mandatory substatement */
+ in.current = " test {description text;}";
+ assert_int_equal(LY_EVALID, parse_deviation(YCTX, &d));
+ CHECK_LOG_CTX("Missing mandatory keyword \"deviate\" as a child of \"deviation\".", "Line number 1.");
+
+ /* invalid substatement */
+ in.current = " test {deviate not-supported; status obsolete;}";
+ assert_int_equal(LY_EVALID, parse_deviation(YCTX, &d));
+ CHECK_LOG_CTX("Invalid keyword \"status\" as a child of \"deviation\".", "Line number 1.");
+}
+
+static void
+test_deviate(void **state)
+{
+ struct lysp_deviate *d = NULL;
+
+ /* invalid cardinality */
+ TEST_DUP_GENERIC("add {", "config", "true", "false", parse_deviate, &d, "1", );
+ TEST_DUP_GENERIC("add {", "mandatory", "true", "false", parse_deviate, &d, "1", );
+ TEST_DUP_GENERIC("add {", "max-elements", "1", "2", parse_deviate, &d, "1", );
+ TEST_DUP_GENERIC("add {", "min-elements", "1", "2", parse_deviate, &d, "1", );
+ TEST_DUP_GENERIC("add {", "units", "kilometers", "miles", parse_deviate, &d, "1", );
+
+ /* invalid substatements */
+#define TEST_NOT_SUP(DEV, STMT, VALUE) \
+ in.current = " "DEV" {"STMT" "VALUE";}..."; \
+ assert_int_equal(LY_EVALID, parse_deviate(YCTX, &d)); \
+ CHECK_LOG_CTX("Deviate \""DEV"\" does not support keyword \""STMT"\".", "Line number 1.");
+
+ TEST_NOT_SUP("not-supported", "units", "meters");
+ TEST_NOT_SUP("not-supported", "must", "1");
+ TEST_NOT_SUP("not-supported", "unique", "x");
+ TEST_NOT_SUP("not-supported", "default", "a");
+ TEST_NOT_SUP("not-supported", "config", "true");
+ TEST_NOT_SUP("not-supported", "mandatory", "true");
+ TEST_NOT_SUP("not-supported", "min-elements", "1");
+ TEST_NOT_SUP("not-supported", "max-elements", "2");
+ TEST_NOT_SUP("not-supported", "type", "string");
+ TEST_NOT_SUP("add", "type", "string");
+ TEST_NOT_SUP("delete", "config", "true");
+ TEST_NOT_SUP("delete", "mandatory", "true");
+ TEST_NOT_SUP("delete", "min-elements", "1");
+ TEST_NOT_SUP("delete", "max-elements", "2");
+ TEST_NOT_SUP("delete", "type", "string");
+ TEST_NOT_SUP("replace", "must", "1");
+ TEST_NOT_SUP("replace", "unique", "a");
+
+ in.current = " nonsence; ...";
+ assert_int_equal(LY_EVALID, parse_deviate(YCTX, &d));
+ CHECK_LOG_CTX("Invalid value \"nonsence\" of \"deviate\".", "Line number 1.");\
+ assert_null(d);
+#undef TEST_NOT_SUP
+}
+
+static void
+test_container(void **state)
+{
+ struct lysp_node_container *c = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "cont {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node**)&c)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)c); c = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("presence", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "cont {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; leaf l {type string;}"
+ "leaf-list ll {type string;} list li;must 'expr';notification not; presence true; reference test;status current;typedef t {type int8;}uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c));
+ CHECK_LYSP_NODE(c, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "cont", 0, LYS_CONTAINER, 0, "test", 1);
+ assert_non_null(c->actions);
+ assert_non_null(c->child);
+ assert_non_null(c->groupings);
+ assert_non_null(c->musts);
+ assert_non_null(c->notifs);
+ assert_string_equal("true", c->presence);
+ assert_non_null(c->typedefs);
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ ly_set_erase(&YCTX->grps_nodes, NULL);
+ lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL;
+
+ /* invalid */
+ in.current = " cont {augment /root;} ...";
+ assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c));
+ CHECK_LOG_CTX("Invalid keyword \"augment\" as a child of \"container\".", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL;
+ in.current = " cont {nonsence true;} ...";
+ assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c));
+ CHECK_LOG_CTX("Invalid character sequence \"nonsence\", expected a keyword.", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */
+ in.current = " cont {action x;} ...";
+ assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c));
+ CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"container\" - "
+ "the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL;
+}
+
+static void
+test_leaf(void **state)
+{
+ struct lysp_node_leaf *l = NULL;
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node**)&l)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)l); l = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("default", "x", "y");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("mandatory", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("type", "int8", "uint8");
+ TEST_DUP("units", "text1", "text2");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content - without mandatory which is mutual exclusive with default */
+ in.current = "l {config false;default \"xxx\";description test;if-feature f;"
+ "must 'expr';reference test;status current;type string; units yyy;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaf(YCTX, NULL, (struct lysp_node **)&l));
+ CHECK_LYSP_NODE(l, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "l", 0, LYS_LEAF, 0, "test", 1);
+ assert_string_equal("xxx", l->dflt.str);
+ assert_string_equal("yyy", l->units);
+ assert_string_equal("string", l->type.name);
+ assert_non_null(l->musts);
+ lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL;
+
+ /* full content - now with mandatory */
+ in.current = "l {mandatory true; type string;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaf(YCTX, NULL, (struct lysp_node **)&l));
+ CHECK_LYSP_NODE(l, NULL, 0, LYS_MAND_TRUE, 0, "l", 0, LYS_LEAF, 0, NULL, 0);
+ assert_string_equal("string", l->type.name);
+ lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL;
+
+ /* invalid */
+ in.current = " l {description \"missing type\";} ...";
+ assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node **)&l));
+ CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"leaf\".", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL;
+
+ in.current = "l { type iid { path qpud wrong {";
+ assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node **)&l));
+ CHECK_LOG_CTX("Invalid character sequence \"wrong\", expected a keyword.", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL;
+}
+
+static void
+test_leaflist(void **state)
+{
+ struct lysp_node_leaflist *ll = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "ll {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node**)&ll)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)ll); ll = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("max-elements", "10", "20");
+ TEST_DUP("min-elements", "10", "20");
+ TEST_DUP("ordered-by", "user", "system");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("type", "int8", "uint8");
+ TEST_DUP("units", "text1", "text2");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content - without min-elements which is mutual exclusive with default */
+ in.current = "ll {config false;default \"xxx\"; default \"yyy\";description test;if-feature f;"
+ "max-elements 10;must 'expr';ordered-by user;reference test;"
+ "status current;type string; units zzz;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll));
+ CHECK_LYSP_NODE(ll, "test", 1, 0x446, 1, "ll", 0, LYS_LEAFLIST, 0, "test", 1);
+ assert_non_null(ll->dflts);
+ assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts));
+ assert_string_equal("xxx", ll->dflts[0].str);
+ assert_string_equal("yyy", ll->dflts[1].str);
+ assert_string_equal("zzz", ll->units);
+ assert_int_equal(10, ll->max);
+ assert_int_equal(0, ll->min);
+ assert_string_equal("string", ll->type.name);
+ assert_non_null(ll->musts);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_MAX, ll->flags);
+ lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL;
+
+ /* full content - now with min-elements */
+ in.current = "ll {min-elements 10; type string;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll));
+ CHECK_LYSP_NODE(ll, NULL, 0, 0x200, 0, "ll", 0, LYS_LEAFLIST, 0, NULL, 0);
+ assert_string_equal("string", ll->type.name);
+ assert_int_equal(0, ll->max);
+ assert_int_equal(10, ll->min);
+ assert_int_equal(LYS_SET_MIN, ll->flags);
+ lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL;
+
+ /* invalid */
+ in.current = " ll {description \"missing type\";} ...";
+ assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll));
+ CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"leaf-list\".", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 - default statement is not allowed */
+ in.current = " ll {default xx; type string;} ...";
+ assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll));
+ CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"leaf-list\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL;
+}
+
+static void
+test_list(void **state)
+{
+ struct lysp_node_list *l = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_list(YCTX, NULL, (struct lysp_node**)&l)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)l); l = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("key", "one", "two");
+ TEST_DUP("max-elements", "10", "20");
+ TEST_DUP("min-elements", "10", "20");
+ TEST_DUP("ordered-by", "user", "system");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "l {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; key l; leaf l {type string;}"
+ "leaf-list ll {type string;} list li;max-elements 10; min-elements 1;must 'expr';notification not; ordered-by system; reference test;"
+ "status current;typedef t {type int8;}unique xxx;unique yyy;uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_list(YCTX, NULL, (struct lysp_node **)&l));
+ CHECK_LYSP_NODE(l, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_MAX | LYS_SET_MIN, 1, "l",
+ 0, LYS_LIST, 0, "test", 1);
+ assert_string_equal("l", l->key);
+ assert_non_null(l->uniques);
+ assert_int_equal(2, LY_ARRAY_COUNT(l->uniques));
+ assert_string_equal("xxx", l->uniques[0].str);
+ assert_string_equal("yyy", l->uniques[1].str);
+ assert_int_equal(10, l->max);
+ assert_int_equal(1, l->min);
+ assert_non_null(l->musts);
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ ly_set_erase(&YCTX->grps_nodes, NULL);
+ lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL;
+
+ /* invalid content */
+ PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */
+ in.current = "l {action x;} ...";
+ assert_int_equal(LY_EVALID, parse_list(YCTX, NULL, (struct lysp_node **)&l));
+ CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"list\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL;
+}
+
+static void
+test_choice(void **state)
+{
+ struct lysp_node_choice *ch = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "ch {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_choice(YCTX, NULL, (struct lysp_node**)&ch)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)ch); ch = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("default", "a", "b");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("mandatory", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content - without default due to a collision with mandatory */
+ in.current = "ch {anydata any;anyxml anyxml; case c;choice ch;config false;container c;description test;if-feature f;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;mandatory true;reference test;status current;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_choice(YCTX, NULL, (struct lysp_node **)&ch));
+ CHECK_LYSP_NODE(ch, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "ch", 0, LYS_CHOICE, 0, "test", 1);
+ lysp_node_free(&fctx, (struct lysp_node *)ch); ch = NULL;
+
+ /* full content - the default missing from the previous node */
+ in.current = "ch {default c;case c;} ...";
+ assert_int_equal(LY_SUCCESS, parse_choice(YCTX, NULL, (struct lysp_node **)&ch));
+ CHECK_LYSP_NODE(ch, NULL, 0, 0, 0, "ch", 0, LYS_CHOICE, 0, NULL, 0);
+ assert_string_equal("c", ch->dflt.str);
+ lysp_node_free(&fctx, (struct lysp_node *)ch); ch = NULL;
+}
+
+static void
+test_case(void **state)
+{
+ struct lysp_node_case *cs = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "cs {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_case(YCTX, NULL, (struct lysp_node**)&cs)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)cs); cs = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "cs {anydata any;anyxml anyxml; choice ch;container c;description test;if-feature f;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;reference test;status current;uses grp;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_case(YCTX, NULL, (struct lysp_node **)&cs));
+ CHECK_LYSP_NODE(cs, "test", 1, LYS_STATUS_CURR, 1, "cs", 0, LYS_CASE, 0, "test", 1);
+ lysp_node_free(&fctx, (struct lysp_node *)cs); cs = NULL;
+
+ /* invalid content */
+ in.current = "cs {config true} ...";
+ assert_int_equal(LY_EVALID, parse_case(YCTX, NULL, (struct lysp_node **)&cs));
+ CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"case\".", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)cs); cs = NULL;
+}
+
+static void
+test_any(void **state, enum ly_stmt kw)
+{
+ struct lysp_node_anydata *any = NULL;
+
+ if (kw == LY_STMT_ANYDATA) {
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+ } else {
+ PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */
+ }
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_any(YCTX, kw, NULL, (struct lysp_node**)&any)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)any); any = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("mandatory", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "any {config true;description test;if-feature f;mandatory true;must 'expr';reference test;status current;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_any(YCTX, kw, NULL, (struct lysp_node **)&any));
+ // CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, TYPE, PARENT, REF, WHEN)
+ uint16_t node_type = kw == LY_STMT_ANYDATA ? LYS_ANYDATA : LYS_ANYXML;
+
+ CHECK_LYSP_NODE(any, "test", 1, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "any", 0, node_type, 0, "test", 1);
+ assert_non_null(any->musts);
+ lysp_node_free(&fctx, (struct lysp_node *)any); any = NULL;
+}
+
+static void
+test_anydata(void **state)
+{
+ test_any(state, LY_STMT_ANYDATA);
+}
+
+static void
+test_anyxml(void **state)
+{
+ test_any(state, LY_STMT_ANYXML);
+}
+
+static void
+test_grouping(void **state)
+{
+ struct lysp_node_grp *grp = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, &grp->node); grp = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "grp {action x;anydata any;anyxml anyxml; choice ch;container c;description test;grouping g;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;notification not;reference test;status current;typedef t {type int8;}uses g;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_grouping(YCTX, NULL, &grp));
+ assert_non_null(grp);
+ assert_int_equal(LYS_GROUPING, grp->nodetype);
+ assert_string_equal("grp", grp->name);
+ assert_string_equal("test", grp->dsc);
+ assert_non_null(grp->exts);
+ assert_string_equal("test", grp->ref);
+ assert_null(grp->parent);
+ assert_int_equal(LYS_STATUS_CURR, grp->flags);
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ ly_set_erase(&YCTX->grps_nodes, NULL);
+ lysp_node_free(&fctx, &grp->node);
+ grp = NULL;
+
+ /* invalid content */
+ in.current = "grp {config true} ...";
+ assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp));
+ CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"grouping\".", "Line number 1.");
+ lysp_node_free(&fctx, &grp->node);
+ grp = NULL;
+
+ in.current = "grp {must 'expr'} ...";
+ assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp));
+ CHECK_LOG_CTX("Invalid keyword \"must\" as a child of \"grouping\".", "Line number 1.");
+ lysp_node_free(&fctx, &grp->node);
+ grp = NULL;
+}
+
+static void
+test_action(void **state)
+{
+ struct lysp_node_action *rpcs = NULL;
+ struct lysp_node_container *c = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_action(YCTX, NULL, &rpcs)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)rpcs); rpcs = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("input", "{leaf l1 {type empty;}} description a", "{leaf l2 {type empty;}} description a");
+ TEST_DUP("output", "{leaf l1 {type empty;}} description a", "{leaf l2 {type empty;}} description a");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "top;";
+ assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c));
+ in.current = "func {description test;grouping grp;if-feature f;reference test;status current;typedef mytype {type int8;} m:ext;"
+ "input {anydata a1; anyxml a2; choice ch; container c; grouping grp; leaf l {type int8;} leaf-list ll {type int8;}"
+ " list li; must 1; typedef mytypei {type int8;} uses grp; m:ext;}"
+ "output {anydata a1; anyxml a2; choice ch; container c; grouping grp; leaf l {type int8;} leaf-list ll {type int8;}"
+ " list li; must 1; typedef mytypeo {type int8;} uses grp; m:ext;}} ...";
+ assert_int_equal(LY_SUCCESS, parse_action(YCTX, (struct lysp_node *)c, &rpcs));
+ assert_non_null(rpcs);
+ assert_int_equal(LYS_ACTION, rpcs->nodetype);
+ assert_string_equal("func", rpcs->name);
+ assert_string_equal("test", rpcs->dsc);
+ assert_non_null(rpcs->exts);
+ assert_non_null(rpcs->iffeatures);
+ assert_string_equal("test", rpcs->ref);
+ assert_non_null(rpcs->groupings);
+ assert_non_null(rpcs->typedefs);
+ assert_int_equal(LYS_STATUS_CURR, rpcs->flags);
+ /* input */
+ assert_int_equal(rpcs->input.nodetype, LYS_INPUT);
+ assert_non_null(rpcs->input.groupings);
+ assert_non_null(rpcs->input.exts);
+ assert_non_null(rpcs->input.musts);
+ assert_non_null(rpcs->input.typedefs);
+ assert_non_null(rpcs->input.child);
+ /* output */
+ assert_int_equal(rpcs->output.nodetype, LYS_OUTPUT);
+ assert_non_null(rpcs->output.groupings);
+ assert_non_null(rpcs->output.exts);
+ assert_non_null(rpcs->output.musts);
+ assert_non_null(rpcs->output.typedefs);
+ assert_non_null(rpcs->output.child);
+
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ ly_set_erase(&YCTX->grps_nodes, NULL);
+ lysp_node_free(&fctx, (struct lysp_node *)rpcs); rpcs = NULL;
+
+ /* invalid content */
+ in.current = "func {config true} ...";
+ assert_int_equal(LY_EVALID, parse_action(YCTX, NULL, &rpcs));
+ CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"rpc\".", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)rpcs); rpcs = NULL;
+
+ lysp_node_free(&fctx, (struct lysp_node *)c);
+}
+
+static void
+test_notification(void **state)
+{
+ struct lysp_node_notif *notifs = NULL;
+ struct lysp_node_container *c = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_notif(YCTX, NULL, &notifs)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)notifs); notifs = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "top;";
+ assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c));
+ in.current = "ntf {anydata a1; anyxml a2; choice ch; container c; description test; grouping grp; if-feature f; leaf l {type int8;}"
+ "leaf-list ll {type int8;} list li; must 1; reference test; status current; typedef mytype {type int8;} uses grp; m:ext;}";
+ assert_int_equal(LY_SUCCESS, parse_notif(YCTX, (struct lysp_node *)c, &notifs));
+ assert_non_null(notifs);
+ assert_int_equal(LYS_NOTIF, notifs->nodetype);
+ assert_string_equal("ntf", notifs->name);
+ assert_string_equal("test", notifs->dsc);
+ assert_non_null(notifs->exts);
+ assert_non_null(notifs->iffeatures);
+ assert_string_equal("test", notifs->ref);
+ assert_non_null(notifs->groupings);
+ assert_non_null(notifs->typedefs);
+ assert_non_null(notifs->musts);
+ assert_non_null(notifs->child);
+ assert_int_equal(LYS_STATUS_CURR, notifs->flags);
+
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ ly_set_erase(&YCTX->grps_nodes, NULL);
+ lysp_node_free(&fctx, (struct lysp_node *)notifs); notifs = NULL;
+
+ /* invalid content */
+ in.current = "ntf {config true} ...";
+ assert_int_equal(LY_EVALID, parse_notif(YCTX, NULL, &notifs));
+ CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"notification\".", "Line number 1.");
+ lysp_node_free(&fctx, (struct lysp_node *)notifs); notifs = NULL;
+
+ lysp_node_free(&fctx, (struct lysp_node *)c);
+}
+
+static void
+test_uses(void **state)
+{
+ struct lysp_node_uses *u = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_uses(YCTX, NULL, (struct lysp_node**)&u)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node*)u); u = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "grpref {augment some/node;description test;if-feature f;reference test;refine some/other/node;status current;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_uses(YCTX, NULL, (struct lysp_node **)&u));
+ CHECK_LYSP_NODE(u, "test", 1, LYS_STATUS_CURR, 1, "grpref", 0, LYS_USES, 0, "test", 1);
+ assert_non_null(u->augments);
+ assert_non_null(u->refines);
+ lysp_node_free(&fctx, (struct lysp_node *)u); u = NULL;
+}
+
+static void
+test_augment(void **state)
+{
+ struct lysp_node_augment *a = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_augment(YCTX, NULL, &a)); \
+ CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", "Line number 1."); \
+ lysp_node_free(&fctx, (struct lysp_node *)a); a = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ in.current = "/target/nodeid {action x; anydata any;anyxml anyxml; case cs; choice ch;container c;description test;if-feature f;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;notification not;reference test;status current;uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_augment(YCTX, NULL, &a));
+ assert_non_null(a);
+ assert_int_equal(LYS_AUGMENT, a->nodetype);
+ assert_string_equal("/target/nodeid", a->nodeid);
+ assert_string_equal("test", a->dsc);
+ assert_non_null(a->exts);
+ assert_non_null(a->iffeatures);
+ assert_string_equal("test", a->ref);
+ assert_non_null(a->when);
+ assert_null(a->parent);
+ assert_int_equal(LYS_STATUS_CURR, a->flags);
+ lysp_node_free(&fctx, (struct lysp_node *)a); a = NULL;
+}
+
+static void
+test_when(void **state)
+{
+ struct lysp_when *w = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */
+
+ in.current = "l { description text1;description text2;} ...";
+ assert_int_equal(LY_EVALID, parse_when(YCTX, &w));
+ assert_null(w);
+ CHECK_LOG_CTX("Duplicate keyword \"description\".", "Line number 1.");
+
+ in.current = "l { reference 1;reference 2;} ...";
+ assert_int_equal(LY_EVALID, parse_when(YCTX, &w));
+ assert_null(w);
+ CHECK_LOG_CTX("Duplicate keyword \"reference\".", "Line number 1.");
+}
+
+static void
+test_value(void **state)
+{
+ struct lysp_type_enum enm;
+
+ in.current = "-0;";
+ memset(&enm, 0, sizeof enm);
+ assert_int_equal(parse_type_enum_value_pos(YCTX, LY_STMT_VALUE, &enm), LY_SUCCESS);
+
+ in.current = "-0;";
+ memset(&enm, 0, sizeof enm);
+ assert_int_equal(parse_type_enum_value_pos(YCTX, LY_STMT_POSITION, &enm), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"-0\" of \"position\".", "Line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_helpers, setup, teardown),
+ UTEST(test_comments, setup, teardown),
+ UTEST(test_arg, setup, teardown),
+ UTEST(test_stmts, setup, teardown),
+ UTEST(test_minmax, setup, teardown),
+ UTEST(test_valid_module, setup, teardown),
+ UTEST(test_module, setup, teardown),
+ UTEST(test_deviation, setup, teardown),
+ UTEST(test_deviate, setup, teardown),
+ UTEST(test_container, setup, teardown),
+ UTEST(test_leaf, setup, teardown),
+ UTEST(test_leaflist, setup, teardown),
+ UTEST(test_list, setup, teardown),
+ UTEST(test_choice, setup, teardown),
+ UTEST(test_case, setup, teardown),
+ UTEST(test_anydata, setup, teardown),
+ UTEST(test_anyxml, setup, teardown),
+ UTEST(test_action, setup, teardown),
+ UTEST(test_notification, setup, teardown),
+ UTEST(test_grouping, setup, teardown),
+ UTEST(test_uses, setup, teardown),
+ UTEST(test_augment, setup, teardown),
+ UTEST(test_when, setup, teardown),
+ UTEST(test_value, setup, teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/schema/test_yin.c b/tests/utests/schema/test_yin.c
new file mode 100644
index 0000000..0ce3abc
--- /dev/null
+++ b/tests/utests/schema/test_yin.c
@@ -0,0 +1,3584 @@
+/**
+ * @file test_yin.c
+ * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for YIN parser and printer
+ *
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "in.h"
+#include "parser_internal.h"
+#include "schema_compile.h"
+#include "tree.h"
+#include "tree_edit.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+#include "xpath.h"
+
+/* copied from parser_yin.c */
+enum yin_argument {
+ YIN_ARG_UNKNOWN = 0, /**< parsed argument can not be matched with any supported yin argument keyword */
+ YIN_ARG_NAME, /**< argument name */
+ YIN_ARG_TARGET_NODE, /**< argument target-node */
+ YIN_ARG_MODULE, /**< argument module */
+ YIN_ARG_VALUE, /**< argument value */
+ YIN_ARG_TEXT, /**< argument text */
+ YIN_ARG_CONDITION, /**< argument condition */
+ YIN_ARG_URI, /**< argument uri */
+ YIN_ARG_DATE, /**< argument data */
+ YIN_ARG_TAG, /**< argument tag */
+ YIN_ARG_NONE /**< empty (special value) */
+};
+
+struct yin_subelement {
+ enum ly_stmt type; /**< type of keyword */
+ void *dest; /**< meta infromation passed to responsible function (mostly information about where parsed subelement should be stored) */
+ uint16_t flags; /**< describes constraints of subelement can be set to YIN_SUBELEM_MANDATORY, YIN_SUBELEM_UNIQUE, YIN_SUBELEM_FIRST, YIN_SUBELEM_VER2, and YIN_SUBELEM_DEFAULT_TEXT */
+};
+
+struct import_meta {
+ const char *prefix; /**< module prefix. */
+ struct lysp_import **imports; /**< imports to add to. */
+};
+
+struct yin_argument_meta {
+ uint16_t *flags; /**< Argument flags */
+ const char **argument; /**< Argument value */
+};
+
+struct tree_node_meta {
+ struct lysp_node *parent; /**< parent node */
+ struct lysp_node **nodes; /**< linked list of siblings */
+};
+
+struct include_meta {
+ const char *name; /**< Module/submodule name. */
+ struct lysp_include **includes; /**< [Sized array](@ref sizedarrays) of parsed includes to add to. */
+};
+
+struct inout_meta {
+ struct lysp_node *parent; /**< Parent node. */
+ struct lysp_node_action_inout *inout_p; /**< inout_p Input/output pointer to write to. */
+};
+
+struct minmax_dev_meta {
+ uint32_t *lim; /**< min/max value to write to. */
+ uint16_t *flags; /**< min/max flags to write to. */
+ struct lysp_ext_instance **exts; /**< extension instances to add to. */
+};
+
+#define YIN_SUBELEM_MANDATORY 0x01
+#define YIN_SUBELEM_UNIQUE 0x02
+#define YIN_SUBELEM_FIRST 0x04
+#define YIN_SUBELEM_VER2 0x08
+
+#define YIN_SUBELEM_PARSED 0x80
+
+/* prototypes of static functions */
+enum yin_argument yin_match_argument_name(const char *name, size_t len);
+
+LY_ERR yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info, size_t subelem_info_size,
+ const void *parent, enum ly_stmt parent_stmt, const char **text_content, struct lysp_ext_instance **exts);
+LY_ERR yin_validate_value(struct lysp_yin_ctx *ctx, enum yang_arg val_type);
+enum ly_stmt yin_match_keyword(struct lysp_yin_ctx *ctx, const char *name, size_t name_len,
+ const char *prefix, size_t prefix_len, enum ly_stmt parrent);
+
+LY_ERR yin_parse_extension_instance(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt,
+ LY_ARRAY_COUNT_TYPE parent_stmt_index, struct lysp_ext_instance **exts);
+LY_ERR yin_parse_element_generic(struct lysp_yin_ctx *ctx, enum ly_stmt parent, struct lysp_stmt **element);
+LY_ERR yin_parse_mod(struct lysp_yin_ctx *ctx, struct lysp_module *mod);
+LY_ERR yin_parse_submod(struct lysp_yin_ctx *ctx, struct lysp_submodule *submod);
+
+/* wrapping element used for mocking has nothing to do with real module structure */
+#define ELEMENT_WRAPPER_START "<status xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+#define ELEMENT_WRAPPER_END "</status>"
+
+#define TEST_1_CHECK_LYSP_EXT_INSTANCE(NODE, INSUBSTMT)\
+ CHECK_LYSP_EXT_INSTANCE((NODE), NULL, 1, INSUBSTMT, 0, "myext:c-define", LY_VALUE_XML)
+
+struct lysp_yin_ctx *YCTX;
+struct lysf_ctx fctx;
+
+static int
+setup_ctx(void **state)
+{
+ struct lysp_module *pmod;
+
+ /* allocate parser context */
+ YCTX = calloc(1, sizeof(*YCTX));
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+ YCTX->format = LYS_IN_YIN;
+ ly_set_new(&YCTX->parsed_mods);
+
+ /* allocate new parsed module */
+ pmod = calloc(1, sizeof *pmod);
+ ly_set_add(YCTX->parsed_mods, pmod, 1, NULL);
+
+ /* allocate new module */
+ pmod->mod = calloc(1, sizeof *pmod->mod);
+ pmod->mod->ctx = UTEST_LYCTX;
+ pmod->mod->parsed = pmod;
+
+ return 0;
+}
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ setup_ctx(state);
+
+ fctx.ctx = UTEST_LYCTX;
+ fctx.mod = PARSER_CUR_PMOD(YCTX)->mod;
+
+ return 0;
+}
+
+static int
+teardown_ctx(void **UNUSED(state))
+{
+ lys_module_free(&fctx, PARSER_CUR_PMOD(YCTX)->mod, 0);
+ lysp_yin_ctx_free(YCTX);
+ YCTX = NULL;
+
+ return 0;
+}
+
+static int
+teardown(void **state)
+{
+ teardown_ctx(state);
+
+ lysf_ctx_erase(&fctx);
+
+ UTEST_TEARDOWN;
+
+ return 0;
+}
+
+#define RESET_STATE \
+ ly_in_free(UTEST_IN, 0); \
+ UTEST_IN = NULL; \
+ teardown_ctx(state); \
+ setup_ctx(state)
+
+static void
+test_yin_match_keyword(void **state)
+{
+ const char *prefix;
+ size_t prefix_len;
+
+ /* create mock yin namespace in xml context */
+ ly_in_new_memory("<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" />", &UTEST_IN);
+ lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx);
+ prefix = YCTX->xmlctx->prefix;
+ prefix_len = YCTX->xmlctx->prefix_len;
+
+ assert_int_equal(yin_match_keyword(YCTX, "anydatax", strlen("anydatax"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE);
+ assert_int_equal(yin_match_keyword(YCTX, "asdasd", strlen("asdasd"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE);
+ assert_int_equal(yin_match_keyword(YCTX, "", 0, prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE);
+ assert_int_equal(yin_match_keyword(YCTX, "anydata", strlen("anydata"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ANYDATA);
+ assert_int_equal(yin_match_keyword(YCTX, "anyxml", strlen("anyxml"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ANYXML);
+ assert_int_equal(yin_match_keyword(YCTX, "argument", strlen("argument"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ARGUMENT);
+ assert_int_equal(yin_match_keyword(YCTX, "augment", strlen("augment"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_AUGMENT);
+ assert_int_equal(yin_match_keyword(YCTX, "base", strlen("base"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BASE);
+ assert_int_equal(yin_match_keyword(YCTX, "belongs-to", strlen("belongs-to"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BELONGS_TO);
+ assert_int_equal(yin_match_keyword(YCTX, "bit", strlen("bit"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BIT);
+ assert_int_equal(yin_match_keyword(YCTX, "case", strlen("case"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CASE);
+ assert_int_equal(yin_match_keyword(YCTX, "choice", strlen("choice"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CHOICE);
+ assert_int_equal(yin_match_keyword(YCTX, "config", strlen("config"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONFIG);
+ assert_int_equal(yin_match_keyword(YCTX, "contact", strlen("contact"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONTACT);
+ assert_int_equal(yin_match_keyword(YCTX, "container", strlen("container"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONTAINER);
+ assert_int_equal(yin_match_keyword(YCTX, "default", strlen("default"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEFAULT);
+ assert_int_equal(yin_match_keyword(YCTX, "description", strlen("description"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DESCRIPTION);
+ assert_int_equal(yin_match_keyword(YCTX, "deviate", strlen("deviate"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEVIATE);
+ assert_int_equal(yin_match_keyword(YCTX, "deviation", strlen("deviation"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEVIATION);
+ assert_int_equal(yin_match_keyword(YCTX, "enum", strlen("enum"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ENUM);
+ assert_int_equal(yin_match_keyword(YCTX, "error-app-tag", strlen("error-app-tag"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ERROR_APP_TAG);
+ assert_int_equal(yin_match_keyword(YCTX, "error-message", strlen("error-message"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ERROR_MESSAGE);
+ assert_int_equal(yin_match_keyword(YCTX, "extension", strlen("extension"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_EXTENSION);
+ assert_int_equal(yin_match_keyword(YCTX, "feature", strlen("feature"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_FEATURE);
+ assert_int_equal(yin_match_keyword(YCTX, "fraction-digits", strlen("fraction-digits"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_FRACTION_DIGITS);
+ assert_int_equal(yin_match_keyword(YCTX, "grouping", strlen("grouping"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_GROUPING);
+ assert_int_equal(yin_match_keyword(YCTX, "identity", strlen("identity"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IDENTITY);
+ assert_int_equal(yin_match_keyword(YCTX, "if-feature", strlen("if-feature"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IF_FEATURE);
+ assert_int_equal(yin_match_keyword(YCTX, "import", strlen("import"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IMPORT);
+ assert_int_equal(yin_match_keyword(YCTX, "include", strlen("include"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_INCLUDE);
+ assert_int_equal(yin_match_keyword(YCTX, "input", strlen("input"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_INPUT);
+ assert_int_equal(yin_match_keyword(YCTX, "key", strlen("key"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_KEY);
+ assert_int_equal(yin_match_keyword(YCTX, "leaf", strlen("leaf"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LEAF);
+ assert_int_equal(yin_match_keyword(YCTX, "leaf-list", strlen("leaf-list"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LEAF_LIST);
+ assert_int_equal(yin_match_keyword(YCTX, "length", strlen("length"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LENGTH);
+ assert_int_equal(yin_match_keyword(YCTX, "list", strlen("list"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LIST);
+ assert_int_equal(yin_match_keyword(YCTX, "mandatory", strlen("mandatory"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MANDATORY);
+ assert_int_equal(yin_match_keyword(YCTX, "max-elements", strlen("max-elements"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MAX_ELEMENTS);
+ assert_int_equal(yin_match_keyword(YCTX, "min-elements", strlen("min-elements"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MIN_ELEMENTS);
+ assert_int_equal(yin_match_keyword(YCTX, "modifier", strlen("modifier"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MODIFIER);
+ assert_int_equal(yin_match_keyword(YCTX, "module", strlen("module"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MODULE);
+ assert_int_equal(yin_match_keyword(YCTX, "must", strlen("must"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MUST);
+ assert_int_equal(yin_match_keyword(YCTX, "namespace", strlen("namespace"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NAMESPACE);
+ assert_int_equal(yin_match_keyword(YCTX, "notification", strlen("notification"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NOTIFICATION);
+ assert_int_equal(yin_match_keyword(YCTX, "ordered-by", strlen("ordered-by"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ORDERED_BY);
+ assert_int_equal(yin_match_keyword(YCTX, "organization", strlen("organization"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ORGANIZATION);
+ assert_int_equal(yin_match_keyword(YCTX, "output", strlen("output"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_OUTPUT);
+ assert_int_equal(yin_match_keyword(YCTX, "path", strlen("path"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PATH);
+ assert_int_equal(yin_match_keyword(YCTX, "pattern", strlen("pattern"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PATTERN);
+ assert_int_equal(yin_match_keyword(YCTX, "position", strlen("position"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_POSITION);
+ assert_int_equal(yin_match_keyword(YCTX, "prefix", strlen("prefix"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PREFIX);
+ assert_int_equal(yin_match_keyword(YCTX, "presence", strlen("presence"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PRESENCE);
+ assert_int_equal(yin_match_keyword(YCTX, "range", strlen("range"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_RANGE);
+ assert_int_equal(yin_match_keyword(YCTX, "reference", strlen("reference"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REFERENCE);
+ assert_int_equal(yin_match_keyword(YCTX, "refine", strlen("refine"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REFINE);
+ assert_int_equal(yin_match_keyword(YCTX, "require-instance", strlen("require-instance"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REQUIRE_INSTANCE);
+ assert_int_equal(yin_match_keyword(YCTX, "revision", strlen("revision"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REVISION);
+ assert_int_equal(yin_match_keyword(YCTX, "revision-date", strlen("revision-date"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REVISION_DATE);
+ assert_int_equal(yin_match_keyword(YCTX, "rpc", strlen("rpc"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_RPC);
+ assert_int_equal(yin_match_keyword(YCTX, "status", strlen("status"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_STATUS);
+ assert_int_equal(yin_match_keyword(YCTX, "submodule", strlen("submodule"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_SUBMODULE);
+ assert_int_equal(yin_match_keyword(YCTX, "type", strlen("type"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_TYPE);
+ assert_int_equal(yin_match_keyword(YCTX, "typedef", strlen("typedef"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_TYPEDEF);
+ assert_int_equal(yin_match_keyword(YCTX, "unique", strlen("unique"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_UNIQUE);
+ assert_int_equal(yin_match_keyword(YCTX, "units", strlen("units"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_UNITS);
+ assert_int_equal(yin_match_keyword(YCTX, "uses", strlen("uses"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_USES);
+ assert_int_equal(yin_match_keyword(YCTX, "value", strlen("value"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_VALUE);
+ assert_int_equal(yin_match_keyword(YCTX, "when", strlen("when"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_WHEN);
+ assert_int_equal(yin_match_keyword(YCTX, "yang-version", strlen("yang-version"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_YANG_VERSION);
+ assert_int_equal(yin_match_keyword(YCTX, "yin-element", strlen("yin-element"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_YIN_ELEMENT);
+}
+
+static void
+test_yin_match_argument_name(void **UNUSED(state))
+{
+ assert_int_equal(yin_match_argument_name("", 5), YIN_ARG_UNKNOWN);
+ assert_int_equal(yin_match_argument_name("qwertyasd", 5), YIN_ARG_UNKNOWN);
+ assert_int_equal(yin_match_argument_name("conditionasd", 8), YIN_ARG_UNKNOWN);
+ assert_int_equal(yin_match_argument_name("condition", 9), YIN_ARG_CONDITION);
+ assert_int_equal(yin_match_argument_name("date", 4), YIN_ARG_DATE);
+ assert_int_equal(yin_match_argument_name("module", 6), YIN_ARG_MODULE);
+ assert_int_equal(yin_match_argument_name("name", 4), YIN_ARG_NAME);
+ assert_int_equal(yin_match_argument_name("tag", 3), YIN_ARG_TAG);
+ assert_int_equal(yin_match_argument_name("target-node", 11), YIN_ARG_TARGET_NODE);
+ assert_int_equal(yin_match_argument_name("text", 4), YIN_ARG_TEXT);
+ assert_int_equal(yin_match_argument_name("uri", 3), YIN_ARG_URI);
+ assert_int_equal(yin_match_argument_name("value", 5), YIN_ARG_VALUE);
+}
+
+static void
+test_yin_parse_content(void **state)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *data =
+ "<prefix value=\"a_mod\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+ " <myext:custom xmlns:myext=\"urn:example:extensions\">totally amazing extension</myext:custom>\n"
+ " <extension name=\"ext\">\n"
+ " <argument name=\"argname\"></argument>\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"></status>\n"
+ " </extension>\n"
+ " <text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>\n"
+ " <if-feature name=\"foo\"></if-feature>\n"
+ " <when condition=\"condition...\">\n"
+ " <reference><text>when_ref</text></reference>\n"
+ " <description><text>when_desc</text></description>\n"
+ " </when>\n"
+ " <config value=\"true\"/>\n"
+ " <error-message>\n"
+ " <value>error-msg</value>\n"
+ " </error-message>\n"
+ " <error-app-tag value=\"err-app-tag\"/>\n"
+ " <units name=\"radians\"></units>\n"
+ " <default value=\"default-value\"/>\n"
+ " <position value=\"25\"></position>\n"
+ " <value value=\"-5\"/>\n"
+ " <require-instance value=\"true\"></require-instance>\n"
+ " <range value=\"5..10\" />\n"
+ " <length value=\"baf\"/>\n"
+ " <pattern value='pattern'>\n"
+ " <modifier value='invert-match'/>\n"
+ " </pattern>\n"
+ " <enum name=\"yay\">\n"
+ " </enum>\n"
+ "</prefix>";
+ struct lysp_ext_instance *exts = NULL;
+ const char *value;
+
+ /* test unique subelem */
+ const char *prefix_value;
+ struct yin_subelement subelems2[2] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ARG_TEXT, &value, YIN_SUBELEM_UNIQUE}};
+
+ data = ELEMENT_WRAPPER_START
+ "<prefix value=\"inv_mod\" />"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ ELEMENT_WRAPPER_END;
+ ly_in_new_memory(data, &UTEST_IN);
+ lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx);
+ lyxml_ctx_next(YCTX->xmlctx);
+
+ ret = yin_parse_content(YCTX, subelems2, 2, NULL, LY_STMT_STATUS, NULL, &exts);
+ assert_int_equal(ret, LY_EVALID);
+ CHECK_LOG_CTX("Redefinition of \"text\" sub-element in \"status\" element.", "Line number 1.");
+ lydict_remove(UTEST_LYCTX, prefix_value);
+ lydict_remove(UTEST_LYCTX, value);
+ RESET_STATE;
+
+ /* test first subelem */
+ data = ELEMENT_WRAPPER_START
+ "<prefix value=\"inv_mod\" />"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ ELEMENT_WRAPPER_END;
+ struct yin_subelement subelems3[2] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ARG_TEXT, &value, YIN_SUBELEM_FIRST}};
+
+ ly_in_new_memory(data, &UTEST_IN);
+ lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx);
+ lyxml_ctx_next(YCTX->xmlctx);
+
+ ret = yin_parse_content(YCTX, subelems3, 2, NULL, LY_STMT_STATUS, NULL, &exts);
+ assert_int_equal(ret, LY_EVALID);
+ CHECK_LOG_CTX("Sub-element \"text\" of \"status\" element must be defined as it's first sub-element.", "Line number 1.");
+ lydict_remove(UTEST_LYCTX, prefix_value);
+ RESET_STATE;
+
+ /* test mandatory subelem */
+ data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END;
+ struct yin_subelement subelems4[1] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE}};
+
+ ly_in_new_memory(data, &UTEST_IN);
+ lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx);
+ lyxml_ctx_next(YCTX->xmlctx);
+
+ ret = yin_parse_content(YCTX, subelems4, 1, NULL, LY_STMT_STATUS, NULL, &exts);
+ assert_int_equal(ret, LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory sub-element \"prefix\" of \"status\" element.", "Line number 1.");
+}
+
+static void
+test_validate_value(void **state)
+{
+ const char *data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END;
+
+ /* create some XML context */
+ ly_in_new_memory(data, &UTEST_IN);
+ lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx);
+ YCTX->xmlctx->status = LYXML_ELEM_CONTENT;
+ YCTX->xmlctx->dynamic = 0;
+
+ YCTX->xmlctx->value = "#invalid";
+ YCTX->xmlctx->value_len = 8;
+ assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID);
+ CHECK_LOG_CTX("Invalid identifier first character '#' (0x0023).", "Line number 1.");
+
+ YCTX->xmlctx->value = "";
+ YCTX->xmlctx->value_len = 0;
+ assert_int_equal(yin_validate_value(YCTX, Y_STR_ARG), LY_SUCCESS);
+
+ YCTX->xmlctx->value = "pre:b";
+ YCTX->xmlctx->value_len = 5;
+ assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID);
+ assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_SUCCESS);
+
+ YCTX->xmlctx->value = "pre:pre:b";
+ YCTX->xmlctx->value_len = 9;
+ assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_EVALID);
+}
+
+static void
+test_valid_module(void **state)
+{
+ struct lys_module *mod;
+ char *printed;
+ const char *links_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"links\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:mod2=\"urn:module2\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <namespace uri=\"urn:module2\"/>\n"
+ " <prefix value=\"mod2\"/>\n"
+ " <identity name=\"just-another-identity\"/>\n"
+ " <grouping name=\"rgroup\">\n"
+ " <leaf name=\"rg1\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"rg2\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </grouping>\n"
+ " <leaf name=\"one-leaf\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <list name=\"list-for-augment\">\n"
+ " <key value=\"keyleaf\"/>\n"
+ " <leaf name=\"keyleaf\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"just-leaf\">\n"
+ " <type name=\"int32\"/>\n"
+ " </leaf>\n"
+ " </list>\n"
+ " <leaf name=\"rleaf\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf-list name=\"llist\">\n"
+ " <type name=\"string\"/>\n"
+ " <min-elements value=\"0\"/>\n"
+ " <max-elements value=\"100\"/>\n"
+ " <ordered-by value=\"user\"/>\n"
+ " </leaf-list>\n"
+ "</module>\n";
+ const char *statements_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"statements\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:mod=\"urn:module\"\n"
+ " xmlns:mod2=\"urn:module2\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <namespace uri=\"urn:module\"/>\n"
+ " <prefix value=\"mod\"/>\n"
+ " <import module=\"links\">\n"
+ " <prefix value=\"mod2\"/>\n"
+ " </import>\n"
+ " <extension name=\"ext\"/>\n"
+ " <identity name=\"random-identity\">\n"
+ " <base name=\"mod2:just-another-identity\"/>\n"
+ " <base name=\"another-identity\"/>\n"
+ " </identity>\n"
+ " <identity name=\"another-identity\">\n"
+ " <base name=\"mod2:just-another-identity\"/>\n"
+ " </identity>\n"
+ " <typedef name=\"percent\">\n"
+ " <type name=\"uint8\">\n"
+ " <range value=\"0 .. 100\"/>\n"
+ " </type>\n"
+ " <units name=\"percent\"/>\n"
+ " </typedef>\n"
+ " <list name=\"list1\">\n"
+ " <key value=\"a\"/>\n"
+ " <leaf name=\"a\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"x\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"y\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </list>\n"
+ " <container name=\"ice-cream-shop\">\n"
+ " <container name=\"employees\">\n"
+ " <when condition=\"/list1/x\"/>\n"
+ " <list name=\"employee\">\n"
+ " <key value=\"id\"/>\n"
+ " <unique tag=\"name\"/>\n"
+ " <config value=\"true\"/>\n"
+ " <min-elements value=\"0\">\n"
+ " <mod:ext/>\n"
+ " </min-elements>\n"
+ " <max-elements value=\"unbounded\"/>\n"
+ " <leaf name=\"id\">\n"
+ " <type name=\"uint64\"/>\n"
+ " <mandatory value=\"true\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"name\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"age\">\n"
+ " <type name=\"uint32\"/>\n"
+ " </leaf>\n"
+ " </list>\n"
+ " </container>\n"
+ " </container>\n"
+ " <container name=\"random\">\n"
+ " <grouping name=\"group\">\n"
+ " <leaf name=\"g1\">\n"
+ " <type name=\"percent\"/>\n"
+ " <mandatory value=\"false\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"g2\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </grouping>\n"
+ " <choice name=\"switch\">\n"
+ " <case name=\"a\">\n"
+ " <leaf name=\"aleaf\">\n"
+ " <type name=\"string\"/>\n"
+ " <default value=\"aaa\"/>\n"
+ " </leaf>\n"
+ " </case>\n"
+ " <case name=\"c\">\n"
+ " <leaf name=\"cleaf\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </case>\n"
+ " </choice>\n"
+ " <anyxml name=\"xml-data\"/>\n"
+ " <anydata name=\"any-data\"/>\n"
+ " <leaf-list name=\"leaflist\">\n"
+ " <type name=\"string\"/>\n"
+ " <min-elements value=\"0\"/>\n"
+ " <max-elements value=\"20\"/>\n"
+ " </leaf-list>\n"
+ " <uses name=\"group\"/>\n"
+ " <uses name=\"mod2:rgroup\"/>\n"
+ " <leaf name=\"lref\">\n"
+ " <type name=\"leafref\">\n"
+ " <path value=\"/mod2:one-leaf\"/>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " <leaf name=\"iref\">\n"
+ " <type name=\"identityref\">\n"
+ " <base name=\"mod2:just-another-identity\"/>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " </container>\n"
+ " <augment target-node=\"/random\">\n"
+ " <leaf name=\"aug-leaf\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </augment>\n"
+ " <notification name=\"notif\"/>\n"
+ " <deviation target-node=\"/mod:ice-cream-shop/mod:employees/mod:employee/mod:age\">\n"
+ " <deviate value=\"not-supported\">\n"
+ " <mod:ext/>\n"
+ " </deviate>\n"
+ " </deviation>\n"
+ " <deviation target-node=\"/mod:list1\">\n"
+ " <deviate value=\"add\">\n"
+ " <mod:ext/>\n"
+ " <must condition=\"1\"/>\n"
+ " <must condition=\"2\"/>\n"
+ " <unique tag=\"x\"/>\n"
+ " <unique tag=\"y\"/>\n"
+ " <config value=\"true\"/>\n"
+ " <min-elements value=\"1\"/>\n"
+ " <max-elements value=\"2\"/>\n"
+ " </deviate>\n"
+ " </deviation>\n"
+ " <deviation target-node=\"/mod:ice-cream-shop/mod:employees/mod:employee\">\n"
+ " <deviate value=\"delete\">\n"
+ " <unique tag=\"name\"/>\n"
+ " </deviate>\n"
+ " </deviation>\n"
+ " <deviation target-node=\"/mod:random/mod:leaflist\">\n"
+ " <deviate value=\"replace\">\n"
+ " <type name=\"uint32\"/>\n"
+ " <min-elements value=\"10\"/>\n"
+ " <max-elements value=\"15\"/>\n"
+ " </deviate>\n"
+ " </deviation>\n"
+ "</module>\n";
+
+ UTEST_ADD_MODULE(links_yin, LYS_IN_YIN, NULL, NULL);
+ UTEST_ADD_MODULE(statements_yin, LYS_IN_YIN, NULL, &mod);
+ lys_print_mem(&printed, mod, LYS_OUT_YIN, 0);
+ assert_string_equal(printed, statements_yin);
+ free(printed);
+}
+
+static void
+test_print_module(void **state)
+{
+ struct lys_module *mod;
+
+ char *orig = malloc(8096);
+
+ strcpy(orig,
+ "module all {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:all\";\n"
+ " prefix all_mod;\n\n"
+ " import ietf-yang-types {\n"
+ " prefix yt;\n"
+ " revision-date 2013-07-15;\n"
+ " description\n"
+ " \"YANG types\";\n"
+ " reference\n"
+ " \"RFC reference\";\n"
+ " }\n\n"
+ " feature feat1 {\n"
+ " if-feature \"feat2\";\n"
+ " status obsolete;\n"
+ " }\n\n"
+ " feature feat2;\n"
+ " feature feat3;\n\n"
+ " identity ident2 {\n"
+ " base ident1;\n"
+ " }\n\n"
+ " identity ident1;\n\n"
+ " typedef tdef1 {\n"
+ " type tdef2 {\n"
+ " length \"3..9 | 30..40\";\n"
+ " pattern \"[ac]*\";\n"
+ " }\n"
+ " units \"none\";\n"
+ " default \"aaa\";\n"
+ " }\n\n"
+ " typedef tdef2 {\n"
+ " type string {\n"
+ " length \"2..10 | 20..50\";\n"
+ " pattern \"[ab]*\";\n"
+ " }\n"
+ " }\n\n"
+ " grouping group1 {\n"
+ " leaf leaf1 {\n"
+ " type int8;\n"
+ " }\n"
+ " }\n\n"
+ " container cont1 {\n"
+ " leaf leaf2 {\n"
+ " if-feature \"feat1\";\n"
+ " type int16;\n"
+ " status obsolete;\n"
+ " }\n\n"
+ " uses group1 {\n"
+ " if-feature \"feat2\";\n"
+ " refine \"leaf1\" {\n"
+ " if-feature \"feat3\";\n"
+ " must \"24 - 4 = number('20')\";\n"
+ " default \"25\";\n"
+ " config true;\n"
+ " mandatory false;\n"
+ " description\n"
+ " \"dsc\";\n"
+ " reference\n"
+ " \"none\";\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf3 {\n"
+ " type int32;\n"
+ " }\n\n"
+ " leaf leaf4 {\n"
+ " type int64 {\n"
+ " range \"1000 .. 50000\" {\n"
+ " error-message\n"
+ " \"Special error message.\";\n"
+ " error-app-tag \"special-tag\";\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf5 {\n"
+ " type uint8;\n"
+ " }\n\n"
+ " leaf leaf6 {\n"
+ " type uint16;\n"
+ " }\n\n"
+ " leaf leaf7 {\n"
+ " type uint32;\n"
+ " }\n\n"
+ " leaf leaf8 {\n"
+ " type uint64;\n"
+ " }\n\n"
+ " choice choic1 {\n"
+ " default \"leaf9b\";\n"
+ " leaf leaf9a {\n"
+ " type decimal64 {\n"
+ " fraction-digits 9;\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf9b {\n"
+ " type boolean;\n"
+ " default \"false\";\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf10 {\n"
+ " type boolean;\n"
+ " }\n\n");
+ strcpy(orig + strlen(orig),
+ " leaf leaf11 {\n"
+ " type enumeration {\n"
+ " enum \"one\";\n"
+ " enum \"two\";\n"
+ " enum \"five\" {\n"
+ " value 5;\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf12 {\n"
+ " type bits {\n"
+ " bit flag0 {\n"
+ " position 0;\n"
+ " }\n"
+ " bit flag1;\n"
+ " bit flag2 {\n"
+ " position 2;\n"
+ " }\n"
+ " bit flag3 {\n"
+ " position 3;\n"
+ " }\n"
+ " }\n"
+ " default \"flag0 flag3\";\n"
+ " }\n\n"
+ " leaf leaf13 {\n"
+ " type binary;\n"
+ " }\n\n"
+ " leaf leaf14 {\n"
+ " type leafref {\n"
+ " path \"/cont1/leaf17\";\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf15 {\n"
+ " type empty;\n"
+ " }\n\n"
+ " leaf leaf16 {\n"
+ " type union {\n"
+ " type instance-identifier {\n"
+ " require-instance true;\n"
+ " }\n"
+ " type int8;\n"
+ " }\n"
+ " }\n\n"
+ " list list1 {\n"
+ " key \"leaf18\";\n"
+ " unique \"leaf19\";\n"
+ " min-elements 1;\n"
+ " max-elements 20;\n"
+ " leaf leaf18 {\n"
+ " type string;\n"
+ " }\n\n"
+ " leaf leaf19 {\n"
+ " type uint32;\n"
+ " }\n\n"
+ " anyxml axml1;\n"
+ " anydata adata1;\n\n"
+ " action act1 {\n"
+ " input {\n"
+ " leaf leaf24 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n\n"
+ " output {\n"
+ " leaf leaf25 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " notification notif1 {\n"
+ " leaf leaf26 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " leaf-list llist1 {\n"
+ " type tdef1;\n"
+ " ordered-by user;\n"
+ " }\n\n"
+ " list list2 {\n"
+ " key \"leaf27 leaf28\";\n"
+ " leaf leaf27 {\n"
+ " type uint8;\n"
+ " }\n\n"
+ " leaf leaf28 {\n"
+ " type uint8;\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf29 {\n"
+ " type instance-identifier;\n"
+ " }\n\n"
+ " container must-deviations-container {\n"
+ " presence \"Allows deviations on the leaf\";\n"
+ " leaf leaf30 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n\n"
+ " leaf leaf23 {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n\n"
+ " augment \"/cont1\" {\n"
+ " leaf leaf17 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n\n"
+ " rpc rpc1 {\n"
+ " input {\n"
+ " leaf leaf20 {\n"
+ " type tdef1;\n"
+ " }\n"
+ " }\n\n"
+ " output {\n"
+ " container cont2 {\n"
+ " leaf leaf21 {\n"
+ " type empty;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " container test-when {\n"
+ " leaf when-check {\n"
+ " type boolean;\n"
+ " }\n\n"
+ " leaf gated-data {\n"
+ " when \"../when-check = 'true'\";\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n\n"
+ " extension c-define {\n"
+ " description\n"
+ " \"Takes as an argument a name string.\n"
+ " Makes the code generator use the given name\n"
+ " in the #define.\";\n"
+ " argument \"name\";\n"
+ " }\n"
+ "}\n");
+
+ char *ori_res = malloc(8096);
+
+ strcpy(ori_res,
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"all\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:all_mod=\"urn:all\"\n"
+ " xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <namespace uri=\"urn:all\"/>\n"
+ " <prefix value=\"all_mod\"/>\n"
+ " <import module=\"ietf-yang-types\">\n"
+ " <prefix value=\"yt\"/>\n"
+ " <revision-date date=\"2013-07-15\"/>\n"
+ " <description>\n"
+ " <text>YANG types</text>\n"
+ " </description>\n"
+ " <reference>\n"
+ " <text>RFC reference</text>\n"
+ " </reference>\n"
+ " </import>\n"
+ " <extension name=\"c-define\">\n"
+ " <argument name=\"name\"/>\n"
+ " <description>\n"
+ " <text>Takes as an argument a name string.\n"
+ "Makes the code generator use the given name\n"
+ "in the #define.</text>\n"
+ " </description>\n"
+ " </extension>\n"
+ " <feature name=\"feat1\">\n"
+ " <if-feature name=\"feat2\"/>\n"
+ " <status value=\"obsolete\"/>\n"
+ " </feature>\n"
+ " <feature name=\"feat2\"/>\n"
+ " <feature name=\"feat3\"/>\n"
+ " <identity name=\"ident2\">\n"
+ " <base name=\"ident1\"/>\n"
+ " </identity>\n"
+ " <identity name=\"ident1\"/>\n"
+ " <typedef name=\"tdef1\">\n"
+ " <type name=\"tdef2\">\n"
+ " <length value=\"3..9 | 30..40\"/>\n"
+ " <pattern value=\"[ac]*\"/>\n"
+ " </type>\n"
+ " <units name=\"none\"/>\n"
+ " <default value=\"aaa\"/>\n"
+ " </typedef>\n"
+ " <typedef name=\"tdef2\">\n"
+ " <type name=\"string\">\n"
+ " <length value=\"2..10 | 20..50\"/>\n"
+ " <pattern value=\"[ab]*\"/>\n"
+ " </type>\n"
+ " </typedef>\n"
+ " <grouping name=\"group1\">\n"
+ " <leaf name=\"leaf1\">\n"
+ " <type name=\"int8\"/>\n"
+ " </leaf>\n"
+ " </grouping>\n"
+ " <container name=\"cont1\">\n"
+ " <leaf name=\"leaf2\">\n"
+ " <if-feature name=\"feat1\"/>\n"
+ " <type name=\"int16\"/>\n"
+ " <status value=\"obsolete\"/>\n"
+ " </leaf>\n"
+ " <uses name=\"group1\">\n"
+ " <if-feature name=\"feat2\"/>\n"
+ " <refine target-node=\"leaf1\">\n"
+ " <if-feature name=\"feat3\"/>\n"
+ " <must condition=\"24 - 4 = number('20')\"/>\n"
+ " <default value=\"25\"/>\n"
+ " <config value=\"true\"/>\n"
+ " <mandatory value=\"false\"/>\n"
+ " <description>\n"
+ " <text>dsc</text>\n"
+ " </description>\n"
+ " <reference>\n"
+ " <text>none</text>\n"
+ " </reference>\n"
+ " </refine>\n"
+ " </uses>\n"
+ " <leaf name=\"leaf3\">\n"
+ " <type name=\"int32\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf4\">\n"
+ " <type name=\"int64\">\n"
+ " <range value=\"1000 .. 50000\">\n"
+ " <error-message>\n"
+ " <value>Special error message.</value>\n"
+ " </error-message>\n"
+ " <error-app-tag value=\"special-tag\"/>\n"
+ " </range>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf5\">\n"
+ " <type name=\"uint8\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf6\">\n"
+ " <type name=\"uint16\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf7\">\n"
+ " <type name=\"uint32\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf8\">\n"
+ " <type name=\"uint64\"/>\n"
+ " </leaf>\n"
+ " <choice name=\"choic1\">\n"
+ " <default value=\"leaf9b\"/>\n"
+ " <leaf name=\"leaf9a\">\n"
+ " <type name=\"decimal64\">\n"
+ " <fraction-digits value=\"9\"/>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf9b\">\n"
+ " <type name=\"boolean\"/>\n"
+ " <default value=\"false\"/>\n"
+ " </leaf>\n"
+ " </choice>\n"
+ " <leaf name=\"leaf10\">\n"
+ " <type name=\"boolean\"/>\n"
+ " </leaf>\n");
+ strcpy(ori_res + strlen(ori_res),
+ " <leaf name=\"leaf11\">\n"
+ " <type name=\"enumeration\">\n"
+ " <enum name=\"one\"/>\n"
+ " <enum name=\"two\"/>\n"
+ " <enum name=\"five\">\n"
+ " <value value=\"5\"/>\n"
+ " </enum>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf12\">\n"
+ " <type name=\"bits\">\n"
+ " <bit name=\"flag0\">\n"
+ " <position value=\"0\"/>\n"
+ " </bit>\n"
+ " <bit name=\"flag1\"/>\n"
+ " <bit name=\"flag2\">\n"
+ " <position value=\"2\"/>\n"
+ " </bit>\n"
+ " <bit name=\"flag3\">\n"
+ " <position value=\"3\"/>\n"
+ " </bit>\n"
+ " </type>\n"
+ " <default value=\"flag0 flag3\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf13\">\n"
+ " <type name=\"binary\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf14\">\n"
+ " <type name=\"leafref\">\n"
+ " <path value=\"/cont1/leaf17\"/>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf15\">\n"
+ " <type name=\"empty\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf16\">\n"
+ " <type name=\"union\">\n"
+ " <type name=\"instance-identifier\">\n"
+ " <require-instance value=\"true\"/>\n"
+ " </type>\n"
+ " <type name=\"int8\"/>\n"
+ " </type>\n"
+ " </leaf>\n"
+ " <list name=\"list1\">\n"
+ " <key value=\"leaf18\"/>\n"
+ " <unique tag=\"leaf19\"/>\n"
+ " <min-elements value=\"1\"/>\n"
+ " <max-elements value=\"20\"/>\n"
+ " <leaf name=\"leaf18\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf19\">\n"
+ " <type name=\"uint32\"/>\n"
+ " </leaf>\n"
+ " <anyxml name=\"axml1\"/>\n"
+ " <anydata name=\"adata1\"/>\n"
+ " <action name=\"act1\">\n"
+ " <input>\n"
+ " <leaf name=\"leaf24\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </input>\n"
+ " <output>\n"
+ " <leaf name=\"leaf25\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </output>\n"
+ " </action>\n"
+ " <notification name=\"notif1\">\n"
+ " <leaf name=\"leaf26\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </notification>\n"
+ " </list>\n"
+ " <leaf-list name=\"llist1\">\n"
+ " <type name=\"tdef1\"/>\n"
+ " <ordered-by value=\"user\"/>\n"
+ " </leaf-list>\n"
+ " <list name=\"list2\">\n"
+ " <key value=\"leaf27 leaf28\"/>\n"
+ " <leaf name=\"leaf27\">\n"
+ " <type name=\"uint8\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"leaf28\">\n"
+ " <type name=\"uint8\"/>\n"
+ " </leaf>\n"
+ " </list>\n"
+ " <leaf name=\"leaf29\">\n"
+ " <type name=\"instance-identifier\"/>\n"
+ " </leaf>\n"
+ " <container name=\"must-deviations-container\">\n"
+ " <presence value=\"Allows deviations on the leaf\"/>\n"
+ " <leaf name=\"leaf30\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </container>\n"
+ " <leaf name=\"leaf23\">\n"
+ " <type name=\"empty\"/>\n"
+ " </leaf>\n"
+ " </container>\n"
+ " <container name=\"test-when\">\n"
+ " <leaf name=\"when-check\">\n"
+ " <type name=\"boolean\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"gated-data\">\n"
+ " <when condition=\"../when-check = 'true'\"/>\n"
+ " <type name=\"uint16\"/>\n"
+ " </leaf>\n"
+ " </container>\n"
+ " <augment target-node=\"/cont1\">\n"
+ " <leaf name=\"leaf17\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " </augment>\n"
+ " <rpc name=\"rpc1\">\n"
+ " <input>\n"
+ " <leaf name=\"leaf20\">\n"
+ " <type name=\"tdef1\"/>\n"
+ " </leaf>\n"
+ " </input>\n"
+ " <output>\n"
+ " <container name=\"cont2\">\n"
+ " <leaf name=\"leaf21\">\n"
+ " <type name=\"empty\"/>\n"
+ " </leaf>\n"
+ " </container>\n"
+ " </output>\n"
+ " </rpc>\n"
+ "</module>\n");
+
+ char *printed;
+ struct ly_out *out;
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out));
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YIN, 0, 0));
+ assert_int_equal(strlen(ori_res), ly_out_printed(out));
+ assert_string_equal(printed, ori_res);
+
+ ly_out_free(out, NULL, 1);
+ free(orig);
+ free(ori_res);
+}
+
+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))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YIN;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_print_submodule(void **state)
+{
+ struct lys_module *mod;
+
+ const char *mod_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"a\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a_mod=\"urn:a\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <namespace uri=\"urn:a\"/>\n"
+ " <prefix value=\"a_mod\"/>\n"
+ " <include module=\"a-sub\"/>\n"
+ "</module>\n";
+
+ char *submod_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<submodule name=\"a-sub\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a_mod=\"urn:a\"\n"
+ " xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <belongs-to module=\"a\">\n"
+ " <prefix value=\"a_mod\"/>\n"
+ " </belongs-to>\n"
+ " <import module=\"ietf-yang-types\">\n"
+ " <prefix value=\"yt\"/>\n"
+ " <revision-date date=\"2013-07-15\"/>\n"
+ " </import>\n\n"
+ " <description>\n"
+ " <text>YANG types</text>\n"
+ " </description>\n"
+ " <reference>\n"
+ " <text>RFC reference</text>\n"
+ " </reference>\n"
+ "</submodule>\n";
+
+ char *printed;
+ struct ly_out *out;
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out));
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod_yin);
+
+ UTEST_ADD_MODULE(mod_yin, LYS_IN_YIN, NULL, &mod);
+ assert_int_equal(LY_SUCCESS, lys_print_submodule(out, mod->parsed->includes[0].submodule, LYS_OUT_YIN, 0, 0));
+ assert_int_equal(strlen(submod_yin), ly_out_printed(out));
+ assert_string_equal(printed, submod_yin);
+
+ ly_out_free(out, NULL, 1);
+}
+
+/* helper function to simplify unit test of each element using parse_content function */
+LY_ERR
+test_element_helper(void **state, const char *data, void *dest, const char **text, struct lysp_ext_instance **exts)
+{
+ const char *name, *prefix;
+ size_t name_len, prefix_len;
+ LY_ERR ret = LY_SUCCESS;
+ struct yin_subelement subelems[71] = {
+ {LY_STMT_ACTION, dest, 0},
+ {LY_STMT_ANYDATA, dest, 0},
+ {LY_STMT_ANYXML, dest, 0},
+ {LY_STMT_ARGUMENT, dest, 0},
+ {LY_STMT_AUGMENT, dest, 0},
+ {LY_STMT_BASE, dest, 0},
+ {LY_STMT_BELONGS_TO, dest, 0},
+ {LY_STMT_BIT, dest, 0},
+ {LY_STMT_CASE, dest, 0},
+ {LY_STMT_CHOICE, dest, 0},
+ {LY_STMT_CONFIG, dest, 0},
+ {LY_STMT_CONTACT, dest, 0},
+ {LY_STMT_CONTAINER, dest, 0},
+ {LY_STMT_DEFAULT, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_DESCRIPTION, dest, 0},
+ {LY_STMT_DEVIATE, dest, 0},
+ {LY_STMT_DEVIATION, dest, 0},
+ {LY_STMT_ENUM, dest, 0},
+ {LY_STMT_ERROR_APP_TAG, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_ERROR_MESSAGE, dest, 0},
+ {LY_STMT_EXTENSION, dest, 0},
+ {LY_STMT_FEATURE, dest, 0},
+ {LY_STMT_FRACTION_DIGITS, dest, 0},
+ {LY_STMT_GROUPING, dest, 0},
+ {LY_STMT_IDENTITY, dest, 0},
+ {LY_STMT_IF_FEATURE, dest, 0},
+ {LY_STMT_IMPORT, dest, 0},
+ {LY_STMT_INCLUDE, dest, 0},
+ {LY_STMT_INPUT, dest, 0},
+ {LY_STMT_KEY, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_LEAF, dest, 0},
+ {LY_STMT_LEAF_LIST, dest, 0},
+ {LY_STMT_LENGTH, dest, 0},
+ {LY_STMT_LIST, dest, 0},
+ {LY_STMT_MANDATORY, dest, 0},
+ {LY_STMT_MAX_ELEMENTS, dest, 0},
+ {LY_STMT_MIN_ELEMENTS, dest, 0},
+ {LY_STMT_MODIFIER, dest, 0},
+ {LY_STMT_MODULE, dest, 0},
+ {LY_STMT_MUST, dest, 0},
+ {LY_STMT_NAMESPACE, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_NOTIFICATION, dest, 0},
+ {LY_STMT_ORDERED_BY, dest, 0},
+ {LY_STMT_ORGANIZATION, dest, 0},
+ {LY_STMT_OUTPUT, dest, 0},
+ {LY_STMT_PATH, dest, 0},
+ {LY_STMT_PATTERN, dest, 0},
+ {LY_STMT_POSITION, dest, 0},
+ {LY_STMT_PREFIX, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_PRESENCE, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_RANGE, dest, 0},
+ {LY_STMT_REFERENCE, dest, 0},
+ {LY_STMT_REFINE, dest, 0},
+ {LY_STMT_REQUIRE_INSTANCE, dest, 0},
+ {LY_STMT_REVISION, dest, 0},
+ {LY_STMT_REVISION_DATE, dest, 0},
+ {LY_STMT_RPC, dest, 0},
+ {LY_STMT_STATUS, dest, 0},
+ {LY_STMT_SUBMODULE, dest, 0},
+ {LY_STMT_TYPE, dest, 0},
+ {LY_STMT_TYPEDEF, dest, 0},
+ {LY_STMT_UNIQUE, dest, 0},
+ {LY_STMT_UNITS, dest, YIN_SUBELEM_UNIQUE},
+ {LY_STMT_USES, dest, 0},
+ {LY_STMT_VALUE, dest, 0},
+ {LY_STMT_WHEN, dest, 0},
+ {LY_STMT_YANG_VERSION, dest, 0},
+ {LY_STMT_YIN_ELEMENT, dest, 0},
+ {LY_STMT_EXTENSION_INSTANCE, dest, 0},
+ {LY_STMT_ARG_TEXT, dest, 0},
+ {LY_STMT_ARG_VALUE, dest, 0}
+ };
+
+ YCTX->main_ctx = (struct lysp_ctx *)YCTX;
+ ly_in_new_memory(data, &UTEST_IN);
+ lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx);
+ prefix = YCTX->xmlctx->prefix;
+ prefix_len = YCTX->xmlctx->prefix_len;
+ name = YCTX->xmlctx->name;
+ name_len = YCTX->xmlctx->name_len;
+ lyxml_ctx_next(YCTX->xmlctx);
+
+ ret = yin_parse_content(YCTX, subelems, 71, NULL,
+ yin_match_keyword(YCTX, name, name_len, prefix, prefix_len, LY_STMT_NONE), text, exts);
+
+ /* free parser and input */
+ lyxml_ctx_free(YCTX->xmlctx);
+ YCTX->xmlctx = NULL;
+ ly_in_free(UTEST_IN, 0);
+ UTEST_IN = NULL;
+ return ret;
+}
+
+#define EXT_SUBELEM "<myext:c-define name=\"MY_MTU\" xmlns:myext=\"urn:example:extensions\"/>"
+
+static void
+test_enum_elem(void **state)
+{
+ struct lysp_type type = {0};
+ const char *data;
+
+ data = ELEMENT_WRAPPER_START
+ "<enum name=\"enum-name\">\n"
+ " <if-feature name=\"feature\" />\n"
+ " <value value=\"55\" />\n"
+ " <status value=\"deprecated\" />\n"
+ " <description><text>desc...</text></description>\n"
+ " <reference><text>ref...</text></reference>\n"
+ " " EXT_SUBELEM "\n"
+ "</enum>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ uint16_t flags = LYS_STATUS_DEPRC | LYS_SET_VALUE;
+
+ CHECK_LYSP_TYPE_ENUM(type.enums, "desc...", 1, flags, 1, "enum-name", "ref...", 55);
+ assert_string_equal(type.enums->iffeatures[0].str, "feature");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(type.enums->exts, LY_STMT_ENUM);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof type);
+
+ data = ELEMENT_WRAPPER_START
+ "<enum name=\"enum-name\"></enum>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(type.enums->name, "enum-name");
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof type);
+}
+
+static void
+test_bit_elem(void **state)
+{
+ struct lysp_type type = {0};
+ const char *data;
+
+ data = ELEMENT_WRAPPER_START
+ "<bit name=\"bit-name\">\n"
+ " <if-feature name=\"feature\" />\n"
+ " <position value=\"55\" />\n"
+ " <status value=\"deprecated\" />\n"
+ " <description><text>desc...</text></description>\n"
+ " <reference><text>ref...</text></reference>\n"
+ EXT_SUBELEM
+ "</bit>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ uint16_t flags = LYS_STATUS_DEPRC | LYS_SET_VALUE;
+
+ CHECK_LYSP_TYPE_ENUM(type.bits, "desc...", 1, flags, 1, "bit-name", "ref...", 55);
+ assert_string_equal(type.bits->iffeatures[0].str, "feature");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(type.bits->exts, LY_STMT_BIT);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof type);
+
+ data = ELEMENT_WRAPPER_START
+ "<bit name=\"bit-name\"> </bit>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_TYPE_ENUM(type.bits, NULL, 0, 0, 0, "bit-name", NULL, 0);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof type);
+}
+
+static void
+test_status_elem(void **state)
+{
+ const char *data;
+ uint16_t flags = 0;
+
+ /* test invalid value */
+ data = ELEMENT_WRAPPER_START "<status value=\"invalid\"></status>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"status\" element. "
+ "Valid values are \"current\", \"deprecated\" and \"obsolete\".", "Line number 1.");
+}
+
+static void
+test_yin_element_elem(void **state)
+{
+ const char *data;
+ uint16_t flags = 0;
+
+ data = ELEMENT_WRAPPER_START "<yin-element value=\"invalid\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"yin-element\" element. "
+ "Valid values are \"true\" and \"false\".", "Line number 1.");
+}
+
+static void
+test_yangversion_elem(void **state)
+{
+ const char *data;
+ uint8_t version = 0;
+
+ /* invalid value */
+ data = ELEMENT_WRAPPER_START "<yang-version value=\"version\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &version, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"version\" of \"value\" attribute in \"yang-version\" element. "
+ "Valid values are \"1\" and \"1.1\".", "Line number 1.");
+}
+
+static void
+test_argument_elem(void **state)
+{
+ const char *data;
+ uint16_t flags = 0;
+ const char *arg;
+ struct yin_argument_meta arg_meta = {&flags, &arg};
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START
+ "<argument name=\"arg\">"
+ "</argument>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &arg_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(arg, "arg");
+ assert_true(flags == 0);
+ lydict_remove(UTEST_LYCTX, arg);
+}
+
+static void
+test_belongsto_elem(void **state)
+{
+ const char *data;
+ struct lysp_submodule submod;
+
+ lydict_insert(UTEST_LYCTX, "module-name", 0, &PARSER_CUR_PMOD(YCTX)->mod->name);
+
+ data = ELEMENT_WRAPPER_START "<belongs-to module=\"module-name\"></belongs-to>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &submod, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory sub-element \"prefix\" of \"belongs-to\" element.", "Line number 1.");
+}
+
+static void
+test_config_elem(void **state)
+{
+ const char *data;
+ uint16_t flags = 0;
+
+ data = ELEMENT_WRAPPER_START "<config value=\"false\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_SUCCESS);
+ assert_true(flags & LYS_CONFIG_R);
+ flags = 0;
+
+ data = ELEMENT_WRAPPER_START "<config value=\"invalid\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"config\" element. "
+ "Valid values are \"true\" and \"false\".", "Line number 1.");
+}
+
+static void
+test_default_elem(void **state)
+{
+ const char *data;
+ struct lysp_qname val = {0};
+
+ data = ELEMENT_WRAPPER_START "<default/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute value of default element.", "Line number 1.");
+}
+
+static void
+test_err_app_tag_elem(void **state)
+{
+ const char *data;
+ const char *val = NULL;
+
+ data = ELEMENT_WRAPPER_START "<error-app-tag/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute value of error-app-tag element.", "Line number 1.");
+}
+
+static void
+test_err_msg_elem(void **state)
+{
+ const char *data;
+ const char *val = NULL;
+
+ data = ELEMENT_WRAPPER_START "<error-message></error-message>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory sub-element \"value\" of \"error-message\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<error-message invalid=\"text\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Unexpected attribute \"invalid\" of \"error-message\" element.", "Line number 1.");
+}
+
+static void
+test_fracdigits_elem(void **state)
+{
+ const char *data;
+ struct lysp_type type = {0};
+
+ /* invalid values */
+ data = ELEMENT_WRAPPER_START "<fraction-digits value=\"-1\"></fraction-digits>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"fraction-digits\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<fraction-digits value=\"02\"></fraction-digits>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"02\" of \"value\" attribute in \"fraction-digits\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<fraction-digits value=\"1p\"></fraction-digits>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"1p\" of \"value\" attribute in \"fraction-digits\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<fraction-digits value=\"19\"></fraction-digits>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"19\" of \"value\" attribute in \"fraction-digits\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<fraction-digits value=\"999999999999999999\"></fraction-digits>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"999999999999999999\" of \"value\" attribute in \"fraction-digits\" element.", "Line number 1.");
+}
+
+static void
+test_iffeature_elem(void **state)
+{
+ const char *data;
+ const char **iffeatures = NULL;
+
+ data = ELEMENT_WRAPPER_START "<if-feature/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &iffeatures, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute name of if-feature element.", "Line number 1.");
+ LY_ARRAY_FREE(iffeatures);
+ iffeatures = NULL;
+}
+
+static void
+test_length_elem(void **state)
+{
+ const char *data;
+ struct lysp_type type = {0};
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<length value=\"length-str\">\n"
+ " <error-message><value>err-msg</value></error-message>\n"
+ " <error-app-tag value=\"err-app-tag\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ EXT_SUBELEM
+ "</length>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_RESTR(type.length, "length-str", "desc",
+ "err-app-tag", "err-msg", 1, "ref");
+ assert_true(type.flags & LYS_SET_LENGTH);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.length->exts[0]), LY_STMT_LENGTH);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START
+ "<length value=\"length-str\">"
+ "</length>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_RESTR(type.length, "length-str", NULL,
+ NULL, NULL, 0, NULL);
+ lysp_type_free(&fctx, &type);
+ assert_true(type.flags & LYS_SET_LENGTH);
+ memset(&type, 0, sizeof(type));
+
+ data = ELEMENT_WRAPPER_START "<length></length>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute value of length element.", "Line number 1.");
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+}
+
+static void
+test_modifier_elem(void **state)
+{
+ const char *data;
+ const char *pat;
+
+ assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "\006pattern", 8, &pat));
+ data = ELEMENT_WRAPPER_START "<modifier value=\"invert\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &pat, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"invert\" of \"value\" attribute in \"modifier\" element. "
+ "Only valid value is \"invert-match\".", "Line number 1.");
+ lydict_remove(UTEST_LYCTX, pat);
+}
+
+static void
+test_namespace_elem(void **state)
+{
+ const char *data;
+ const char *ns;
+
+ data = ELEMENT_WRAPPER_START "<namespace/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &ns, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute uri of namespace element.", "Line number 1.");
+}
+
+static void
+test_pattern_elem(void **state)
+{
+ const char *data;
+ struct lysp_type type = {0};
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<pattern value=\"super_pattern\">\n"
+ " <modifier value=\"invert-match\"/>\n"
+ " <error-message><value>err-msg-value</value></error-message>\n"
+ " <error-app-tag value=\"err-app-tag-value\"/>\n"
+ " <description><text>&quot;pattern-desc&quot;</text></description>\n"
+ " <reference><text>pattern-ref</text></reference>\n"
+ EXT_SUBELEM
+ "</pattern>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ assert_true(type.flags & LYS_SET_PATTERN);
+ CHECK_LYSP_RESTR(type.patterns, "\x015super_pattern", "\"pattern-desc\"",
+ "err-app-tag-value", "err-msg-value", 1, "pattern-ref");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.patterns->exts[0]), LY_STMT_PATTERN);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<pattern value=\"pattern\"> </pattern>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_RESTR(type.patterns, "\x006pattern", NULL, NULL, NULL, 0, NULL);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+}
+
+static void
+test_value_position_elem(void **state)
+{
+ const char *data;
+ struct lysp_type_enum en = {0};
+
+ /* valid values */
+ data = ELEMENT_WRAPPER_START "<value value=\"-55\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, -55);
+ memset(&en, 0, sizeof(en));
+
+ data = ELEMENT_WRAPPER_START "<value value=\"0\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0);
+ memset(&en, 0, sizeof(en));
+
+ data = ELEMENT_WRAPPER_START "<value value=\"-0\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0);
+ memset(&en, 0, sizeof(en));
+
+ /* valid positions */
+ data = ELEMENT_WRAPPER_START "<position value=\"0\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0);
+ memset(&en, 0, sizeof(en));
+
+ /* invalid values */
+ data = ELEMENT_WRAPPER_START "<value value=\"99999999999999999999999\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"99999999999999999999999\" of \"value\" attribute in \"value\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<value value=\"1k\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"1k\" of \"value\" attribute in \"value\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<value value=\"\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"value\" element.", "Line number 1.");
+
+ /*invalid positions */
+ data = ELEMENT_WRAPPER_START "<position value=\"-5\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"-5\" of \"value\" attribute in \"position\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<position value=\"-0\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"-0\" of \"value\" attribute in \"position\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<position value=\"99999999999999999999\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"99999999999999999999\" of \"value\" attribute in \"position\" element.", "Line number 1.");
+
+ data = ELEMENT_WRAPPER_START "<position value=\"\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"position\" element.", "Line number 1.");
+}
+
+static void
+test_prefix_elem(void **state)
+{
+ const char *data;
+ const char *value = NULL;
+
+ data = ELEMENT_WRAPPER_START "<prefix value=\"pref\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &value, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(value, "pref");
+ lydict_remove(UTEST_LYCTX, value);
+}
+
+static void
+test_range_elem(void **state)
+{
+ const char *data;
+ struct lysp_type type = {0};
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<range value=\"range-str\">\n"
+ " <error-message><value>err-msg</value></error-message>\n"
+ " <error-app-tag value=\"err-app-tag\" />\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ EXT_SUBELEM
+ "</range>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_RESTR(type.range, "range-str", "desc",
+ "err-app-tag", "err-msg", 1, "ref");
+ assert_true(type.flags & LYS_SET_RANGE);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.range->exts[0]), LY_STMT_RANGE);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<range value=\"range-str\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_RESTR(type.range, "range-str", NULL,
+ NULL, NULL, 0, NULL);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+}
+
+static void
+test_reqinstance_elem(void **state)
+{
+ const char *data;
+ struct lysp_type type = {0};
+
+ data = ELEMENT_WRAPPER_START "<require-instance value=\"true\">" EXT_SUBELEM "</require-instance>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ assert_int_equal(type.require_instance, 1);
+ assert_true(type.flags & LYS_SET_REQINST);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.exts[0]), LY_STMT_REQUIRE_INSTANCE);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+
+ data = ELEMENT_WRAPPER_START "<require-instance value=\"false\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ assert_int_equal(type.require_instance, 0);
+ assert_true(type.flags & LYS_SET_REQINST);
+ memset(&type, 0, sizeof(type));
+
+ data = ELEMENT_WRAPPER_START "<require-instance value=\"invalid\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID);
+ memset(&type, 0, sizeof(type));
+ CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"require-instance\" element. "
+ "Valid values are \"true\" and \"false\".", "Line number 1.");
+}
+
+static void
+test_revision_date_elem(void **state)
+{
+ const char *data;
+ char rev[LY_REV_SIZE];
+
+ data = ELEMENT_WRAPPER_START "<revision-date date=\"2000-01-01\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, rev, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(rev, "2000-01-01");
+
+ data = ELEMENT_WRAPPER_START "<revision-date date=\"2000-50-05\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, rev, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"2000-50-05\" of \"revision-date\".", "Line number 1.");
+}
+
+static void
+test_unique_elem(void **state)
+{
+ const char *data;
+ const char **values = NULL;
+
+ data = ELEMENT_WRAPPER_START "<unique tag=\"tag\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &values, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(*values, "tag");
+ lydict_remove(UTEST_LYCTX, *values);
+ LY_ARRAY_FREE(values);
+ values = NULL;
+}
+
+static void
+test_units_elem(void **state)
+{
+ const char *data;
+ const char *values = NULL;
+
+ data = ELEMENT_WRAPPER_START "<units name=\"name\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &values, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(values, "name");
+ lydict_remove(UTEST_LYCTX, values);
+ values = NULL;
+}
+
+static void
+test_yin_text_value_elem(void **state)
+{
+ const char *data;
+ const char *val;
+
+ data = ELEMENT_WRAPPER_START "<text>text</text>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(val, "text");
+ lydict_remove(UTEST_LYCTX, val);
+
+ data = "<error-message xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <value>text</value> </error-message>";
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(val, "text");
+ lydict_remove(UTEST_LYCTX, val);
+
+ data = ELEMENT_WRAPPER_START "<text></text>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS);
+ assert_string_equal("", val);
+ lydict_remove(UTEST_LYCTX, val);
+}
+
+static void
+test_type_elem(void **state)
+{
+ const char *data;
+ struct lysp_type type = {0};
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<type name=\"type-name\">\n"
+ " <base name=\"base-name\"/>\n"
+ " <bit name=\"bit\"/>\n"
+ " <enum name=\"enum\"/>\n"
+ " <fraction-digits value=\"2\"/>\n"
+ " <length value=\"length\"/>\n"
+ " <path value=\"/path\"/>\n"
+ " <pattern value=\"pattern\"/>\n"
+ " <range value=\"range\" />\n"
+ " <require-instance value=\"true\"/>\n"
+ " <type name=\"sub-type-name\"/>\n"
+ EXT_SUBELEM
+ "</type>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(type.name, "type-name");
+ assert_string_equal(*type.bases, "base-name");
+ assert_string_equal(type.bits->name, "bit");
+ assert_string_equal(type.enums->name, "enum");
+ assert_int_equal(type.fraction_digits, 2);
+ CHECK_LYSP_RESTR(type.length, "length", NULL,
+ NULL, NULL, 0, NULL);
+ assert_string_equal(type.path->expr, "/path");
+ CHECK_LYSP_RESTR(type.patterns, "\006pattern", NULL,
+ NULL, NULL, 0, NULL);
+ CHECK_LYSP_RESTR(type.range, "range", NULL,
+ NULL, NULL, 0, NULL);
+ assert_int_equal(type.require_instance, 1);
+ assert_string_equal(type.types->name, "sub-type-name");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.exts[0]), LY_STMT_TYPE);
+ assert_true(type.flags & LYS_SET_BASE);
+ assert_true(type.flags & LYS_SET_BIT);
+ assert_true(type.flags & LYS_SET_ENUM);
+ assert_true(type.flags & LYS_SET_FRDIGITS);
+ assert_true(type.flags & LYS_SET_LENGTH);
+ assert_true(type.flags & LYS_SET_PATH);
+ assert_true(type.flags & LYS_SET_PATTERN);
+ assert_true(type.flags & LYS_SET_RANGE);
+ assert_true(type.flags & LYS_SET_REQINST);
+ assert_true(type.flags & LYS_SET_TYPE);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<type name=\"type-name\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS);
+ lysp_type_free(&fctx, &type);
+ memset(&type, 0, sizeof(type));
+}
+
+static void
+test_max_elems_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_list list = {0};
+ struct lysp_refine refine = {0};
+
+ data = "<refine xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"10\"/> </refine>";
+ assert_int_equal(test_element_helper(state, data, &refine, NULL, NULL), LY_SUCCESS);
+ assert_int_equal(refine.max, 10);
+ assert_true(refine.flags & LYS_SET_MAX);
+
+ data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"0\"/> </list>";
+ assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"0\" of \"value\" attribute in \"max-elements\" element.", "Line number 1.");
+
+ data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"-10\"/> </list>";
+ assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"-10\" of \"value\" attribute in \"max-elements\" element.", "Line number 1.");
+
+ data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"k\"/> </list>";
+ assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"k\" of \"value\" attribute in \"max-elements\" element.", "Line number 1.");
+
+ data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"u12\"/> </list>";
+ assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"u12\" of \"value\" attribute in \"max-elements\" element.", "Line number 1.");
+}
+
+static void
+test_min_elems_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_leaflist llist = {0};
+
+ data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"-5\"/> </leaf-list>";
+ assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Value \"-5\" of \"value\" attribute in \"min-elements\" element is out of bounds.", "Line number 1.");
+
+ data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"99999999999999999\"/> </leaf-list>";
+ assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Value \"99999999999999999\" of \"value\" attribute in \"min-elements\" element is out of bounds.", "Line number 1.");
+
+ data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"5k\"/> </leaf-list>";
+ assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"5k\" of \"value\" attribute in \"min-elements\" element.", "Line number 1.");
+
+ data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"05\"/> </leaf-list>";
+ assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"05\" of \"value\" attribute in \"min-elements\" element.", "Line number 1.");
+}
+
+static void
+test_ordby_elem(void **state)
+{
+ const char *data;
+ uint16_t flags = 0;
+
+ data = ELEMENT_WRAPPER_START "<ordered-by value=\"user\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_SUCCESS);
+ assert_true(flags & LYS_ORDBY_USER);
+
+ data = ELEMENT_WRAPPER_START "<ordered-by value=\"inv\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"inv\" of \"value\" attribute in \"ordered-by\" element. "
+ "Valid values are \"system\" and \"user\".", "Line number 1.");
+}
+
+static void
+test_any_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings};
+ struct lysp_node_anydata *parsed = NULL;
+ uint16_t flags;
+
+ /* anyxml max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<anyxml name=\"any-name\">\n"
+ " <config value=\"true\" />\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"feature\" />\n"
+ " <mandatory value=\"true\" />\n"
+ " <must condition=\"must-cond\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</anyxml>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_anydata *)siblings;
+ flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC;
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "any-name", 0, LYS_ANYXML, 0, "ref", 1);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_ANYXML);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* anydata max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<anydata name=\"any-name\">\n"
+ " <config value=\"true\" />\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"feature\" />\n"
+ " <mandatory value=\"true\" />\n"
+ " <must condition=\"must-cond\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</anydata>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_anydata *)siblings;
+ flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC;
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "any-name", 0, LYS_ANYDATA, 0, "ref", 1);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_ANYDATA);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* min subelems */
+ node_meta.parent = (void *)0x10;
+ data = ELEMENT_WRAPPER_START "<anydata name=\"any-name\"> </anydata>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_anydata *)siblings;
+ assert_ptr_equal(parsed->parent, node_meta.parent);
+ CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0,
+ "any-name", 0, LYS_ANYDATA, 1, NULL, 0);
+ lysp_node_free(&fctx, siblings);
+}
+
+static void
+test_leaf_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings};
+ struct lysp_node_leaf *parsed = NULL;
+ uint16_t flags;
+
+ /* max elements */
+ data = ELEMENT_WRAPPER_START
+ "<leaf name=\"leaf\">\n"
+ " <config value=\"true\" />\n"
+ " <default value=\"def-val\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"feature\" />\n"
+ " <mandatory value=\"true\" />\n"
+ " <must condition=\"must-cond\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <type name=\"type\"/>\n"
+ " <units name=\"uni\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</leaf>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_leaf *)siblings;
+ flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC;
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "leaf", 0, LYS_LEAF, 0, "ref", 1);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF);
+ assert_string_equal(parsed->musts->arg.str, "must-cond");
+ assert_string_equal(parsed->type.name, "type");
+ assert_string_equal(parsed->units, "uni");
+ assert_string_equal(parsed->dflt.str, "def-val");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* min elements */
+ data = ELEMENT_WRAPPER_START "<leaf name=\"leaf\"> <type name=\"type\"/> </leaf>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_leaf *)siblings;
+ assert_string_equal(parsed->name, "leaf");
+ assert_string_equal(parsed->type.name, "type");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_leaf_list_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings};
+ struct lysp_node_leaflist *parsed = NULL;
+ uint16_t flags;
+
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">\n"
+ " <config value=\"true\" />\n"
+ " <default value=\"def-val0\"/>\n"
+ " <default value=\"def-val1\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"feature\"/>\n"
+ " <max-elements value=\"5\"/>\n"
+ " <must condition=\"must-cond\"/>\n"
+ " <ordered-by value=\"user\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <type name=\"type\"/>\n"
+ " <units name=\"uni\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_leaflist *)siblings;
+ flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MAX;
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "llist", 0, LYS_LEAFLIST, 0, "ref", 1);
+ CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL);
+ assert_string_equal(parsed->dflts[0].str, "def-val0");
+ assert_string_equal(parsed->dflts[1].str, "def-val1");
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ assert_int_equal(parsed->max, 5);
+ assert_string_equal(parsed->type.name, "type");
+ assert_string_equal(parsed->units, "uni");
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF_LIST);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">\n"
+ " <config value=\"true\" />\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"feature\"/>\n"
+ " <min-elements value=\"5\"/>\n"
+ " <must condition=\"must-cond\"/>\n"
+ " <ordered-by value=\"user\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <type name=\"type\"/>\n"
+ " <units name=\"uni\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_leaflist *)siblings;
+ flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MIN;
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "llist", 0, LYS_LEAFLIST, 0, "ref", 1);
+ CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ assert_int_equal(parsed->min, 5);
+ assert_string_equal(parsed->type.name, "type");
+ assert_string_equal(parsed->units, "uni");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF_LIST);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">\n"
+ " <config value=\"true\" />\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"feature\"/>\n"
+ " <max-elements value=\"15\"/>\n"
+ " <min-elements value=\"5\"/>\n"
+ " <must condition=\"must-cond\"/>\n"
+ " <ordered-by value=\"user\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <type name=\"type\"/>\n"
+ " <units name=\"uni\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_leaflist *)siblings;
+ flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MIN | LYS_SET_MAX;
+ CHECK_LYSP_NODE(parsed, "desc", 0, flags, 1,
+ "llist", 0, LYS_LEAFLIST, 0, "ref", 1);
+ CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ assert_int_equal(parsed->min, 5);
+ assert_int_equal(parsed->max, 15);
+ assert_string_equal(parsed->type.name, "type");
+ assert_string_equal(parsed->units, "uni");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">\n"
+ " <type name=\"type\"/>\n"
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_leaflist *)siblings;
+ assert_string_equal(parsed->name, "llist");
+ assert_string_equal(parsed->type.name, "type");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* invalid combinations */
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">\n"
+ " <max-elements value=\"5\"/>\n"
+ " <min-elements value=\"15\"/>\n"
+ " <type name=\"type\"/>"
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid combination of min-elements and max-elements: min value 15 is bigger than the max value 5.", "Line number 4.");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">\n"
+ " <default value=\"def-val1\"/>\n"
+ " <min-elements value=\"15\"/>\n"
+ " <type name=\"type\"/>\n"
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid combination of sub-elemnts \"min-elements\" and \"default\" in \"leaf-list\" element.", "Line number 5.");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<leaf-list name=\"llist\">"
+ "</leaf-list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory sub-element \"type\" of \"leaf-list\" element.", "Line number 1.");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_presence_elem(void **state)
+{
+ const char *data;
+ const char *val;
+
+ data = ELEMENT_WRAPPER_START "<presence value=\"presence-val\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(val, "presence-val");
+ lydict_remove(UTEST_LYCTX, val);
+
+ data = ELEMENT_WRAPPER_START "<presence/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute value of presence element.", "Line number 1.");
+}
+
+static void
+test_key_elem(void **state)
+{
+ const char *data;
+ const char *val;
+
+ data = ELEMENT_WRAPPER_START "<key value=\"key-value\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(val, "key-value");
+ lydict_remove(UTEST_LYCTX, val);
+
+ data = ELEMENT_WRAPPER_START "<key/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory attribute value of key element.", "Line number 1.");
+}
+
+static void
+test_uses_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {NULL, &siblings};
+ struct lysp_node_uses *parsed = NULL;
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<uses name=\"uses-name\">\n"
+ " <when condition=\"cond\" />\n"
+ " <if-feature name=\"feature\" />\n"
+ " <status value=\"obsolete\" />\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <refine target-node=\"target\"/>\n"
+ " <augment target-node=\"target\" />\n"
+ EXT_SUBELEM
+ "</uses>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_uses *)&siblings[0];
+ CHECK_LYSP_NODE(parsed, "desc", 1, LYS_STATUS_OBSLT, 1,
+ "uses-name", 0, LYS_USES, 0, "ref", 1);
+ CHECK_LYSP_WHEN(parsed->when, "cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "feature");
+ assert_string_equal(parsed->refines->nodeid, "target");
+ assert_string_equal(parsed->augments->nodeid, "target");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_USES);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<uses name=\"uses-name\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(siblings[0].name, "uses-name");
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_list_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {NULL, &siblings};
+ struct lysp_node_list *parsed = NULL;
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<list name=\"list-name\">\n"
+ " <when condition=\"when\"/>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <must condition=\"must-cond\"/>\n"
+ " <key value=\"key\"/>\n"
+ " <unique tag=\"utag\"/>\n"
+ " <config value=\"true\"/>\n"
+ " <min-elements value=\"10\"/>\n"
+ " <ordered-by value=\"user\"/>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <container name=\"cont\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <action name=\"action\"/>\n"
+ " <grouping name=\"grp\"/>\n"
+ " <notification name=\"notf\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"sub-list\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ " <uses name=\"uses-name\"/>\n"
+ EXT_SUBELEM
+ "</list>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_list *)&siblings[0];
+ assert_string_equal(parsed->child->name, "anyd");
+ assert_int_equal(parsed->child->nodetype, LYS_ANYDATA);
+ assert_string_equal(parsed->child->next->name, "anyx");
+ assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(parsed->child->next->next->name, "cont");
+ assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(parsed->child->next->next->next->name, "choice");
+ assert_int_equal(parsed->child->next->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(parsed->child->next->next->next->next->name, "leaf");
+ assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(parsed->child->next->next->next->next->next->name, "llist");
+ assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->name, "sub-list");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_null(parsed->child->next->next->next->next->next->next->next->next);
+ uint16_t flags = LYS_ORDBY_USER | LYS_STATUS_DEPRC | LYS_CONFIG_W | LYS_SET_MIN;
+
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "list-name", 0, LYS_LIST, 0, "ref", 1);
+ CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL);
+ CHECK_LYSP_WHEN(parsed->when, "when", NULL, 0, NULL);
+ assert_string_equal(parsed->groupings->name, "grp");
+ assert_string_equal(parsed->actions->name, "action");
+ assert_int_equal(parsed->groupings->nodetype, LYS_GROUPING);
+ assert_string_equal(parsed->notifs->name, "notf");
+ assert_string_equal(parsed->iffeatures[0].str, "iff");
+ assert_string_equal(parsed->key, "key");
+ assert_int_equal(parsed->min, 10);
+ assert_string_equal(parsed->typedefs->name, "tpdf");
+ assert_string_equal(parsed->uniques->str, "utag");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LIST);
+ lysp_node_free(&fctx, siblings);
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ siblings = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<list name=\"list-name\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_list *)&siblings[0];
+ CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0,
+ "list-name", 0, LYS_LIST, 0, NULL, 0);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_notification_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_notif *notifs = NULL;
+ struct tree_node_meta notif_meta = {NULL, (struct lysp_node **)&notifs};
+
+ /* max subelems */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<notification name=\"notif-name\">\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"sub-list\"/>\n"
+ " <must condition=\"cond\"/>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ " <uses name=\"uses-name\"/>\n"
+ " <container name=\"cont\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <grouping name=\"grp\"/>\n"
+ EXT_SUBELEM
+ "</notification>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &notif_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(notifs->name, "notif-name");
+ assert_string_equal(notifs->child->name, "anyd");
+ assert_int_equal(notifs->child->nodetype, LYS_ANYDATA);
+ assert_string_equal(notifs->child->next->name, "anyx");
+ assert_int_equal(notifs->child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(notifs->child->next->next->name, "leaf");
+ assert_int_equal(notifs->child->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(notifs->child->next->next->next->name, "llist");
+ assert_int_equal(notifs->child->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(notifs->child->next->next->next->next->name, "sub-list");
+ assert_int_equal(notifs->child->next->next->next->next->nodetype, LYS_LIST);
+ assert_true(notifs->flags & LYS_STATUS_DEPRC);
+ assert_string_equal(notifs->groupings->name, "grp");
+ assert_int_equal(notifs->groupings->nodetype, LYS_GROUPING);
+ assert_string_equal(notifs->child->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(notifs->child->next->next->next->next->next->nodetype, LYS_USES);
+ assert_string_equal(notifs->child->next->next->next->next->next->next->name, "cont");
+ assert_int_equal(notifs->child->next->next->next->next->next->next->nodetype, LYS_CONTAINER);
+ assert_int_equal(notifs->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(notifs->child->next->next->next->next->next->next->next->name, "choice");
+ assert_null(notifs->child->next->next->next->next->next->next->next->next);
+ assert_string_equal(notifs->iffeatures[0].str, "iff");
+ assert_string_equal(notifs->musts->arg.str, "cond");
+ assert_int_equal(notifs->nodetype, LYS_NOTIF);
+ assert_null(notifs->parent);
+ assert_string_equal(notifs->ref, "ref");
+ assert_string_equal(notifs->typedefs->name, "tpdf");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(notifs->exts[0]), LY_STMT_NOTIFICATION);
+ lysp_node_free(&fctx, (struct lysp_node *)notifs);
+ notifs = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<notification name=\"notif-name\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &notif_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(notifs->name, "notif-name");
+ lysp_node_free(&fctx, (struct lysp_node *)notifs);
+ notifs = NULL;
+}
+
+static void
+test_grouping_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_grp *grps = NULL;
+ struct tree_node_meta grp_meta = {NULL, (struct lysp_node **)&grps};
+
+ /* max subelems */
+ data = ELEMENT_WRAPPER_START
+ "<grouping name=\"grp-name\">\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <grouping name=\"sub-grp\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <notification name=\"notf\"/>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ " <uses name=\"uses-name\"/>\n"
+ " <action name=\"act\"/>\n"
+ " <container name=\"cont\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ EXT_SUBELEM
+ "</grouping>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &grp_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(grps->name, "grp-name");
+ assert_string_equal(grps->child->name, "anyd");
+ assert_string_equal(grps->child->next->name, "anyx");
+ assert_string_equal(grps->child->next->next->name, "leaf");
+ assert_string_equal(grps->child->next->next->next->name, "llist");
+ assert_string_equal(grps->child->next->next->next->next->name, "list");
+ assert_string_equal(grps->dsc, "desc");
+ assert_true(grps->flags & LYS_STATUS_CURR);
+ assert_string_equal(grps->groupings->name, "sub-grp");
+ assert_int_equal(grps->nodetype, LYS_GROUPING);
+ assert_string_equal(grps->notifs->name, "notf");
+ assert_null(grps->parent);
+ assert_string_equal(grps->ref, "ref");
+ assert_string_equal(grps->typedefs->name, "tpdf");
+ assert_string_equal(grps->actions->name, "act");
+ assert_string_equal(grps->child->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(grps->child->next->next->next->next->next->nodetype, LYS_USES);
+ assert_string_equal(grps->child->next->next->next->next->next->next->name, "cont");
+ assert_int_equal(grps->child->next->next->next->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(grps->child->next->next->next->next->next->next->next->name, "choice");
+ assert_int_equal(grps->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(grps->exts[0]), LY_STMT_GROUPING);
+ lysp_node_free(&fctx, &grps->node);
+ grps = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<grouping name=\"grp-name\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &grp_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(grps->name, "grp-name");
+ lysp_node_free(&fctx, &grps->node);
+ grps = NULL;
+}
+
+static void
+test_container_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {NULL, &siblings};
+ struct lysp_node_container *parsed = NULL;
+
+ /* max subelems */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<container name=\"cont-name\">\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <config value=\"true\"/>\n"
+ " <container name=\"subcont\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <grouping name=\"sub-grp\"/>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <must condition=\"cond\"/>\n"
+ " <notification name=\"notf\"/>\n"
+ " <presence value=\"presence\"/>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ " <uses name=\"uses-name\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ " <action name=\"act\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ EXT_SUBELEM
+ "</container>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_container *)siblings;
+ uint16_t flags = LYS_CONFIG_W | LYS_STATUS_CURR;
+
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "cont-name", 0, LYS_CONTAINER, 0, "ref", 1);
+ CHECK_LYSP_RESTR(parsed->musts, "cond", NULL, NULL, NULL, 0, NULL);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+
+ assert_string_equal(parsed->iffeatures[0].str, "iff");
+ assert_string_equal(parsed->presence, "presence");
+ assert_string_equal(parsed->typedefs->name, "tpdf");
+ assert_string_equal(parsed->groupings->name, "sub-grp");
+ assert_string_equal(parsed->child->name, "anyd");
+ assert_int_equal(parsed->child->nodetype, LYS_ANYDATA);
+ assert_string_equal(parsed->child->next->name, "anyx");
+ assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(parsed->child->next->next->name, "subcont");
+ assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(parsed->child->next->next->next->name, "leaf");
+ assert_int_equal(parsed->child->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(parsed->child->next->next->next->next->name, "llist");
+ assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->name, "list");
+ assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "choice");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE);
+ assert_null(parsed->child->next->next->next->next->next->next->next->next);
+ assert_string_equal(parsed->notifs->name, "notf");
+ assert_string_equal(parsed->actions->name, "act");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CONTAINER);
+ lysp_node_free(&fctx, siblings);
+ ly_set_erase(&YCTX->tpdfs_nodes, NULL);
+ siblings = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<container name=\"cont-name\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_container *)siblings;
+ CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0,
+ "cont-name", 0, LYS_CONTAINER, 0, NULL, 0);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_case_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {NULL, &siblings};
+ struct lysp_node_case *parsed = NULL;
+
+ /* max subelems */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<case name=\"case-name\">\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <container name=\"subcont\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <uses name=\"uses-name\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ EXT_SUBELEM
+ "</case>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_case *)siblings;
+ uint16_t flags = LYS_STATUS_CURR;
+
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "case-name", 0, LYS_CASE, 0, "ref", 1);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "iff");
+ assert_string_equal(parsed->child->name, "anyd");
+ assert_int_equal(parsed->child->nodetype, LYS_ANYDATA);
+ assert_string_equal(parsed->child->next->name, "anyx");
+ assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(parsed->child->next->next->name, "subcont");
+ assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(parsed->child->next->next->next->name, "leaf");
+ assert_int_equal(parsed->child->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(parsed->child->next->next->next->next->name, "llist");
+ assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->name, "list");
+ assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "choice");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE);
+ assert_null(parsed->child->next->next->next->next->next->next->next->next);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CASE);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<case name=\"case-name\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_case *)siblings;
+ CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0,
+ "case-name", 0, LYS_CASE, 0, NULL, 0);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_choice_elem(void **state)
+{
+ const char *data;
+ struct lysp_node *siblings = NULL;
+ struct tree_node_meta node_meta = {NULL, &siblings};
+ struct lysp_node_choice *parsed = NULL;
+
+ /* max subelems */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<choice name=\"choice-name\">\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <case name=\"sub-case\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <config value=\"true\"/>\n"
+ " <container name=\"subcont\"/>\n"
+ " <default value=\"def\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <mandatory value=\"true\" />\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</choice>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_choice *)siblings;
+ uint16_t flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_CURR;
+
+ CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1,
+ "choice-name", 0, LYS_CHOICE, 0, "ref", 1);
+ CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL);
+ assert_string_equal(parsed->iffeatures[0].str, "iff");
+ assert_string_equal(parsed->child->name, "anyd");
+ assert_int_equal(parsed->child->nodetype, LYS_ANYDATA);
+ assert_string_equal(parsed->child->next->name, "anyx");
+ assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(parsed->child->next->next->name, "sub-case");
+ assert_int_equal(parsed->child->next->next->nodetype, LYS_CASE);
+ assert_string_equal(parsed->child->next->next->next->name, "choice");
+ assert_int_equal(parsed->child->next->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(parsed->child->next->next->next->next->name, "subcont");
+ assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(parsed->child->next->next->next->next->next->name, "leaf");
+ assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->name, "llist");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "list");
+ assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_null(parsed->child->next->next->next->next->next->next->next->next);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CHOICE);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<choice name=\"choice-name\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS);
+ parsed = (struct lysp_node_choice *)siblings;
+ assert_string_equal(parsed->name, "choice-name");
+ CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0,
+ "choice-name", 0, LYS_CHOICE, 0, NULL, 0);
+ lysp_node_free(&fctx, siblings);
+ siblings = NULL;
+}
+
+static void
+test_inout_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_action_inout inout = {0};
+ struct inout_meta inout_meta = {NULL, &inout};
+
+ /* max subelements */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<input>\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <container name=\"subcont\"/>\n"
+ " <grouping name=\"sub-grp\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <must condition=\"cond\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ " <uses name=\"uses-name\"/>\n"
+ EXT_SUBELEM
+ "</input>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_ACTION_INOUT(&(inout), 1, 1, 1, 1, LYS_INPUT, 0, 1);
+ CHECK_LYSP_RESTR(inout.musts, "cond", NULL, NULL, NULL, 0, NULL);
+ assert_string_equal(inout.typedefs->name, "tpdf");
+ assert_string_equal(inout.groupings->name, "sub-grp");
+ assert_string_equal(inout.child->name, "anyd");
+ assert_int_equal(inout.child->nodetype, LYS_ANYDATA);
+ assert_string_equal(inout.child->next->name, "anyx");
+ assert_int_equal(inout.child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(inout.child->next->next->name, "choice");
+ assert_int_equal(inout.child->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(inout.child->next->next->next->name, "subcont");
+ assert_int_equal(inout.child->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(inout.child->next->next->next->next->name, "leaf");
+ assert_int_equal(inout.child->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(inout.child->next->next->next->next->next->name, "llist");
+ assert_int_equal(inout.child->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(inout.child->next->next->next->next->next->next->name, "list");
+ assert_int_equal(inout.child->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(inout.child->next->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(inout.child->next->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_null(inout.child->next->next->next->next->next->next->next->next);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(inout.exts[0]), LY_STMT_INPUT);
+ lysp_node_free(&fctx, (struct lysp_node *)&inout);
+ memset(&inout, 0, sizeof inout);
+
+ /* max subelements */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<output>\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <container name=\"subcont\"/>\n"
+ " <grouping name=\"sub-grp\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <must condition=\"cond\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ " <uses name=\"uses-name\"/>\n"
+ EXT_SUBELEM
+ "</output>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS);
+ CHECK_LYSP_ACTION_INOUT(&(inout), 1, 1, 1, 1, LYS_OUTPUT, 0, 1);
+ assert_string_equal(inout.musts->arg.str, "cond");
+ assert_string_equal(inout.typedefs->name, "tpdf");
+ assert_string_equal(inout.groupings->name, "sub-grp");
+ assert_string_equal(inout.child->name, "anyd");
+ assert_int_equal(inout.child->nodetype, LYS_ANYDATA);
+ assert_string_equal(inout.child->next->name, "anyx");
+ assert_int_equal(inout.child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(inout.child->next->next->name, "choice");
+ assert_int_equal(inout.child->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(inout.child->next->next->next->name, "subcont");
+ assert_int_equal(inout.child->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(inout.child->next->next->next->next->name, "leaf");
+ assert_int_equal(inout.child->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(inout.child->next->next->next->next->next->name, "llist");
+ assert_int_equal(inout.child->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(inout.child->next->next->next->next->next->next->name, "list");
+ assert_int_equal(inout.child->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(inout.child->next->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(inout.child->next->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_null(inout.child->next->next->next->next->next->next->next->next);
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(inout.exts[0]), LY_STMT_OUTPUT);
+ lysp_node_free(&fctx, (struct lysp_node *)&inout);
+ memset(&inout, 0, sizeof inout);
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<input><leaf name=\"l\"><type name=\"empty\"/></leaf></input>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS);
+ lysp_node_free(&fctx, (struct lysp_node *)&inout);
+ memset(&inout, 0, sizeof inout);
+
+ data = ELEMENT_WRAPPER_START "<output><leaf name=\"l\"><type name=\"empty\"/></leaf></output>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS);
+ lysp_node_free(&fctx, (struct lysp_node *)&inout);
+ memset(&inout, 0, sizeof inout);
+
+ /* invalid combinations */
+ data = ELEMENT_WRAPPER_START "<input name=\"test\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_EVALID);
+ lysp_node_free(&fctx, (struct lysp_node *)&inout);
+ CHECK_LOG_CTX("Unexpected attribute \"name\" of \"input\" element.", "Line number 1.");
+ memset(&inout, 0, sizeof inout);
+}
+
+static void
+test_action_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_action *actions = NULL;
+ struct tree_node_meta act_meta = {NULL, (struct lysp_node **)&actions};
+ uint16_t flags;
+
+ /* max subelems */
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<action name=\"act\">\n"
+ " <description><text>desc</text></description>\n"
+ " <grouping name=\"grouping\"/>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <input><uses name=\"uses-name\"/></input>\n"
+ " <output><must condition=\"cond\"/><leaf name=\"l\"><type name=\"type\"/></leaf></output>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ EXT_SUBELEM
+ "</action>"
+ ELEMENT_WRAPPER_END;
+ /* there must be parent for action */
+ act_meta.parent = (void *)1;
+ assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS);
+ act_meta.parent = NULL;
+ flags = LYS_STATUS_DEPRC;
+ CHECK_LYSP_ACTION(actions, "desc", 1, flags, 1, 1,\
+ 1, 0, 0, 0,\
+ 1, 0,\
+ "act", LYS_ACTION, \
+ 1, 0, 0, 1,\
+ 1, 0,\
+ 1, "ref", 1);
+
+ assert_string_equal(actions->iffeatures[0].str, "iff");
+ assert_string_equal(actions->typedefs->name, "tpdf");
+ assert_string_equal(actions->groupings->name, "grouping");
+ assert_string_equal(actions->output.musts->arg.str, "cond");
+ assert_string_equal(actions->input.child->name, "uses-name");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(actions->exts[0]), LY_STMT_ACTION);
+ lysp_node_free(&fctx, (struct lysp_node *)actions);
+ actions = NULL;
+
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<rpc name=\"act\">\n"
+ " <description><text>desc</text></description>\n"
+ " <grouping name=\"grouping\"/>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <input><uses name=\"uses-name\"/></input>\n"
+ " <output><must condition=\"cond\"/><leaf name=\"l\"><type name=\"type\"/></leaf></output>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"deprecated\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ EXT_SUBELEM
+ "</rpc>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS);
+ flags = LYS_STATUS_DEPRC;
+ CHECK_LYSP_ACTION(actions, "desc", 1, flags, 1, 1,\
+ 1, 0, 0, 0,\
+ 1, 0,\
+ "act", LYS_RPC, \
+ 1, 0, 0, 1,\
+ 1, 0,\
+ 0, "ref", 1);
+
+ assert_string_equal(actions->iffeatures[0].str, "iff");
+ assert_string_equal(actions->typedefs->name, "tpdf");
+ assert_string_equal(actions->groupings->name, "grouping");
+ assert_string_equal(actions->input.child->name, "uses-name");
+ assert_string_equal(actions->output.musts->arg.str, "cond");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(actions->exts[0]), LY_STMT_RPC);
+ lysp_node_free(&fctx, (struct lysp_node *)actions);
+ actions = NULL;
+
+ /* min subelems */
+ data = ELEMENT_WRAPPER_START "<action name=\"act\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(actions->name, "act");
+ lysp_node_free(&fctx, (struct lysp_node *)actions);
+ actions = NULL;
+}
+
+static void
+test_augment_elem(void **state)
+{
+ const char *data;
+ struct lysp_node_augment *augments = NULL;
+ struct tree_node_meta aug_meta = {NULL, (struct lysp_node **)&augments};
+
+ PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1;
+ data = ELEMENT_WRAPPER_START
+ "<augment target-node=\"target\">\n"
+ " <action name=\"action\"/>\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <case name=\"case\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <container name=\"subcont\"/>\n"
+ " <description><text>desc</text></description>\n"
+ " <if-feature name=\"iff\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"list\"/>\n"
+ " <notification name=\"notif\"/>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <status value=\"current\"/>\n"
+ " <uses name=\"uses\"/>\n"
+ " <when condition=\"when-cond\"/>\n"
+ EXT_SUBELEM
+ "</augment>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &aug_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(augments->nodeid, "target");
+ assert_null(augments->parent);
+ assert_int_equal(augments->nodetype, LYS_AUGMENT);
+ assert_true(augments->flags & LYS_STATUS_CURR);
+ assert_string_equal(augments->dsc, "desc");
+ assert_string_equal(augments->ref, "ref");
+ assert_string_equal(augments->when->cond, "when-cond");
+ assert_string_equal(augments->iffeatures[0].str, "iff");
+ assert_string_equal(augments->child->name, "anyd");
+ assert_int_equal(augments->child->nodetype, LYS_ANYDATA);
+ assert_string_equal(augments->child->next->name, "anyx");
+ assert_int_equal(augments->child->next->nodetype, LYS_ANYXML);
+ assert_string_equal(augments->child->next->next->name, "case");
+ assert_int_equal(augments->child->next->next->nodetype, LYS_CASE);
+ assert_string_equal(augments->child->next->next->next->name, "choice");
+ assert_int_equal(augments->child->next->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(augments->child->next->next->next->next->name, "subcont");
+ assert_int_equal(augments->child->next->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(augments->child->next->next->next->next->next->name, "leaf");
+ assert_int_equal(augments->child->next->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(augments->child->next->next->next->next->next->next->name, "llist");
+ assert_int_equal(augments->child->next->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(augments->child->next->next->next->next->next->next->next->name, "list");
+ assert_int_equal(augments->child->next->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(augments->child->next->next->next->next->next->next->next->next->name, "uses");
+ assert_int_equal(augments->child->next->next->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_null(augments->child->next->next->next->next->next->next->next->next->next);
+ assert_string_equal(augments->actions->name, "action");
+ assert_string_equal(augments->notifs->name, "notif");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(augments->exts[0]), LY_STMT_AUGMENT);
+ lysp_node_free(&fctx, (struct lysp_node *)augments);
+ augments = NULL;
+
+ data = ELEMENT_WRAPPER_START "<augment target-node=\"target\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &aug_meta, NULL, NULL), LY_SUCCESS);
+ assert_string_equal(augments->nodeid, "target");
+ lysp_node_free(&fctx, (struct lysp_node *)augments);
+ augments = NULL;
+}
+
+static void
+test_deviate_elem(void **state)
+{
+ const char *data;
+ struct lysp_deviate *deviates = NULL;
+
+ /* invalid arguments */
+ data = ELEMENT_WRAPPER_START "<deviate value=\"\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"deviate\" element. "
+ "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", "Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"invalid\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"deviate\" element. "
+ "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", "Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"ad\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"ad\" of \"value\" attribute in \"deviate\" element. "
+ "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", "Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"adds\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Invalid value \"adds\" of \"value\" attribute in \"deviate\" element. "
+ "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", "Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<deviate value=\"not-supported\">\n"
+ " <must condition=\"c\"/>\n"
+ "</deviate>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Deviate of this type doesn't allow \"must\" as it's sub-element.", "Line number 2.");
+}
+
+static void
+test_deviation_elem(void **state)
+{
+ const char *data;
+ struct lysp_deviation *deviations = NULL;
+
+ /* invalid */
+ data = ELEMENT_WRAPPER_START "<deviation target-node=\"target\"/>" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(state, data, &deviations, NULL, NULL), LY_EVALID);
+ CHECK_LOG_CTX("Missing mandatory sub-element \"deviate\" of \"deviation\" element.", "Line number 1.");
+}
+
+static struct lysp_module *
+mod_renew(struct lysp_yin_ctx *ctx)
+{
+ struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
+ struct lysp_module *pmod;
+
+ lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0);
+ pmod = calloc(1, sizeof *pmod);
+ ctx->parsed_mods->objs[0] = pmod;
+ pmod->mod = calloc(1, sizeof *pmod->mod);
+ pmod->mod->parsed = pmod;
+ pmod->mod->ctx = ly_ctx;
+
+ fctx.mod = pmod->mod;
+
+ return pmod;
+}
+
+static void
+test_module_elem(void **state)
+{
+ const char *data;
+ struct lysp_module *lysp_mod = mod_renew(YCTX);
+
+ /* max subelems */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <namespace uri=\"ns\"/>\n"
+ " <prefix value=\"pref\"/>\n"
+ " <include module=\"b-mod\"/>\n"
+ " <import module=\"a-mod\"><prefix value=\"imp-pref\"/></import>\n"
+ " <organization><text>org</text></organization>\n"
+ " <contact><text>contact</text></contact>\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <revision date=\"2019-02-02\"/>\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <container name=\"cont\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"sub-list\"/>\n"
+ " <uses name=\"uses-name\"/>\n"
+ " <augment target-node=\"target\"/>\n"
+ " <deviation target-node=\"target\">\n"
+ " <deviate value=\"not-supported\"/>\n"
+ " </deviation>\n"
+ " <extension name=\"ext\"/>\n"
+ " <feature name=\"feature\"/>\n"
+ " <grouping name=\"grp\"/>\n"
+ " <identity name=\"ident-name\"/>\n"
+ " <notification name=\"notf\"/>\n"
+ " <rpc name=\"rpc-name\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ EXT_SUBELEM "\n"
+ "</module>\n";
+ assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS);
+ assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
+
+ assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS);
+ assert_string_equal(lysp_mod->mod->name, "mod");
+ assert_string_equal(lysp_mod->revs, "2019-02-02");
+ assert_string_equal(lysp_mod->mod->ns, "ns");
+ assert_string_equal(lysp_mod->mod->prefix, "pref");
+ assert_null(lysp_mod->mod->filepath);
+ assert_string_equal(lysp_mod->mod->org, "org");
+ assert_string_equal(lysp_mod->mod->contact, "contact");
+ assert_string_equal(lysp_mod->mod->dsc, "desc");
+ assert_string_equal(lysp_mod->mod->ref, "ref");
+ assert_int_equal(lysp_mod->version, LYS_VERSION_1_1);
+ CHECK_LYSP_IMPORT(lysp_mod->imports, NULL, 0, "a-mod",
+ "imp-pref", NULL, "");
+ assert_string_equal(lysp_mod->includes->name, "b-mod");
+ assert_string_equal(lysp_mod->extensions->name, "ext");
+ assert_string_equal(lysp_mod->features->name, "feature");
+ assert_string_equal(lysp_mod->identities->name, "ident-name");
+ assert_string_equal(lysp_mod->typedefs->name, "tpdf");
+ assert_string_equal(lysp_mod->groupings->name, "grp");
+ assert_string_equal(lysp_mod->data->name, "anyd");
+ assert_int_equal(lysp_mod->data->nodetype, LYS_ANYDATA);
+ assert_string_equal(lysp_mod->data->next->name, "anyx");
+ assert_int_equal(lysp_mod->data->next->nodetype, LYS_ANYXML);
+ assert_string_equal(lysp_mod->data->next->next->name, "choice");
+ assert_int_equal(lysp_mod->data->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(lysp_mod->data->next->next->next->name, "cont");
+ assert_int_equal(lysp_mod->data->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(lysp_mod->data->next->next->next->next->name, "leaf");
+ assert_int_equal(lysp_mod->data->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(lysp_mod->data->next->next->next->next->next->name, "llist");
+ assert_int_equal(lysp_mod->data->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(lysp_mod->data->next->next->next->next->next->next->name, "sub-list");
+ assert_int_equal(lysp_mod->data->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(lysp_mod->data->next->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(lysp_mod->data->next->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_null(lysp_mod->data->next->next->next->next->next->next->next->next);
+ assert_string_equal(lysp_mod->augments->nodeid, "target");
+ assert_string_equal(lysp_mod->rpcs->name, "rpc-name");
+ assert_string_equal(lysp_mod->notifs->name, "notf");
+ assert_string_equal(lysp_mod->deviations->nodeid, "target");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(lysp_mod->exts[0]), LY_STMT_MODULE);
+
+ /* min subelems */
+ ly_in_free(UTEST_IN, 0);
+ lyxml_ctx_free(YCTX->xmlctx);
+ lysp_mod = mod_renew(YCTX);
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n"
+ " <namespace uri=\"ns\"/>\n"
+ " <prefix value=\"pref\"/>\n"
+ " <yang-version value=\"1.1\"/>\n"
+ "</module>";
+ assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS);
+ assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
+ assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS);
+ assert_string_equal(lysp_mod->mod->name, "mod");
+
+ /* incorrect subelem order */
+ ly_in_free(UTEST_IN, 0);
+ lyxml_ctx_free(YCTX->xmlctx);
+ lysp_mod = mod_renew(YCTX);
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n"
+ " <feature name=\"feature\"/>\n"
+ " <namespace uri=\"ns\"/>\n"
+ " <prefix value=\"pref\"/>\n"
+ " <yang-version value=\"1.1\"/>\n"
+ "</module>";
+ assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS);
+ assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
+ assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_EVALID);
+ CHECK_LOG_CTX("Invalid order of module\'s sub-elements \"namespace\" can\'t appear after \"feature\".", "Line number 3.");
+}
+
+static struct lysp_submodule *
+submod_renew(struct lysp_yin_ctx *ctx, const char *belongs_to)
+{
+ struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
+ struct lysp_submodule *submod;
+
+ lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0);
+ submod = calloc(1, sizeof *submod);
+ ctx->parsed_mods->objs[0] = submod;
+ submod->mod = calloc(1, sizeof *submod->mod);
+ lydict_insert(ly_ctx, belongs_to, 0, &submod->mod->name);
+ submod->mod->parsed = (struct lysp_module *)submod;
+ submod->mod->ctx = ly_ctx;
+
+ fctx.mod = submod->mod;
+
+ return submod;
+}
+
+static void
+test_submodule_elem(void **state)
+{
+ const char *data;
+ struct lysp_submodule *lysp_submod = submod_renew(YCTX, "module-name");
+
+ /* max subelements */
+ data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <belongs-to module=\"module-name\">\n"
+ " <prefix value=\"pref\"/>\n"
+ " </belongs-to>\n"
+ " <include module=\"b-mod\"/>\n"
+ " <import module=\"a-mod\"><prefix value=\"imp-pref\"/></import>\n"
+ " <organization><text>org</text></organization>\n"
+ " <contact><text>contact</text></contact>\n"
+ " <description><text>desc</text></description>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <revision date=\"2019-02-02\"/>\n"
+ " <anydata name=\"anyd\"/>\n"
+ " <anyxml name=\"anyx\"/>\n"
+ " <choice name=\"choice\"/>\n"
+ " <container name=\"cont\"/>\n"
+ " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n"
+ " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n"
+ " <list name=\"sub-list\"/>\n"
+ " <uses name=\"uses-name\"/>\n"
+ " <augment target-node=\"target\"/>\n"
+ " <deviation target-node=\"target\">\n"
+ " <deviate value=\"not-supported\"/>\n"
+ " </deviation>\n"
+ " <extension name=\"ext\"/>\n"
+ " <feature name=\"feature\"/>\n"
+ " <grouping name=\"grp\"/>\n"
+ " <identity name=\"ident-name\"/>\n"
+ " <notification name=\"notf\"/>\n"
+ " <rpc name=\"rpc-name\"/>\n"
+ " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n"
+ EXT_SUBELEM "\n"
+ "</submodule>\n";
+ assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS);
+ assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
+
+ assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS);
+ assert_string_equal(lysp_submod->name, "mod");
+ assert_string_equal(lysp_submod->revs, "2019-02-02");
+ assert_string_equal(lysp_submod->prefix, "pref");
+ assert_null(lysp_submod->filepath);
+ assert_string_equal(lysp_submod->org, "org");
+ assert_string_equal(lysp_submod->contact, "contact");
+ assert_string_equal(lysp_submod->dsc, "desc");
+ assert_string_equal(lysp_submod->ref, "ref");
+ assert_int_equal(lysp_submod->version, LYS_VERSION_1_1);
+ CHECK_LYSP_IMPORT(lysp_submod->imports, NULL, 0, "a-mod",
+ "imp-pref", NULL, "");
+ assert_string_equal(lysp_submod->includes->name, "b-mod");
+ assert_string_equal(lysp_submod->extensions->name, "ext");
+ assert_string_equal(lysp_submod->features->name, "feature");
+ assert_string_equal(lysp_submod->identities->name, "ident-name");
+ assert_string_equal(lysp_submod->typedefs->name, "tpdf");
+ assert_string_equal(lysp_submod->groupings->name, "grp");
+ assert_string_equal(lysp_submod->data->name, "anyd");
+ assert_int_equal(lysp_submod->data->nodetype, LYS_ANYDATA);
+ assert_string_equal(lysp_submod->data->next->name, "anyx");
+ assert_int_equal(lysp_submod->data->next->nodetype, LYS_ANYXML);
+ assert_string_equal(lysp_submod->data->next->next->name, "choice");
+ assert_int_equal(lysp_submod->data->next->next->nodetype, LYS_CHOICE);
+ assert_string_equal(lysp_submod->data->next->next->next->name, "cont");
+ assert_int_equal(lysp_submod->data->next->next->next->nodetype, LYS_CONTAINER);
+ assert_string_equal(lysp_submod->data->next->next->next->next->name, "leaf");
+ assert_int_equal(lysp_submod->data->next->next->next->next->nodetype, LYS_LEAF);
+ assert_string_equal(lysp_submod->data->next->next->next->next->next->name, "llist");
+ assert_int_equal(lysp_submod->data->next->next->next->next->next->nodetype, LYS_LEAFLIST);
+ assert_string_equal(lysp_submod->data->next->next->next->next->next->next->name, "sub-list");
+ assert_int_equal(lysp_submod->data->next->next->next->next->next->next->nodetype, LYS_LIST);
+ assert_string_equal(lysp_submod->data->next->next->next->next->next->next->next->name, "uses-name");
+ assert_int_equal(lysp_submod->data->next->next->next->next->next->next->next->nodetype, LYS_USES);
+ assert_null(lysp_submod->data->next->next->next->next->next->next->next->next);
+ assert_string_equal(lysp_submod->augments->nodeid, "target");
+ assert_string_equal(lysp_submod->rpcs->name, "rpc-name");
+ assert_string_equal(lysp_submod->notifs->name, "notf");
+ assert_string_equal(lysp_submod->deviations->nodeid, "target");
+ TEST_1_CHECK_LYSP_EXT_INSTANCE(&(lysp_submod->exts[0]), LY_STMT_SUBMODULE);
+
+ /* min subelemnts */
+ ly_in_free(UTEST_IN, 0);
+ lyxml_ctx_free(YCTX->xmlctx);
+ lysp_submod = submod_renew(YCTX, "module-name");
+ data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <belongs-to module=\"module-name\"><prefix value=\"pref\"/></belongs-to>\n"
+ "</submodule>";
+ assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS);
+ assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
+ assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS);
+ assert_string_equal(lysp_submod->prefix, "pref");
+ assert_int_equal(lysp_submod->version, LYS_VERSION_1_0);
+
+ /* incorrect subelem order */
+ ly_in_free(UTEST_IN, 0);
+ lyxml_ctx_free(YCTX->xmlctx);
+ lysp_submod = submod_renew(YCTX, "module-name");
+ data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <reference><text>ref</text></reference>\n"
+ " <belongs-to module=\"module-name\"><prefix value=\"pref\"/></belongs-to>\n"
+ "</submodule>";
+ assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS);
+ assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
+ assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_EVALID);
+ CHECK_LOG_CTX("Invalid order of submodule's sub-elements \"belongs-to\" can't appear after \"reference\".", "Line number 4.");
+}
+
+static void
+test_yin_parse_module(void **state)
+{
+ const char *data;
+ struct lys_module *mod;
+ struct lysp_yin_ctx *yin_ctx = NULL;
+ struct ly_in *in = NULL;
+
+ mod = calloc(1, sizeof *mod);
+ mod->ctx = UTEST_LYCTX;
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"a\"> \n"
+ " <yang-version value=\"1.1\"/>\n"
+ " <namespace uri=\"urn:tests:extensions:metadata:a\"/>\n"
+ " <prefix value=\"a\"/>\n"
+ " <import module=\"ietf-yang-metadata\">\n"
+ " <prefix value=\"md\"/>\n"
+ " </import>\n"
+ " <feature name=\"f\"/>\n"
+ " <md:annotation name=\"x\">\n"
+ " <description>\n"
+ " <text>test</text>\n"
+ " </description>\n"
+ " <reference>\n"
+ " <text>test</text>\n"
+ " </reference>\n"
+ " <if-feature name=\"f\"/>\n"
+ " <status value=\"current\"/>\n"
+ " <type name=\"uint8\"/>\n"
+ " <units name=\"meters\"/>\n"
+ " </md:annotation>\n"
+ "</module>\n";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS);
+ assert_null(mod->parsed->exts->child->next->child);
+ assert_string_equal(mod->parsed->exts->child->next->arg, "test");
+ lys_module_free(&fctx, mod, 0);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ mod = NULL;
+ yin_ctx = NULL;
+
+ mod = calloc(1, sizeof *mod);
+ mod->ctx = UTEST_LYCTX;
+ data = "<module name=\"example-foo\""
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\""
+ " xmlns:foo=\"urn:example:foo\""
+ " xmlns:myext=\"urn:example:extensions\">\n"
+
+ " <yang-version value=\"1\"/>\n"
+
+ " <namespace uri=\"urn:example:foo\"/>\n"
+ " <prefix value=\"foo\"/>\n"
+
+ " <import module=\"example-extensions\">\n"
+ " <prefix value=\"myext\"/>\n"
+ " </import>\n"
+
+ " <list name=\"interface\">\n"
+ " <key value=\"name\"/>\n"
+ " <leaf name=\"name\">\n"
+ " <type name=\"string\"/>\n"
+ " </leaf>\n"
+ " <leaf name=\"mtu\">\n"
+ " <type name=\"uint32\"/>\n"
+ " <description>\n"
+ " <text>The MTU of the interface.</text>\n"
+ " </description>\n"
+ " <myext:c-define name=\"MY_MTU\"/>\n"
+ " </leaf>\n"
+ " </list>\n"
+ "</module>\n";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS);
+ lys_module_free(&fctx, mod, 0);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ mod = NULL;
+ yin_ctx = NULL;
+
+ mod = calloc(1, sizeof *mod);
+ mod->ctx = UTEST_LYCTX;
+ data = "<module name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <namespace uri=\"urn:example:foo\"/>\n"
+ " <prefix value=\"foo\"/>\n"
+ "</module>\n";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS);
+ lys_module_free(&fctx, mod, 0);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ mod = NULL;
+ yin_ctx = NULL;
+
+ mod = calloc(1, sizeof *mod);
+ mod->ctx = UTEST_LYCTX;
+ data = "<submodule name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+ "</submodule>\n";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EINVAL);
+ CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL);
+ lys_module_free(&fctx, mod, 0);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+
+ mod = calloc(1, sizeof *mod);
+ mod->ctx = UTEST_LYCTX;
+ data = "<module name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <namespace uri=\"urn:example:foo\"/>\n"
+ " <prefix value=\"foo\"/>\n"
+ "</module>\n"
+ "<module>";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EVALID);
+ CHECK_LOG_CTX("Trailing garbage \"<module>\" after module, expected end-of-input.", "Line number 6.");
+ lys_module_free(&fctx, mod, 0);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ mod = NULL;
+ yin_ctx = NULL;
+}
+
+static void
+test_yin_parse_submodule(void **state)
+{
+ const char *data;
+ struct lysp_yin_ctx *yin_ctx = NULL;
+ struct lysp_submodule *submod = NULL;
+ struct ly_in *in;
+
+ lydict_insert(UTEST_LYCTX, "a", 0, &PARSER_CUR_PMOD(YCTX)->mod->name);
+
+ data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<submodule name=\"asub\""
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\""
+ " xmlns:a=\"urn:a\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <belongs-to module=\"a\">\n"
+ " <prefix value=\"a_pref\"/>\n"
+ " </belongs-to>\n"
+ " <include module=\"atop\"/>\n"
+ " <feature name=\"fox\"/>\n"
+ " <notification name=\"bar-notif\">\n"
+ " <if-feature name=\"bar\"/>\n"
+ " </notification>\n"
+ " <notification name=\"fox-notif\">\n"
+ " <if-feature name=\"fox\"/>\n"
+ " </notification>\n"
+ " <augment target-node=\"/a_pref:top\">\n"
+ " <if-feature name=\"bar\"/>\n"
+ " <container name=\"bar-sub\"/>\n"
+ " </augment>\n"
+ " <augment target-node=\"/top\">\n"
+ " <container name=\"bar-sub2\"/>\n"
+ " </augment>\n"
+ "</submodule>";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_SUCCESS);
+ lysp_module_free(&fctx, (struct lysp_module *)submod);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ yin_ctx = NULL;
+ submod = NULL;
+
+ data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <belongs-to module=\"a\">\n"
+ " <prefix value=\"a_pref\"/>\n"
+ " </belongs-to>\n"
+ "</submodule>";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_SUCCESS);
+ lysp_module_free(&fctx, (struct lysp_module *)submod);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ yin_ctx = NULL;
+ submod = NULL;
+
+ data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"inval\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+ "</module>";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_EINVAL);
+ CHECK_LOG_CTX("Input data contains module when a submodule is expected.", NULL);
+ lysp_module_free(&fctx, (struct lysp_module *)submod);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ yin_ctx = NULL;
+ submod = NULL;
+
+ data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <belongs-to module=\"a\">\n"
+ " <prefix value=\"a_pref\"/>\n"
+ " </belongs-to>\n"
+ "</submodule>\n"
+ "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+ " <yang-version value=\"1\"/>\n"
+ " <belongs-to module=\"a\">\n"
+ " <prefix value=\"a_pref\"/>\n"
+ " </belongs-to>\n"
+ "</submodule>";
+ assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
+ assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_EVALID);
+ CHECK_LOG_CTX("Trailing garbage \"<submodule name...\" after submodule, expected end-of-input.", "Line number 8.");
+ lysp_module_free(&fctx, (struct lysp_module *)submod);
+ lysp_yin_ctx_free(yin_ctx);
+ ly_in_free(in, 0);
+ yin_ctx = NULL;
+ submod = NULL;
+}
+
+int
+main(void)
+{
+
+ const struct CMUnitTest tests[] = {
+ UTEST(test_yin_match_keyword, setup, teardown),
+ UTEST(test_yin_parse_content, setup, teardown),
+ UTEST(test_validate_value, setup, teardown),
+ UTEST(test_valid_module),
+ UTEST(test_print_module),
+ UTEST(test_print_submodule),
+
+ UTEST(test_yin_match_argument_name),
+ cmocka_unit_test_setup_teardown(test_enum_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_bit_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_status_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_yin_element_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_yangversion_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_argument_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_belongsto_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_config_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_default_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_err_app_tag_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_err_msg_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_fracdigits_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_iffeature_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_length_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_modifier_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_namespace_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_pattern_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_value_position_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_prefix_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_range_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_reqinstance_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_revision_date_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_unique_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_units_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_yin_text_value_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_type_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_max_elems_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_min_elems_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_ordby_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_any_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_leaf_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_leaf_list_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_presence_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_key_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_uses_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_list_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_notification_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_grouping_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_container_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_case_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_choice_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_inout_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_action_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_augment_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_deviate_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_deviation_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_module_elem, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_submodule_elem, setup, teardown),
+
+ cmocka_unit_test_setup_teardown(test_yin_parse_module, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_yin_parse_submodule, setup, teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c
new file mode 100644
index 0000000..05b6b97
--- /dev/null
+++ b/tests/utests/types/binary.c
@@ -0,0 +1,269 @@
+/**
+ * @file binary.c
+ * @author Michal Vaško <mvasko@cesnet.cz>
+ * @brief test for built-in binary type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_plugin_store(void **state)
+{
+ const char *val, *dec_val;
+ unsigned char bin_val[2];
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value value = {0};
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BINARY]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* check proper type */
+ assert_string_equal("libyang 2 - binary, version 1", type->id);
+
+ /* check store XML double pad */
+ val = "YWhveQ==";
+ dec_val = "ahoy";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* single pad */
+ val = "YWhveWo=";
+ dec_val = "ahoyj";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* no pad */
+ val = "YWhveWoy";
+ dec_val = "ahoyj2";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* binary data */
+ val = "q80=";
+ bin_val[0] = 0xab;
+ bin_val[1] = 0xcd;
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, bin_val, 2,
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* newlines after every 64 chars */
+ val = "MIIEAzCCAuugAwIBAgIURc4sipHvJSlNrQIhRhZilBvV4RowDQYJKoZIhvcNAQEL\n"
+ "BQAwgZAxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBNb3JhdmlhMQ0wCwYD\n"
+ "VQQHDARCcm5vMRgwFgYDVQQKDA9DRVNORVQgei5zLnAuby4xDDAKBgNVBAsMA1RN\n"
+ "QzETMBEGA1UEAwwKZXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs\n"
+ "ZS5vcmcwHhcNMjEwOTAzMTAyMTAxWhcNMzEwOTAxMTAyMTAxWjCBkDELMAkGA1UE\n"
+ "BhMCQ1oxFjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xGDAW\n"
+ "BgNVBAoMD0NFU05FVCB6LnMucC5vLjEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApl\n"
+ "eGFtcGxlIENBMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLm9yZzCCASIwDQYJ\n"
+ "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAN4Ld3JDDocyy9KXNJhEUPeZpQW3UdUN\n"
+ "Xloeh5n/bxasgThkBuQ7oF/nKyVUe517U1CJA993ZIc0jhIWThAnqXkz70DX5EZ7\n"
+ "ancPd01MidA6T8k1RYYJWr+vyIRYYBYzK7LSnU6wMWqPTgzZB+KMWwb065ooLEB5\n"
+ "XwqAeTIMPLRqM1Galewl4ZSuRJnrXxRjfF3AWNyC9dZw6wIg8cppvoLdBGQiFJQf\n"
+ "9SgiVy+HyedAytFEixqKAAIgQUJwhCgbEd6jGFbeaL8HT4MFp1VmaaUBQMkZj/Gn\n"
+ "KBwCk5BEMu76EN1pzHc4Dd6DabQNGCnsqOPe31yhQGmNFy9R6zNnWZMCAwEAAaNT\n"
+ "MFEwHQYDVR0OBBYEFM7w/pO8vk5oowvWPoCKo0RW/JcnMB8GA1UdIwQYMBaAFM7w\n"
+ "/pO8vk5oowvWPoCKo0RW/JcnMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n"
+ "BQADggEBAG/xfYuRKnCyiwYC/K7kAjHmCNnLCr1mx8P1ECsSJPme3OThDTNeGf8i\n"
+ "N2952tGmMFDa+DaAwPc6Gt3cWTb/NYMTLWlt2yj5rJAcLXxIU0SMafBf+F7E/R8A\n"
+ "b/HDDjs0pQaJ0EJhQJVkMdfj3Wq9l0dJT5iEBUrUQflDufiMdEJEIGKZh86MgzEL\n"
+ "bcn1QX8dlLc91M2OifWStqLzXPicG+jjuoPUceC0flMQDb2qx03sxvJKfYfS5ArA\n"
+ "CqvdWyXLoP7DI9THJrMI/vBHJKpl4Wtmsh2OLn9VHauFMzPSGke5GwjXCpbXGepj\n"
+ "9qWN8Gd/FWgSDH2OBvZ6aHdB1pPjN9k=";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* empty value */
+ val = "";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, "", "", 0);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* short value */
+ val = "YQ==";
+ dec_val = "a";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /*
+ * ERROR TESTS
+ */
+ val = "q80.";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ assert_string_equal(err->msg, "Invalid Base64 character '.'.");
+ ly_err_free(err);
+
+ val = "q80";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ assert_string_equal(err->msg, "Base64 encoded value length must be divisible by 4.");
+ ly_err_free(err);
+}
+
+static void
+test_plugin_print(void **state)
+{
+ const char *schema, *val;
+ struct lyd_value value = {0};
+ struct lys_module *mod;
+ struct lysc_type *lysc_type;
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BINARY]);
+ struct ly_err_item *err = NULL;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* Testing empty value. */
+ val = "";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ assert_string_equal("", value.realtype->plugin->print(UTEST_LYCTX, &(value), LY_VALUE_CANON, NULL, NULL, NULL));
+ type->free(UTEST_LYCTX, &value);
+}
+
+static void
+test_plugin_duplicate(void **state)
+{
+ const char *schema, *val;
+ struct lyd_value value = {0}, dup;
+ struct lys_module *mod;
+ struct lysc_type *lysc_type;
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BINARY]);
+ struct ly_err_item *err = NULL;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* Testing empty value. */
+ val = "";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &value, &dup));
+ CHECK_LYD_VALUE(dup, BINARY, "", "", 0);
+ type->free(UTEST_LYCTX, &value);
+ type->free(UTEST_LYCTX, &dup);
+}
+
+static void
+test_data_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb", "leaf port {type binary;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "port", "");
+ TEST_SUCCESS_LYB("lyb", "port", "YWhveQ==");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_plugin_store),
+ UTEST(test_plugin_print),
+ UTEST(test_plugin_duplicate),
+ UTEST(test_data_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c
new file mode 100644
index 0000000..3d42ebc
--- /dev/null
+++ b/tests/utests/types/bits.c
@@ -0,0 +1,1117 @@
+/**
+ * @file bits.c
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @brief test for int8 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":\"" DATA "\"}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_ERROR_JSON(MOD_NAME, DATA) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_schema_yang(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_type_bits *lysc_type;
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero;\nbit one;"
+ " bit ten{position 10;}\tbit \"eleven\"; bit last{position 4294967295; }}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ schema = MODULE_CREATE_YANG("T1", "leaf port {type bits { bit _ten {position 10;} bit _ten-one;"
+ " bit _two {position 2;} bit ten_end...;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ /* TEST MODULE SUBTYPE */
+ schema = MODULE_CREATE_YANG("T2", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ schema = MODULE_CREATE_YANG("T3", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}"
+ "leaf port {type my_type {"
+ " bit ten {position 10;} bit two;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ /*
+ * TEST ERROR
+ */
+ /* test change bit possition */
+ schema = MODULE_CREATE_YANG("TERR_0", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit \"twelve\";}}"
+ "leaf port {type my_type {"
+ " bit ten {position 11;} bit two;}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", "/TERR_0:port");
+
+ /* add new bit */
+ schema = MODULE_CREATE_YANG("TERR_1", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}"
+ "leaf port {type my_type {"
+ " bit ten {position 10;} bit two; bit test;}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "/TERR_1:port");
+
+ /* different max value => autoadd index */
+ schema = MODULE_CREATE_YANG("TERR_2", "leaf port {type bits {"
+ " bit first {position -1;} bit second;"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL,
+ "Invalid value \"-1\" of \"position\".", "Line number 5.");
+
+ /* different max value => autoadd index */
+ schema = MODULE_CREATE_YANG("TERR_3", "leaf port {type bits {"
+ " bit first {position 4294967295;} bit second;"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid bits - it is not possible to auto-assign bit position for \"second\" since the highest value is already 4294967295.",
+ "/TERR_3:port");
+
+ schema = MODULE_CREATE_YANG("TERR_4", "leaf port {type bits {"
+ " bit first {position 10;} bit \"\";"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL,
+ "Statement argument is required.", "Line number 5.");
+
+ /* wrong character */
+ schema = MODULE_CREATE_YANG("TERR_5", "leaf port {type bits {"
+ " bit first {position 10;} bit abcd^;"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL,
+ "Invalid identifier character '^' (0x005e).", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERR_6", "leaf port {type bits {"
+ " bit hi; bit hi;"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL,
+ "Duplicate identifier \"hi\" of bit statement.", "Line number 5.");
+
+ /* wrong character */
+ schema = MODULE_CREATE_YANG("TERR_7", "leaf port {type bits {"
+ " bit first {position 10;} bit \"ab&cd\";"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL,
+ "Invalid identifier character '&' (0x0026).", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERR_8", "leaf port {type bits {"
+ " bit first {position 10;} bit \"4abcd\";"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL,
+ "Invalid identifier first character '4' (0x0034).", "Line number 5.");
+
+ schema = MODULE_CREATE_YANG("TERR_9", "leaf port {type bits;}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Missing bit substatement for bits type.", "/TERR_9:port");
+
+ /* new features of YANG 1.1 in YANG 1.0 */
+ schema = "module TERR_10 {"
+ " namespace \"urn:tests:TERR_10\";"
+ " prefix pref;"
+ " feature f;"
+ " leaf l {type bits {"
+ " bit one {if-feature f;}"
+ " }}"
+ "}";
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_10\" failed.", NULL,
+ "Invalid keyword \"if-feature\" as a child of \"bit\" - the statement is allowed only in YANG 1.1 modules.",
+ "Line number 1.");
+
+ schema = "module TERR_11 {"
+ " namespace \"urn:tests:TERR_10\";"
+ " prefix pref;"
+ " typedef mytype {type bits {bit one;}}"
+ " leaf l {type mytype {bit one;}}"
+ "}";
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Bits type can be subtyped only in YANG 1.1 modules.", "/TERR_11:l");
+
+ /* feature is not present */
+ schema = MODULE_CREATE_YANG("IF_0", "feature f;"
+ "leaf port {type bits { bit zero;\nbit one;"
+ " bit ten{if-feature f; position 10;}\tbit eleven;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_int_equal(LY_ENOT, lys_feature_value (mod, "f"));
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ /* feature is present */
+ schema = MODULE_CREATE_YANG("IF_1", "feature f;"
+ "leaf port {type bits { bit zero;\nbit one;"
+ " bit ten{position 10; if-feature f;}\tbit eleven;}}");
+ const char *IF_1_FEATURES[] = {"f", NULL};
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, IF_1_FEATURES, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+}
+
+static void
+test_schema_yin(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_type_bits *lysc_type;
+
+ schema = MODULE_CREATE_YIN("T0",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"zero\"/> <bit name=\"one\"/>"
+ " <bit name=\"ten\"> <position value=\"10\"/> </bit>"
+ " <bit name=\"eleven\"/>"
+ " <bit name=\"last\"> <position value=\"4294967295\"/> </bit>"
+ "</type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ schema = MODULE_CREATE_YIN("T1",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"_ten\"> <position value=\"10\"/> </bit> <bit name=\"_ten-one\"/>"
+ " <bit name=\"_two\"> <position value=\"2\"/> </bit> <bit name=\"ten_end...\"/>"
+ "</type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ /* TEST MODULE SUBTYPE */
+ schema = MODULE_CREATE_YIN("T2",
+ "<typedef name=\"my_type\"> <type name=\"bits\">"
+ " <bit name=\"ten\"> <position value=\"10\"/> </bit>"
+ " <bit name=\"eleven\"/> <bit name=\"two\"> <position value=\"2\"/> </bit>"
+ " <bit name=\"twelve\"/>"
+ "</type> </typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"/></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ schema = MODULE_CREATE_YIN("T3", "<typedef name=\"my_type\"> <type name=\"bits\">"
+ " <bit name=\"ten\"> <position value=\"10\"/></bit>"
+ " <bit name=\"eleven\"/> <bit name=\"two\"> <position value=\"2\"/> </bit>"
+ " <bit name=\"twelve\"/>"
+ "</type></typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ " <bit name=\"ten\"> <position value=\"10\"/> </bit>"
+ " <bit name=\"two\"/>"
+ "</type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ 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);
+ 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);
+
+ /*
+ * TEST ERROR
+ */
+ /* test change bit possition */
+ schema = MODULE_CREATE_YIN("TERR_0", "<typedef name=\"my_type\"> <type name=\"bits\">"
+ " <bit name=\"ten\"> <position value=\"10\"/> </bit>"
+ " <bit name=\"eleven\"/>"
+ " <bit name=\"two\"> <position value=\"2\"/> </bit>"
+ " <bit name=\"twelve\"/>"
+ "</type></typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ " <bit name=\"ten\"> <position value=\"11\"/> </bit> <bit name=\"two\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", "/TERR_0:port");
+
+ /* add new bit */
+ schema = MODULE_CREATE_YIN("TERR_1",
+ "<typedef name=\"my_type\"> <type name=\"bits\">"
+ " <bit name=\"ten\"> <position value=\"10\"/> </bit>"
+ " <bit name=\"eleven\"/>"
+ " <bit name=\"two\"> <position value=\"2\"/> </bit>"
+ " <bit name=\"twelve\"/>"
+ "</type></typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ " <bit name=\"ten\"> <position value=\"10\"/> </bit>"
+ " <bit name=\"two\"/>"
+ " <bit name=\"test\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "/TERR_1:port");
+
+ /* different max value => autoadd index */
+ schema = MODULE_CREATE_YIN("TERR_2",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"first\"> <position value=\"-1\"> </bit>"
+ " <bit name=\"second\">"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL,
+ "Invalid value \"-1\" of \"value\" attribute in \"position\" element.", "Line number 8.");
+
+ /* different max value => autoadd index */
+ schema = MODULE_CREATE_YIN("TERR_3",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"first\"> <position value=\"4294967295\"/> </bit>"
+ " <bit name=\"second\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid bits - it is not possible to auto-assign bit position for \"second\" since the highest value is already 4294967295.",
+ "/TERR_3:port");
+
+ schema = MODULE_CREATE_YIN("TERR_4",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\" ahoj \"> <position value=\"20\"/> </bit>"
+ " <bit name=\"second\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL,
+ "Invalid identifier first character ' ' (0x0020).",
+ "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_5",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"ah oj\"> <position value=\"20\"/> </bit>"
+ " <bit name=\"second\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL,
+ "Invalid identifier character ' ' (0x0020).",
+ "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_6",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"hi\"/> "
+ " <bit name=\"hi\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL,
+ "Duplicate identifier \"hi\" of bit statement.",
+ "Line number 8.");
+
+ schema = MODULE_CREATE_YIN("TERR_7",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"4ahoj\"> <position value=\"20\"/> </bit>"
+ " <bit name=\"second\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL,
+ "Invalid identifier first character '4' (0x0034).",
+ "Line number 8.");
+
+ /* TEST EMPTY NAME*/
+ schema = MODULE_CREATE_YIN("TERR_8",
+ "<leaf name=\"port\"> <type name=\"bits\">"
+ " <bit name=\"\"> <position value=\"20\"/> </bit>"
+ " <bit name=\"second\"/>"
+ "</type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL,
+ "Empty identifier is not allowed.",
+ "Line number 8.");
+}
+
+static void
+test_schema_print(void **state)
+{
+ const char *schema_yang, *schema_yin;
+ char *printed;
+ struct lys_module *mod;
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT0",
+ "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}"
+ "leaf port {type my_type {"
+ " bit ten {position 10;} bit two;}}");
+
+ schema_yin = MODULE_CREATE_YIN("PRINT0",
+ " <typedef name=\"my_type\">\n"
+ " <type name=\"bits\">\n"
+ " <bit name=\"ten\">\n"
+ " <position value=\"10\"/>\n"
+ " </bit>\n"
+ " <bit name=\"eleven\"/>\n"
+ " <bit name=\"two\">\n"
+ " <position value=\"2\"/>\n"
+ " </bit>\n"
+ " <bit name=\"twelve\"/>\n"
+ " </type>\n"
+ " </typedef>\n"
+ " <leaf name=\"port\">\n"
+ " <type name=\"my_type\">\n"
+ " <bit name=\"ten\">\n"
+ " <position value=\"10\"/>\n"
+ " </bit>\n"
+ " <bit name=\"two\"/>\n"
+ " </type>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT1",
+ "\n"
+ " typedef my_type {\n"
+ " type bits {\n"
+ " bit ten {\n"
+ " position 10;\n"
+ " }\n"
+ " bit eleven;\n"
+ " bit two {\n"
+ " position 2;\n"
+ " }\n"
+ " bit twelve;\n"
+ " }\n"
+ " }\n\n"
+ " leaf port {\n"
+ " type my_type {\n"
+ " bit ten {\n"
+ " position 10;\n"
+ " }\n"
+ " bit two;\n"
+ " }\n"
+ " }\n");
+
+ schema_yin = MODULE_CREATE_YIN("PRINT1",
+ " <typedef name=\"my_type\">\n"
+ " <type name=\"bits\">\n"
+ " <bit name=\"ten\">\n"
+ " <position value=\"10\"/>\n"
+ " </bit>\n"
+ " <bit name=\"eleven\"/>\n"
+ " <bit name=\"two\">\n"
+ " <position value=\"2\"/>\n"
+ " </bit>\n"
+ " <bit name=\"twelve\"/>\n"
+ " </type>\n"
+ " </typedef>\n"
+ " <leaf name=\"port\">\n"
+ " <type name=\"my_type\">\n"
+ " <bit name=\"ten\">\n"
+ " <position value=\"10\"/>\n"
+ " </bit>\n"
+ " <bit name=\"two\"/>\n"
+ " </type>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+}
+
+static void
+test_data_xml(void **state)
+{
+
+ const char *schema;
+ struct lyd_node *tree;
+ const char *data;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("T0", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;"
+ " bit _test-end...;}}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("T0", "ten two twelve", BITS, "two ten twelve", "two", "ten", "twelve");
+ TEST_SUCCESS_XML("T0", "ten\ntwo\ttwelve", BITS, "two ten twelve", "two", "ten", "twelve");
+ TEST_SUCCESS_XML("T0", "ten two", BITS, "two ten", "two", "ten");
+ TEST_SUCCESS_XML("T0", "_test-end...", BITS, "_test-end...", "_test-end...");
+ TEST_SUCCESS_XML("T0", "twelve\nten\ttwo \n eleven", BITS, "two ten eleven twelve",
+ "two", "ten", "eleven", "twelve");
+ TEST_SUCCESS_XML("T0", "", BITS, "");
+ TEST_SUCCESS_XML("T0", "\n\t", BITS, "");
+
+ TEST_ERROR_XML("T0", "twelvea");
+ CHECK_LOG_CTX("Invalid bit \"twelvea\".",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_XML("T0", "twelve t");
+ CHECK_LOG_CTX("Invalid bit \"t\".",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_XML("T0", "ELEVEN");
+ CHECK_LOG_CTX("Invalid bit \"ELEVEN\".",
+ "Schema location \"/T0:port\", line number 1.");
+
+ /* empty value */
+ data = "<port xmlns=\"urn:tests:T0\"/>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, BITS, "");
+ lyd_free_all(tree);
+
+}
+
+static void
+test_data_json(void **state)
+{
+ const char *schema;
+
+ /* variable for default value test */
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("T0", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;"
+ " bit _test-end...;}}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_JSON("T0", "ten two twelve", BITS, "two ten twelve", "two", "ten", "twelve");
+ TEST_SUCCESS_JSON("T0", "ten\\ntwo\\ttwelve", BITS, "two ten twelve", "two", "ten", "twelve");
+ TEST_SUCCESS_JSON("T0", "ten two", BITS, "two ten", "two", "ten");
+ TEST_SUCCESS_JSON("T0", "_test-end...", BITS, "_test-end...", "_test-end...");
+ TEST_SUCCESS_JSON("T0", "twelve\\nten\\ttwo \\n eleven", BITS, "two ten eleven twelve",
+ "two", "ten", "eleven", "twelve");
+ TEST_SUCCESS_JSON("T0", "", BITS, "");
+ TEST_SUCCESS_JSON("T0", "\\n\\t", BITS, "");
+
+ TEST_ERROR_JSON("T0", "twelvea");
+ CHECK_LOG_CTX("Invalid character sequence \"twelvea}\", expected a JSON value.",
+ "Line number 1.");
+ TEST_ERROR_JSON("T0", "twelve t");
+ CHECK_LOG_CTX("Invalid character sequence \"twelve t}\", expected a JSON value.",
+ "Line number 1.");
+ TEST_ERROR_JSON("T0", "ELEVEN");
+ CHECK_LOG_CTX("Invalid character sequence \"ELEVEN}\", expected a JSON value.",
+ "Line number 1.");
+}
+
+static void
+test_data_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb", "typedef my_type{type bits {"
+ " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;"
+ " bit _test-end...;}}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "port", "ten twelve");
+ TEST_SUCCESS_LYB("lyb", "port", "two");
+ TEST_SUCCESS_LYB("lyb", "port", "");
+}
+
+static void
+test_diff(void **state)
+{
+ const char *schema;
+ struct lyd_node *model_1, *model_2;
+ struct lyd_node *diff;
+ const char *expected_string;
+ const char *data_1;
+ const char *data_2;
+ const char *diff_expected;
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero; bit one;"
+ " bit two; bit three;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ data_1 = "<port xmlns=\"urn:tests:T0\"> two three </port>";
+ data_2 = "<port xmlns=\"urn:tests:T0\"> one</port>";
+ diff_expected = "<port xmlns=\"urn:tests:T0\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"replace\""
+ " yang:orig-default=\"false\" yang:orig-value=\"two three\">one</port>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1)
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2)
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+
+ /* create data from diff */
+ diff_expected = "<port xmlns=\"urn:tests:T0\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"replace\""
+ " yang:orig-default=\"false\" yang:orig-value=\"two three \"></port>";
+ CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, diff)
+ data_1 = "<port xmlns=\"urn:tests:T0\"> two three </port>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1)
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ expected_string = "<port xmlns=\"urn:tests:T0\"/>";
+
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ lyd_free_all(model_1);
+ lyd_free_all(diff);
+
+ /* create data from diff */
+ diff_expected = "<port xmlns=\"urn:tests:T0\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"replace\""
+ " yang:orig-default=\"false\" yang:orig-value=\"two three\"> one </port>";
+ CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, diff)
+ data_1 = "<port xmlns=\"urn:tests:T0\"> two three </port>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1)
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ expected_string = "<port xmlns=\"urn:tests:T0\">one</port>";
+
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ lyd_free_all(model_1);
+ lyd_free_all(diff);
+
+}
+
+static void
+test_print(void **state)
+{
+ const char *schema;
+ const char *expected_string;
+ struct lyd_node *model_1;
+ const char *data;
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero; bit one;"
+ " bit two; bit three;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* print zero bits */
+ data = "<port xmlns=\"urn:tests:T0\"></port>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1)
+ /* XML */
+ expected_string = "<port xmlns=\"urn:tests:T0\"/>";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ /* JSON */
+ expected_string = "{\"T0:port\":\"\"}";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ /* free */
+ lyd_free_all(model_1);
+
+ /* print one bit */
+ data = "<port xmlns=\"urn:tests:T0\"> two </port>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1)
+ /* XML */
+ expected_string = "<port xmlns=\"urn:tests:T0\">two</port>";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ /* JSON */
+ expected_string = "{\"T0:port\":\"two\"}";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ /* free */
+ lyd_free_all(model_1);
+
+ /* print two bits */
+ data = "<port xmlns=\"urn:tests:T0\">three two </port>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1)
+ /* XML */
+ expected_string = "<port xmlns=\"urn:tests:T0\">two three</port>";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ /* JSON */
+ expected_string = "{\"T0:port\":\"two three\"}";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ /* free */
+ lyd_free_all(model_1);
+}
+
+static void
+test_plugin_store(void **state)
+{
+ const char *val_text = NULL;
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value value = {0};
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BITS]);
+ struct lysc_type *lysc_type;
+ struct lysc_type lysc_type_test;
+ LY_ERR ly_ret;
+ char *alloc;
+ const char *schema;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero; bit one;"
+ " bit two; bit three;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* check proper type */
+ assert_string_equal("libyang 2 - bits, version 1", type->id);
+
+ /* check store
+ */
+ val_text = "";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BITS, "");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "zero one two";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BITS, "zero one two", "zero", "one", "two");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "zero two";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BITS, "zero two", "zero", "two");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "\n ";
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, BITS, "");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /*
+ * minor tests
+ * dynamic alocated input text
+ */
+ val_text = "two";
+ alloc = (char *)malloc(strlen(val_text) + 1);
+ memcpy(alloc, val_text, strlen(val_text) + 1);
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, alloc, strlen(val_text),
+ LYPLG_TYPE_STORE_DYNAMIC, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ alloc = NULL;
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, BITS, "two", "two");
+ type->free(UTEST_LYCTX, &value);
+
+ /* wrong lysc_type of value */
+ lysc_type_test = *lysc_type;
+ lysc_type_test.basetype = LY_TYPE_INT8;
+ val_text = "two";
+ ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EINVAL, ly_ret);
+ ly_err_free(err);
+
+ /*
+ * ERROR TESTS
+ */
+ val_text = "two";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+ val_text = "two two";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+}
+
+static void
+test_plugin_compare(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BITS]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+ /* different type */
+ const char *diff_type_text = "20";
+ struct lyd_value diff_type_val;
+ struct lysc_type *diff_type;
+ /* Value which are going to be created to tested */
+ const char *val_init[] = {"", "two zero", "three", "zero two", "zero", "three"};
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0", "typedef my_int_type { type bits { bit zero; bit one;"
+ " bit two; bit three;}}"
+ "leaf p1 {type my_int_type;}"
+ "leaf p2 {type my_int_type;}"
+ "leaf p3 {type my_int_type{bit three; bit zero;}}"
+ "leaf p4 {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /*
+ * BASIC TEST;
+ */
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[0]), &(values[0])));
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[1]), &(values[3])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[0]), &(values[1])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[3]), &(values[4])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[1]), &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[1]), &(values[2])));
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[2]), &(values[5])));
+
+ /*
+ * SAME TYPE but different node
+ */
+ diff_type_text = val_init[2];
+ diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_SUCCESS, type->compare(&diff_type_val, &(values[2])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /*
+ * derivated type add some limitations
+ */
+ diff_type_text = val_init[2];
+ diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[2])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /*
+ * different type (STRING)
+ */
+ diff_type_text = val_init[2];
+ diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[2])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /* delete values */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+static void
+test_plugin_print(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BITS]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+ /* Value which are going to be created to tested */
+ const char *val_init[] = {"", "two zero", "three", "zero two", "zero", "three"};
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0",
+ "leaf p1 { type bits { bit zero; bit one;"
+ " bit two; bit three;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* print value */
+ ly_bool dynamic = 0;
+
+ assert_string_equal("", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("zero two", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("three", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("zero two", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("zero", type->print(UTEST_LYCTX, &(values[4]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("three", type->print(UTEST_LYCTX, &(values[5]), LY_VALUE_XML, NULL, &dynamic, NULL));
+
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+static void
+test_plugin_dup(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BITS]);
+ struct lysc_type *lysc_type;
+ const char *schema;
+ LY_ERR ly_ret;
+ /* Value which are going to be tested */
+ const char *val_init[] = {"", "two zero", "three", "zero two", "zero", "three one two zero"};
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0",
+ "leaf p1 { type bits { bit zero; bit one;"
+ " bit two; bit three;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* print duplicate value */
+ struct lyd_value dup_value;
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[0]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, BITS, "");
+ assert_ptr_equal(dup_value.realtype, values[0].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[1]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, BITS, "zero two", "zero", "two");
+ assert_ptr_equal(dup_value.realtype, values[1].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[2]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, BITS, "three", "three");
+ assert_ptr_equal(dup_value.realtype, values[2].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[3]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, BITS, "zero two", "zero", "two");
+ assert_ptr_equal(dup_value.realtype, values[3].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[4]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, BITS, "zero", "zero");
+ assert_ptr_equal(dup_value.realtype, values[4].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[5]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, BITS, "zero one two three", "zero", "one", "two", "three");
+ assert_ptr_equal(dup_value.realtype, values[5].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ /* error tests */
+ assert_int_equal(LY_EINVAL, type->duplicate(NULL, &(values[0]), &dup_value));
+
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema_yang),
+ UTEST(test_schema_yin),
+ UTEST(test_schema_print),
+ UTEST(test_data_xml),
+ UTEST(test_data_json),
+ UTEST(test_data_lyb),
+ UTEST(test_diff),
+ UTEST(test_print),
+
+ UTEST(test_plugin_store),
+ UTEST(test_plugin_compare),
+ UTEST(test_plugin_print),
+ UTEST(test_plugin_dup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/boolean.c b/tests/utests/types/boolean.c
new file mode 100644
index 0000000..841db2a
--- /dev/null
+++ b/tests/utests/types/boolean.c
@@ -0,0 +1,111 @@
+/**
+ * @file boolean.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "typedef tboolean {type boolean;}"
+ "leaf l1 {type boolean;}"
+ "leaf l2 {type tboolean;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "l1", "true", BOOL, "true", 1);
+
+ TEST_SUCCESS_XML("defs", "l1", "false", BOOL, "false", 0);
+
+ TEST_SUCCESS_XML("defs", "l2", "false", BOOL, "false", 0);
+
+ /* invalid value */
+ TEST_ERROR_XML("defs", "l1", "unsure");
+ CHECK_LOG_CTX("Invalid boolean value \"unsure\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", " true");
+ CHECK_LOG_CTX("Invalid boolean value \" true\".",
+ "Schema location \"/defs:l1\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "leaf bool {type boolean;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "bool", "true");
+ TEST_SUCCESS_LYB("lyb", "bool", "false");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/decimal64.c b/tests/utests/types/decimal64.c
new file mode 100644
index 0000000..e0a7cab
--- /dev/null
+++ b/tests/utests/types/decimal64.c
@@ -0,0 +1,125 @@
+/**
+ * @file decimal64.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf l1 {type decimal64 {fraction-digits 1; range 1.5..10;}}"
+ "leaf l2 {type decimal64 {fraction-digits 18;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "l1", "\n +8 \t\n ", DEC64, "8.0", 80);
+ TEST_SUCCESS_XML("defs", "l1", "8.00", DEC64, "8.0", 80);
+
+ TEST_SUCCESS_XML("defs", "l2", "-9.223372036854775808", DEC64, "-9.223372036854775808",
+ INT64_C(-9223372036854775807) - INT64_C(1));
+ TEST_SUCCESS_XML("defs", "l2", "9.223372036854775807", DEC64, "9.223372036854775807", INT64_C(9223372036854775807));
+
+ TEST_ERROR_XML("defs", "l1", "\n 15 \t\n ");
+ CHECK_LOG_CTX("Unsatisfied range - value \"15.0\" is out of the allowed range.",
+ "Schema location \"/defs:l1\", line number 3.");
+
+ TEST_ERROR_XML("defs", "l1", "\n 0 \t\n ");
+ CHECK_LOG_CTX("Unsatisfied range - value \"0.0\" is out of the allowed range.",
+ "Schema location \"/defs:l1\", line number 3.");
+
+ TEST_ERROR_XML("defs", "l1", "xxx");
+ CHECK_LOG_CTX("Invalid 1. character of decimal64 value \"xxx\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", "");
+ CHECK_LOG_CTX("Invalid empty decimal64 value.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", "8.5 xxx");
+ CHECK_LOG_CTX("Invalid 6. character of decimal64 value \"8.5 xxx\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", "8.55 xxx");
+ CHECK_LOG_CTX("Value \"8.55\" of decimal64 type exceeds defined number (1) of fraction digits.",
+ "Schema location \"/defs:l1\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "dec64", "8.00");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/empty.c b/tests/utests/types/empty.c
new file mode 100644
index 0000000..0ab9710
--- /dev/null
+++ b/tests/utests/types/empty.c
@@ -0,0 +1,108 @@
+/**
+ * @file empty.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "typedef tempty {type empty;}"
+ "leaf l1 {type empty;}"
+ "leaf l2 {type tempty;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "l1", "", EMPTY, "");
+
+ TEST_SUCCESS_XML("defs", "l2", "", EMPTY, "");
+
+ /* invalid value */
+ TEST_ERROR_XML("defs", "l1", "x");
+ CHECK_LOG_CTX("Invalid empty value length 1.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", " ");
+ CHECK_LOG_CTX("Invalid empty value length 1.",
+ "Schema location \"/defs:l1\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "leaf empty {type empty;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "empty", "");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/enumeration.c b/tests/utests/types/enumeration.c
new file mode 100644
index 0000000..660479f
--- /dev/null
+++ b/tests/utests/types/enumeration.c
@@ -0,0 +1,114 @@
+/**
+ * @file enumeration.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "feature f; leaf l1 {type enumeration {enum white; enum yellow {if-feature f;}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "l1", "white", ENUM, "white", "white");
+
+ /* disabled feature */
+ TEST_ERROR_XML("defs", "l1", "yellow");
+ CHECK_LOG_CTX("Invalid enumeration value \"yellow\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ /* leading/trailing whitespaces */
+ TEST_ERROR_XML("defs", "l1", " white");
+ CHECK_LOG_CTX("Invalid enumeration value \" white\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", "white\n");
+ CHECK_LOG_CTX("Invalid enumeration value \"white\n\".",
+ "Schema location \"/defs:l1\", line number 2.");
+
+ /* invalid value */
+ TEST_ERROR_XML("defs", "l1", "black");
+ CHECK_LOG_CTX("Invalid enumeration value \"black\".",
+ "Schema location \"/defs:l1\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb", "leaf l1 {type enumeration {enum white; enum yellow; enum black;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "l1", "white");
+ TEST_SUCCESS_LYB("lyb", "l1", "black");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/identityref.c b/tests/utests/types/identityref.c
new file mode 100644
index 0000000..cdfe057
--- /dev/null
+++ b/tests/utests/types/identityref.c
@@ -0,0 +1,123 @@
+/**
+ * @file identityref.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema, *schema2;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("ident-base", "identity ident-base;"
+ "identity ident-imp {base ident-base;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ schema2 = MODULE_CREATE_YANG("defs", "import ident-base {prefix ib;}"
+ "identity ident1 {base ib:ident-base;}"
+ "leaf l1 {type identityref {base ib:ident-base;}}");
+ UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "", "l1", "ident1", IDENT, "defs:ident1", "ident1");
+
+ TEST_SUCCESS_XML("defs", "xmlns:i=\"urn:tests:ident-base\"", "l1", "i:ident-imp", IDENT, "ident-base:ident-imp",
+ "ident-imp");
+
+ /* invalid value */
+ TEST_ERROR_XML("defs", "", "l1", "fast-ethernet");
+ CHECK_LOG_CTX("Invalid identityref \"fast-ethernet\" value - identity not found in module \"defs\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "xmlns:x=\"urn:tests:defs\"", "l1", "x:slow-ethernet");
+ CHECK_LOG_CTX("Invalid identityref \"x:slow-ethernet\" value - identity not found in module \"defs\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "xmlns:x=\"urn:tests:ident-base\"", "l1", "x:ident-base");
+ CHECK_LOG_CTX("Invalid identityref \"x:ident-base\" value - identity not derived from the base \"ident-base:ident-base\".",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "xmlns:x=\"urn:tests:unknown\"", "l1", "x:ident-base");
+ CHECK_LOG_CTX("Invalid identityref \"x:ident-base\" value - unable to map prefix to YANG schema.",
+ "Schema location \"/defs:l1\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "identity idbase;"
+ "identity ident {base idbase;}"
+ "leaf lf {type identityref {base idbase;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "lf", "ident");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/inet_types.c b/tests/utests/types/inet_types.c
new file mode 100644
index 0000000..eb4e480
--- /dev/null
+++ b/tests/utests/types/inet_types.c
@@ -0,0 +1,149 @@
+/**
+ * @file inet_types.c
+ * @author Michal Vaško <mvasko@cesnet.cz>
+ * @brief test for ietf-inet-types values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ " import ietf-inet-types {\n" \
+ " prefix inet;\n" \
+ " }\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("a",
+ "leaf l {type inet:ip-address;}"
+ "leaf l2 {type inet:ipv6-address;}"
+ "leaf l3 {type inet:ip-address-no-zone;}"
+ "leaf l4 {type inet:ipv6-address-no-zone;}"
+ "leaf l5 {type inet:ip-prefix;}"
+ "leaf l6 {type inet:ipv4-prefix;}"
+ "leaf l7 {type inet:ipv6-prefix;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* ip-address */
+ TEST_SUCCESS_XML("a", "l", "192.168.0.1", UNION, "192.168.0.1", STRING, "192.168.0.1");
+ TEST_SUCCESS_XML("a", "l", "192.168.0.1%12", UNION, "192.168.0.1%12", STRING, "192.168.0.1%12");
+ TEST_SUCCESS_XML("a", "l", "2008:15:0:0:0:0:feAC:1", UNION, "2008:15::feac:1", STRING, "2008:15::feac:1");
+
+ /* ipv6-address */
+ TEST_SUCCESS_XML("a", "l2", "FAAC:21:011:Da85::87:daaF%1", STRING, "faac:21:11:da85::87:daaf%1");
+
+ /* ip-address-no-zone */
+ TEST_SUCCESS_XML("a", "l3", "127.0.0.1", UNION, "127.0.0.1", STRING, "127.0.0.1");
+ TEST_SUCCESS_XML("a", "l3", "0:00:000:0000:000:00:0:1", UNION, "::1", STRING, "::1");
+
+ /* ipv6-address-no-zone */
+ TEST_SUCCESS_XML("a", "l4", "A:B:c:D:e:f:1:0", STRING, "a:b:c:d:e:f:1:0");
+
+ /* ip-prefix */
+ TEST_SUCCESS_XML("a", "l5", "158.1.58.4/1", UNION, "128.0.0.0/1", STRING, "128.0.0.0/1");
+ TEST_SUCCESS_XML("a", "l5", "158.1.58.4/24", UNION, "158.1.58.0/24", STRING, "158.1.58.0/24");
+ TEST_SUCCESS_XML("a", "l5", "2000:A:B:C:D:E:f:a/16", UNION, "2000::/16", STRING, "2000::/16");
+
+ /* ipv4-prefix */
+ TEST_SUCCESS_XML("a", "l6", "0.1.58.4/32", STRING, "0.1.58.4/32");
+ TEST_SUCCESS_XML("a", "l6", "12.1.58.4/8", STRING, "12.0.0.0/8");
+
+ /* ipv6-prefix */
+ TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/112", STRING, "::c:d:e:f:0/112");
+ TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/110", STRING, "::c:d:e:c:0/110");
+ TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/96", STRING, "::c:d:e:0:0/96");
+ TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/55", STRING, "::/55");
+}
+
+static void
+test_data_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "leaf l {type inet:ip-address;}"
+ "leaf l2 {type inet:ipv6-address;}"
+ "leaf l3 {type inet:ip-address-no-zone;}"
+ "leaf l4 {type inet:ipv6-address-no-zone;}"
+ "leaf l5 {type inet:ip-prefix;}"
+ "leaf l6 {type inet:ipv4-prefix;}"
+ "leaf l7 {type inet:ipv6-prefix;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_LYB("lyb", "l", "192.168.0.1");
+ TEST_SUCCESS_LYB("lyb", "l2", "FAAC:21:011:Da85::87:daaF%1");
+ TEST_SUCCESS_LYB("lyb", "l3", "127.0.0.1");
+ TEST_SUCCESS_LYB("lyb", "l4", "A:B:c:D:e:f:1:0");
+ TEST_SUCCESS_LYB("lyb", "l5", "158.1.58.4/1");
+ TEST_SUCCESS_LYB("lyb", "l6", "12.1.58.4/8");
+ TEST_SUCCESS_LYB("lyb", "l7", "::C:D:E:f:a/112");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_data_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/instanceid.c b/tests/utests/types/instanceid.c
new file mode 100644
index 0000000..06c8622
--- /dev/null
+++ b/tests/utests/types/instanceid.c
@@ -0,0 +1,292 @@
+/**
+ * @file instanceid.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 1, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, RET) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, RET, tree); \
+ assert_null(tree); \
+ }
+
+#define LYB_CHECK_START \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data;
+
+#define LYB_CHECK_END \
+ { \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME1, DATA1, NODE_NAME2, DATA2) \
+ LYB_CHECK_START \
+ data = "<" NODE_NAME1 " xmlns=\"urn:tests:" MOD_NAME "\">" DATA1 "</" NODE_NAME1 ">" \
+ "<xdf:" NODE_NAME2 " xmlns:xdf=\"urn:tests:" MOD_NAME "\">/xdf:" DATA2 "</xdf:" NODE_NAME2 ">"; \
+ LYB_CHECK_END \
+
+#define TEST_SUCCESS_LYB2(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ LYB_CHECK_START \
+ data = "<" NODE_NAME " xmlns:aa=\"urn:tests:lyb2\" xmlns=\"urn:tests:" MOD_NAME "\">/aa:" DATA "</" NODE_NAME ">"; \
+ LYB_CHECK_END \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema, *schema2;
+ const enum ly_path_pred_type val1[] = {LY_PATH_PREDTYPE_NONE, LY_PATH_PREDTYPE_NONE};
+ const enum ly_path_pred_type val2[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
+ const enum ly_path_pred_type val3[] = {LY_PATH_PREDTYPE_LEAFLIST};
+ const enum ly_path_pred_type val4[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
+ const enum ly_path_pred_type val5[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
+ const enum ly_path_pred_type val6[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("mod", "container cont {leaf l2 {type empty;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ schema2 = MODULE_CREATE_YANG("defs", "identity ident; identity ident-der1 {base ident;} identity ident-der2 {base ident;}"
+ "leaf l1 {type instance-identifier {require-instance true;}}"
+ "leaf l2 {type instance-identifier {require-instance false;}}"
+ "container cont {leaf l {type empty;}}"
+ "list list {key \"id\"; leaf id {type string;} leaf value {type string;}}"
+ "leaf-list llist {type uint32;}"
+ "list list-inst {key \"id\"; leaf id {type instance-identifier;} leaf value {type string;}}"
+ "list list-ident {key \"id\"; leaf id {type identityref {base ident;}} leaf value {type string;}}"
+ "list list2 {key \"id id2\"; leaf id {type string;} leaf id2 {type string;}}"
+ "list list-keyless {config false; leaf value {type string;}}");
+ UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML2("<cont xmlns=\"urn:tests:defs\"><l/></cont>", "defs", "xmlns:xdf=\"urn:tests:defs\"", "l1",
+ "/xdf:cont/xdf:l", INST, "/defs:cont/l", val1);
+
+ TEST_SUCCESS_XML2("<list xmlns=\"urn:tests:defs\"><id>a</id></list><list xmlns=\"urn:tests:defs\"><id>b</id></list>",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:list[xdf:id='b']/xdf:id", INST,
+ "/defs:list[id='b']/id", val2);
+
+ TEST_SUCCESS_XML2("<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:llist[.='1']", INST, "/defs:llist[.='1']", val3);
+
+ TEST_SUCCESS_XML2("<list-inst xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">/b:llist[.='1']</id>"
+ "<value>x</value></list-inst>"
+ "<list-inst xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">/b:llist[.='2']</id>"
+ "<value>y</value></list-inst>"
+ "<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list-inst[a:id=\"/a:llist[.='1']\"]/a:value",
+ INST, "/defs:list-inst[id=\"/defs:llist[.='1']\"]/value", val4);
+
+ TEST_SUCCESS_XML2("<list-ident xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">b:ident-der1</id>"
+ "<value>x</value></list-ident>"
+ "<list-ident xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">b:ident-der2</id>"
+ "<value>y</value></list-ident>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list-ident[a:id='a:ident-der1']/a:value",
+ INST, "/defs:list-ident[id='defs:ident-der1']/value", val5);
+
+ TEST_SUCCESS_XML2("<list2 xmlns=\"urn:tests:defs\"><id>defs:xxx</id><id2>x</id2></list2>"
+ "<list2 xmlns=\"urn:tests:defs\"><id>a:xxx</id><id2>y</id2></list2>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list2[a:id='a:xxx'][a:id2='y']/a:id2",
+ INST, "/defs:list2[id='a:xxx'][id2='y']/id2", val6);
+
+ /* syntax/semantic errors */
+ TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>a</id></list>"
+ "<list xmlns=\"urn:tests:defs\"><id>b</id><value>x</value></list>",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:list[2]/xdf:value", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[2]/xdf:value\" value - semantic error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/t:cont/t:1l", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont/t:1l\" value - syntax error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/t:cont:t:1l", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont:t:1l\" value - syntax error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:cont/xdf:invalid/xdf:path", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:cont/xdf:invalid/xdf:path\" value - semantic error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ /* non-existing instances, instance-identifier is here in JSON format because it is already in internal
+ * representation without canonical prefixes */
+ TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>",
+ "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2", LY_ENOTFOUND);
+ CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/mod:cont/l2\" value - required instance not found.",
+ "Data location \"/defs:l1\".", "instance-required");
+
+ TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:llist[.='2']", LY_ENOTFOUND);
+ CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:llist[.='2']\" value - required instance not found.",
+ "Data location \"/defs:l1\".", "instance-required");
+
+ TEST_ERROR_XML2("<list2 xmlns=\"urn:tests:defs\"><id>a</id><id2>a</id2></list2>"
+ "<list2 xmlns=\"urn:tests:defs\"><id>c</id><id2>b</id2></list2>"
+ "<llist xmlns=\"urn:tests:defs\">a</llist>"
+ "<llist xmlns=\"urn:tests:defs\">b</llist>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:list2[a:id='a'][a:id2='a']/a:id", LY_ENOTFOUND);
+ CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:list2[id='a'][id2='a']/id\" value - required instance not found.",
+ "Data location \"/defs:l1\".", "instance-required");
+
+ TEST_ERROR_XML2("<list2 xmlns=\"urn:tests:defs\"><id>a</id><id2>a</id2></list2>"
+ "<list2 xmlns=\"urn:tests:defs\"><id>c</id><id2>b</id2></list2>"
+ "<llist xmlns=\"urn:tests:defs\">1</llist>"
+ "<llist xmlns=\"urn:tests:defs\">2</llist>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:llist[.='3']", LY_ENOTFOUND);
+ CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:llist[.='3']\" value - required instance not found.",
+ "Data location \"/defs:l1\".", "instance-required");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:list-keyless[3]", LY_ENOTFOUND);
+ CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:list-keyless[3]\" value - required instance not found.",
+ "Data location \"/defs:l1\".", "instance-required");
+
+ /* more errors */
+ TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>",
+ "defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[1", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1\" value - syntax error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>",
+ "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont[1]", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont[1]\" value - semantic error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>",
+ "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "[1]", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"[1]\" value - syntax error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"><l2/></cont>",
+ "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2[l2='1']", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[l2='1']\" value - syntax error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"><l2/></cont>",
+ "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2[m:l2='1']", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[m:l2='1']\" value - semantic error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>",
+ "defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[4]", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[4]\" value - semantic error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[6]", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[6]\" value - semantic error.",
+ "Schema location \"/defs:l2\", line number 1.");
+
+ TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>1</id><value>x</value></list>",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[xdf:value='x']", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:value='x']\" value - semantic error.",
+ "Schema location \"/defs:l2\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[.='x']", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[.='x']\" value - semantic error.",
+ "Schema location \"/defs:l2\", line number 1.");
+
+ TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist>",
+ "defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[.='x']", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='x']\" value - semantic error.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[1][2]", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1][2]\" value - syntax error.",
+ "Schema location \"/defs:l2\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[.='a'][.='b']", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='a'][.='b']\" value - syntax error.",
+ "Schema location \"/defs:l2\", line number 1.");
+
+ TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>1</id><value>x</value></list>",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value\" value - syntax error.",
+ "Schema location \"/defs:l2\", line number 1.");
+
+ TEST_ERROR_XML2("",
+ "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list2[xdf:id='1']/xdf:value", LY_EVALID);
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list2[xdf:id='1']/xdf:value\" value - semantic error.",
+ "Schema location \"/defs:l2\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "leaf-list leaflisttarget {type string;}"
+ "leaf inst {type instance-identifier {require-instance true;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "leaflisttarget", "1", "inst", "leaflisttarget[.='1']");
+
+ /* ietf-netconf-acm node-instance-identifier type */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+ schema = MODULE_CREATE_YANG("lyb2",
+ "import ietf-netconf-acm {prefix acm;}"
+ "leaf-list ll {type string;}"
+ "leaf nii {type acm:node-instance-identifier;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB2("lyb2", "nii", "ll[. = 'some_string']");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/instanceid_keys.c b/tests/utests/types/instanceid_keys.c
new file mode 100644
index 0000000..6d527cb
--- /dev/null
+++ b/tests/utests/types/instanceid_keys.c
@@ -0,0 +1,77 @@
+/**
+ * @file instanceid_keys.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for yang instance-identifier-keys type
+ *
+ * Copyright (c) 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML_NS1(MOD_NAME, NODE_NAME, PREFIX, NS, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" xmlns:" PREFIX "=\"" NS "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "import yang {prefix y;} leaf l1 {type y:instance-identifier-keys;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML_NS1("defs", "l1", "px", "urn:tests:defs", "[px:key='val']", STRING, "[defs:key='val']");
+
+ TEST_ERROR_XML("defs", "l1", "black");
+ CHECK_LOG_CTX("Invalid first character 'b', list key predicates expected.", "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", "[this is not a valid xpath]");
+ CHECK_LOG_CTX("Invalid character 0x69 ('i'), perhaps \"this\" is supposed to be a function call.",
+ "Schema location \"/defs:l1\", line number 1.");
+
+ TEST_ERROR_XML("defs", "l1", "[px:key='val']");
+ CHECK_LOG_CTX("Failed to resolve prefix \"px\".", "Schema location \"/defs:l1\", line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/int16.c b/tests/utests/types/int16.c
new file mode 100644
index 0000000..f6796a1
--- /dev/null
+++ b/tests/utests/types/int16.c
@@ -0,0 +1,75 @@
+/**
+ * @file int16.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for int16 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int16 {range -20..-10;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("defs", "100");
+ CHECK_LOG_CTX("Unsatisfied range - value \"100\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/int32.c b/tests/utests/types/int32.c
new file mode 100644
index 0000000..9989a67
--- /dev/null
+++ b/tests/utests/types/int32.c
@@ -0,0 +1,75 @@
+/**
+ * @file int32.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for int32 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int32;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("defs", "0x01");
+ CHECK_LOG_CTX("Invalid type int32 value \"0x01\".",
+ "Schema location \"/defs:port\", line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/int64.c b/tests/utests/types/int64.c
new file mode 100644
index 0000000..6b5e47e
--- /dev/null
+++ b/tests/utests/types/int64.c
@@ -0,0 +1,83 @@
+/**
+ * @file int64.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for int32 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int64;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("defs", "");
+ CHECK_LOG_CTX("Invalid type int64 empty value.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ TEST_ERROR_XML("defs", " ");
+ CHECK_LOG_CTX("Invalid type int64 empty value.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ TEST_ERROR_XML("defs", "-10 xxx");
+ CHECK_LOG_CTX("Invalid type int64 value \"-10 xxx\".",
+ "Schema location \"/defs:port\", line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/int8.c b/tests/utests/types/int8.c
new file mode 100644
index 0000000..7d0b9ad
--- /dev/null
+++ b/tests/utests/types/int8.c
@@ -0,0 +1,1765 @@
+/**
+ * @file int8.c
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @brief test for int8 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define LYD_TREE_CREATE(INPUT, MODEL) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL)
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_ERROR_JSON(MOD_NAME, DATA) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_schema_yang(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_range *range;
+
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 127\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, 0);
+ assert_int_equal(range->parts[0].max_64, 50);
+ assert_int_equal(range->parts[1].min_64, 127);
+ assert_int_equal(range->parts[1].max_64, 127);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T0 */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type int8;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ 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, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST MODULE T1 */
+ schema = MODULE_CREATE_YANG("T1", "leaf port {type int8 {range \"0 .. 50 |51 .. 60\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, 0);
+ assert_int_equal(range->parts[0].max_64, 50);
+ assert_int_equal(range->parts[1].min_64, 51);
+ assert_int_equal(range->parts[1].max_64, 60);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 |51 .. 60", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T1 */
+ schema = MODULE_CREATE_YANG("T2", "leaf port {type int8 {range \"20\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, 20);
+ assert_int_equal(range->parts[0].max_64, 20);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "20", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T3 */
+ schema = MODULE_CREATE_YANG("T3", "leaf port {type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL);
+ assert_int_equal(range->parts[0].min_64, -128);
+ assert_int_equal(range->parts[0].max_64, -60);
+ assert_int_equal(range->parts[1].min_64, -1);
+ assert_int_equal(range->parts[1].max_64, 1);
+ assert_int_equal(range->parts[2].min_64, 60);
+ assert_int_equal(range->parts[2].max_64, 127);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "-128 .. -60 | -1 .. 1 | 60 .. 127", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T4 */
+ schema = MODULE_CREATE_YANG("T4", "leaf port {type int8 {range \"1 .. 1\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, 1);
+ assert_int_equal(range->parts[0].max_64, 1);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "1 .. 1", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T4 */
+ schema = MODULE_CREATE_YANG("T5", "leaf port {type int8 {range \"7\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, 7);
+ assert_int_equal(range->parts[0].max_64, 7);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "7", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T4 */
+ schema = MODULE_CREATE_YANG("T6", "leaf port {type int8 {range \"min .. max\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, -128);
+ assert_int_equal(range->parts[0].max_64, 127);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST ERROR -60 .. 0 | 0 .. 127 */
+ schema = MODULE_CREATE_YANG("ERR0", "leaf port {type int8 {range \"-60 .. 0 | 0 .. 127\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EEXIST);
+ CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "/ERR0:port");
+
+ /* TEST ERROR 0 .. 128 */
+ schema = MODULE_CREATE_YANG("ERR1", "leaf port {type int8 {range \"0 .. 128\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "/ERR1:port");
+
+ /* TEST ERROR -129 .. 126 */
+ schema = MODULE_CREATE_YANG("ERR2", "leaf port {type int8 {range \"-129 .. 0\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/ERR2:port");
+
+ /* TEST ERROR 0 */
+ schema = MODULE_CREATE_YANG("ERR3", "leaf port {type int8 {range \"-129\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/ERR3:port");
+
+ /*
+ * TEST MODULE SUBTYPE
+ */
+ schema = MODULE_CREATE_YANG("TS0",
+ "typedef my_int_type {"
+ " type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}"
+ "}"
+ "leaf my_leaf {type my_int_type; }");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL);
+ assert_int_equal(range->parts[0].min_64, -128);
+ assert_int_equal(range->parts[0].max_64, -60);
+ assert_int_equal(range->parts[1].min_64, -1);
+ assert_int_equal(range->parts[1].max_64, 1);
+ assert_int_equal(range->parts[2].min_64, 60);
+ assert_int_equal(range->parts[2].max_64, 127);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_int_type", 0, 0, 1, 0, 0, 0);
+
+ /* TEST SUBTYPE RANGE */
+ schema = MODULE_CREATE_YANG("TS1",
+ "typedef my_int_type {"
+ " type int8 {range \"-100 .. -60 | -1 .. 1 | 60 .. 127\";}"
+ "}"
+ "leaf my_leaf {type my_int_type {range \"min .. -60\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, -100);
+ assert_int_equal(range->parts[0].max_64, -60);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. -60", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST SUBTYPE RANGE */
+ schema = MODULE_CREATE_YANG("TS2",
+ "typedef my_int_type {"
+ " type int8 {range \"-100 .. -60 | -1 .. 1 | 60 .. 120\";}"
+ "}"
+ "leaf my_leaf {type my_int_type {range \"70 .. max\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, 70);
+ assert_int_equal(range->parts[0].max_64, 120);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "70 .. max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST SUBTYPE RANGE */
+ schema = MODULE_CREATE_YANG("TS3",
+ "typedef my_int_type {"
+ " type int8 {range \"-100 .. -60 | -1 .. 1 | 60 .. 127\";}"
+ "}"
+ "leaf my_leaf {type my_int_type {range \"-1 .. 1\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, -1);
+ assert_int_equal(range->parts[0].max_64, 1);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "-1 .. 1", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST SUBTYPE RANGE */
+ schema = MODULE_CREATE_YANG("TS4",
+ "typedef my_int_type {"
+ " type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}"
+ "}"
+ "leaf my_leaf {type my_int_type { "
+ " range \"min .. -60 | -1 .. 1 | 60 .. max\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL);
+ assert_int_equal(range->parts[0].min_64, -128);
+ assert_int_equal(range->parts[0].max_64, -60);
+ assert_int_equal(range->parts[1].min_64, -1);
+ assert_int_equal(range->parts[1].max_64, 1);
+ assert_int_equal(range->parts[2].min_64, 60);
+ assert_int_equal(range->parts[2].max_64, 127);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. -60 | -1 .. 1 | 60 .. max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST SUBTYPE ERROR min .. max */
+ schema = MODULE_CREATE_YANG("TS_ERR0",
+ "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}"
+ "leaf my_leaf {type my_int_type {range \"min .. max\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid range restriction - the derived restriction (min .. max) is not equally or more limiting.",
+ "/TS_ERR0:my_leaf");
+
+ /* TEST SUBTYPE ERROR -80 .. 80 */
+ schema = MODULE_CREATE_YANG("TS_ERR1",
+ "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}"
+ " leaf my_leaf {type my_int_type {range \"-80 .. 80\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid range restriction - the derived restriction (-80 .. 80) is not equally or more limiting.",
+ "/TS_ERR1:my_leaf");
+
+ /* TEST SUBTYPE ERROR 0 .. max */
+ schema = MODULE_CREATE_YANG("TS_ERR2",
+ "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}"
+ "leaf my_leaf {type my_int_type {range \"0 .. max\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid range restriction - the derived restriction (0 .. max) is not equally or more limiting.",
+ "/TS_ERR2:my_leaf");
+
+ /* TEST SUBTYPE ERROR -2 .. 2 */
+ schema = MODULE_CREATE_YANG("TS_ERR3",
+ "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}"
+ "leaf my_leaf {type my_int_type {range \"-2 .. 2\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid range restriction - the derived restriction (-2 .. 2) is not equally or more limiting.",
+ "/TS_ERR3:my_leaf");
+
+ /* TEST SUBTYPE ERROR -2 .. 2 */
+ schema = MODULE_CREATE_YANG("TS_ERR4",
+ "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}"
+ "leaf my_leaf {type my_int_type {range \"-100 .. -90 | 100 .. 128\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.",
+ "/TS_ERR4:my_leaf");
+
+ /*
+ * TEST DEFAULT VALUE
+ */
+ schema = MODULE_CREATE_YANG("DF0",
+ "leaf port {"
+ " type int8 {range \"0 .. 50 | 127\";}"
+ " default \"20\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "20", 20);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, 0);
+ assert_int_equal(range->parts[0].max_64, 50);
+ assert_int_equal(range->parts[1].min_64, 127);
+ assert_int_equal(range->parts[1].max_64, 127);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "20");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST DEFAULT VALUE */
+ schema = MODULE_CREATE_YANG("DF1", "leaf port {type int8 {range \"0 .. 50 | 127\";}"
+ "default \"127\"; }");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "127", 127);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, 0);
+ assert_int_equal(range->parts[0].max_64, 50);
+ assert_int_equal(range->parts[1].min_64, 127);
+ assert_int_equal(range->parts[1].max_64, 127);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "127");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST DEFAULT VALUE ERROR */
+ schema = MODULE_CREATE_YANG("TD_ERR0",
+ "leaf port {"
+ " type int8 {range \"0 .. 50 | 127\";}"
+ " default \"128\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"128\" is out of type int8 min/max bounds.).",
+ "Schema location \"/TD_ERR0:port\".");
+
+ /* TEST DEFAULT VALUE ERROR */
+ schema = MODULE_CREATE_YANG("TD_ERR1",
+ "leaf port {"
+ " type int8 {range \"0 .. 50 | 127\";}"
+ " default \"-1\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"-1\" is out of the allowed range.).",
+ "Schema location \"/TD_ERR1:port\".");
+
+ /* TEST DEFAULT VALUE ERROR */
+ schema = MODULE_CREATE_YANG("TD_ERR2",
+ "leaf port {"
+ " type int8 {range \"0 .. 50 | 127\";}"
+ " default \"60\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"60\" is out of the allowed range.).",
+ "Schema location \"/TD_ERR2:port\".");
+
+ /* TEST DEFAULT VALUE ERROR */
+ schema = MODULE_CREATE_YANG("TD_ERR3",
+ "typedef my_int_type { type int8 {range \"60 .. 127\";} default \"127\";}"
+ "leaf my_leaf {type my_int_type {range \"70 .. 80\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"127\" is out of the allowed range.).",
+ "Schema location \"/TD_ERR3:my_leaf\".");
+
+ /* TEST DEFAULT HEXADECIMAL */
+ schema = MODULE_CREATE_YANG("DF_HEX0",
+ "leaf port {"
+ " type int8;"
+ " default \"0xf\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "0xf");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT HEXADECIMAL */
+ schema = MODULE_CREATE_YANG("DF_HEX1",
+ "leaf port {"
+ " type int8;"
+ " default \"-0xf\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-0xf");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT HEXADECIMAL */
+ schema = MODULE_CREATE_YANG("DF_HEXI0",
+ "leaf port {"
+ " type int8 {range \"0 .. 50 | 127\";}"
+ " default \"+0x7F\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "127", 127);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, 0);
+ assert_int_equal(range->parts[0].max_64, 50);
+ assert_int_equal(range->parts[1].min_64, 127);
+ assert_int_equal(range->parts[1].max_64, 127);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+0x7F");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST DEFAULT HEXADECIMAL ERROR */
+ schema = MODULE_CREATE_YANG("DF_HEX2",
+ "leaf port {"
+ " type int8;"
+ " default \"0xff\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0xff\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_HEX2:port\".");
+
+ /* TEST DEFAULT HEXADECIMAL ERROR */
+ schema = MODULE_CREATE_YANG("DF_HEX3",
+ "leaf port {"
+ " type int8;"
+ " default \"-0x81\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0x81\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_HEX3:port\".");
+
+ /* TEST DEFAULT HEXADECIMAL ERROR */
+ schema = MODULE_CREATE_YANG("DF_HEX4",
+ "leaf port {"
+ " type int8;"
+ " default \"0x80\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0x80\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_HEX4:port\".");
+
+ /* TEST DEFAULT VALUE OCTAL */
+ schema = MODULE_CREATE_YANG("DF_OCT0",
+ "leaf port {"
+ " type int8;"
+ " default \"017\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "017");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE OCTAL */
+ schema = MODULE_CREATE_YANG("DF_OCT1",
+ "leaf port {"
+ " type int8;"
+ " default \"-017\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-017");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE OCTAL */
+ schema = MODULE_CREATE_YANG("DF_OCTI0",
+ "leaf port {"
+ " type int8;"
+ " default \"+017\";"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+017");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE OCTAL ERROR*/
+ schema = MODULE_CREATE_YANG("DF_OCT2",
+ "leaf port {"
+ " type int8;"
+ " default \"0377\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0377\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_OCT2:port\".");
+
+ /* TEST DEFAULT VALUE OCTAL ERROR*/
+ schema = MODULE_CREATE_YANG("DF_OCT3",
+ "leaf port {"
+ " type int8;"
+ " default \"-0201\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0201\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_OCT3:port\".");
+
+ /* TEST DEFAULT VALUE OCTAL ERROR*/
+ schema = MODULE_CREATE_YANG("DF_OCT4",
+ "leaf port {"
+ " type int8;"
+ " default \"0200\";"
+ "}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0200\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_OCT4:port\".");
+}
+
+static void
+test_schema_yin(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_range *range;
+
+ /* TEST T0 */
+ schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\"> <type name=\"int8\"/> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ 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, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST T1 */
+ schema = MODULE_CREATE_YIN("T1",
+ "<leaf name=\"port\"> "
+ " <type name=\"int8\"> <range value = \"0 .. 10\"/> </type>"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_64, 0);
+ assert_int_equal(range->parts[0].max_64, 10);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 10", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST T1 */
+ schema = MODULE_CREATE_YIN("T2",
+ "<leaf name=\"port\"> "
+ " <type name=\"int8\"> <range value = \"-127 .. 10 | max\"/> </type>"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, -127);
+ assert_int_equal(range->parts[0].max_64, 10);
+ assert_int_equal(range->parts[1].min_64, 127);
+ assert_int_equal(range->parts[1].max_64, 127);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "-127 .. 10 | max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST T2 */
+ schema = MODULE_CREATE_YIN("T3",
+ "<leaf name=\"port\"> "
+ " <type name=\"int8\"> <range value =\"min .. 10 | 11 .. 12 | 30\"/> </type>"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL);
+ assert_int_equal(range->parts[0].min_64, -128);
+ assert_int_equal(range->parts[0].max_64, 10);
+ assert_int_equal(range->parts[1].min_64, 11);
+ assert_int_equal(range->parts[1].max_64, 12);
+ assert_int_equal(range->parts[2].min_64, 30);
+ assert_int_equal(range->parts[2].max_64, 30);
+ 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, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. 10 | 11 .. 12 | 30", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST ERROR -60 .. 0 | 0 .. 127 */
+ schema = MODULE_CREATE_YIN("TE0",
+ "<leaf name=\"port\"> "
+ " <type name=\"int8\"> <range value = \"min .. 0 | 0 .. 12\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EEXIST);
+ CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "/TE0:port");
+
+ /* TEST ERROR 0 .. 128 */
+ schema = MODULE_CREATE_YIN("TE1",
+ "<leaf name=\"port\">"
+ " <type name=\"int8\"> <range value = \"0 .. 128\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "/TE1:port");
+
+ /* TEST ERROR -129 .. 126 */
+ schema = MODULE_CREATE_YIN("TE2",
+ "<leaf name=\"port\"> "
+ " <type name=\"int8\"> <range value =\"-129 .. 126\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/TE2:port");
+
+ /* TEST YIN */
+ schema = MODULE_CREATE_YIN("TS0",
+ "<typedef name= \"my_int_type\">"
+ " <type name=\"int8\"> <range value = \"-127 .. 10 | max\"/> </type>"
+ "</typedef>"
+ "<leaf name=\"my_leaf\"> <type name=\"my_int_type\"/> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, -127);
+ assert_int_equal(range->parts[0].max_64, 10);
+ assert_int_equal(range->parts[1].min_64, 127);
+ assert_int_equal(range->parts[1].max_64, 127);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_int_type", 0, 0, 1, 0, 0, 0);
+
+ /* TEST YIN */
+ schema = MODULE_CREATE_YIN("TS1",
+ "<typedef name= \"my_int_type\">"
+ " <type name=\"int8\"> <range value = \"-127 .. 10 | 90 .. 100\"/> </type>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_int_type\"> <range value ="
+ " \"min .. -30 | 100 .. max\"/> </type> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, -127);
+ assert_int_equal(range->parts[0].max_64, -30);
+ assert_int_equal(range->parts[1].min_64, 100);
+ assert_int_equal(range->parts[1].max_64, 100);
+ 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, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. -30 | 100 .. max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST ERROR */
+ schema = MODULE_CREATE_YIN("TS_ERR1",
+ "<typedef name= \"my_int_type\">"
+ " <type name=\"int8\"> <range value = \"-127 .. 10 | 90 .. 100\"/> </type>"
+ "</typedef>"
+ "<leaf name=\"port\">"
+ " <type name=\"my_int_type\"> <range value = \"min .. max\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid range restriction - the derived restriction (min .. max) is not equally or more limiting.",
+ "/TS_ERR1:port");
+
+ /* TEST ERROR */
+ schema = MODULE_CREATE_YIN("TS_ERR2",
+ "<typedef name= \"my_int_type\">"
+ " <type name=\"int8\"> <range value = \"-127 .. 10 | 90 .. 100\"/> </type>"
+ "</typedef>"
+ "<leaf name=\"port\">"
+ " <type name=\"my_int_type\"> <range value = \"5 .. 11\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid range restriction - the derived restriction (5 .. 11) is not equally or more limiting.",
+ "/TS_ERR2:port");
+
+ /* TEST DEFAULT VALUE */
+ schema = MODULE_CREATE_YIN("DF0",
+ "<leaf name=\"port\">"
+ " <default value=\"12\" />"
+ " <type name=\"int8\"> <range value = \"min .. 0 | 1 .. 12\"/> </type>"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "12", 12);
+ range = ((struct lysc_type_num *)lysc_leaf->type)->range;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_64, -128);
+ assert_int_equal(range->parts[0].max_64, 0);
+ assert_int_equal(range->parts[1].min_64, 1);
+ assert_int_equal(range->parts[1].max_64, 12);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "12");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. 0 | 1 .. 12", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST ERROR TD0 */
+ schema = MODULE_CREATE_YIN("TD_ERR0",
+ "<leaf name=\"port\">"
+ " <default value=\"128\" />"
+ " <type name=\"int8\"> <range value = \"min .. 0 | 1 .. 12\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"128\" is out of type int8 min/max bounds.).",
+ "Schema location \"/TD_ERR0:port\".");
+
+ /* TEST ERROR TD1 */
+ schema = MODULE_CREATE_YIN("TD_ERR1",
+ "<leaf name=\"port\">"
+ " <default value=\"13\" />"
+ " <type name=\"int8\"> <range value = \"min .. 0 | 1 .. 12\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"13\" is out of the allowed range.).",
+ "Schema location \"/TD_ERR1:port\".");
+
+ /* TEST ERROR TD1 */
+ schema = MODULE_CREATE_YIN("TD_ERR3",
+ "<typedef name= \"my_int_type\">"
+ " <default value=\"10\" />"
+ " <type name=\"int8\"> <range value = \"-127 .. 10 | max\"/> </type>"
+ "</typedef>"
+ "<leaf name=\"my_leaf\">"
+ " <type name=\"my_int_type\">"
+ " <range value = \"-127 .. -80\"/> </type>"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"10\" is out of the allowed range.).",
+ "Schema location \"/TD_ERR3:my_leaf\".");
+
+ /* TEST DEFAULT VALUE HEXADECIMAL */
+ schema = MODULE_CREATE_YIN("DF_HEX0",
+ "<leaf name=\"port\">"
+ " <default value=\"+0xf\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+0xf");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE HEXADECIMAL */
+ schema = MODULE_CREATE_YIN("DF_HEX1",
+ "<leaf name=\"port\">"
+ " <default value=\"-0xf\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-0xf");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE HEXADECIMAL ERROR */
+ schema = MODULE_CREATE_YIN("DF_HEX2",
+ "<leaf name=\"port\">"
+ " <default value=\"0xff\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0xff\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_HEX2:port\".");
+
+ /* TEST DEFAULT VALUE HEXADECIMAL ERROR */
+ schema = MODULE_CREATE_YIN("DF_HEX2",
+ "<leaf name=\"port\">"
+ " <default value=\"-0x81\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0x81\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_HEX2:port\".");
+
+ /* TEST DEFAULT VALUE HEXADECIMAL ERROR */
+ schema = MODULE_CREATE_YIN("DF_HEX4",
+ "<leaf name=\"port\">"
+ " <default value=\"0x80\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0x80\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_HEX4:port\".");
+
+ /* TEST DEFAULT VALUE OCTAL */
+ schema = MODULE_CREATE_YIN("DF_OCT0",
+ "<leaf name=\"port\">"
+ " <default value=\"+017\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+017");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE OCTAL */
+ schema = MODULE_CREATE_YIN("DF_OCT1",
+ "<leaf name=\"port\">"
+ " <default value=\"-017\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *)mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0);
+ CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15);
+ lysp_leaf = (void *)mod->parsed->data;
+ CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-017");
+ CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0);
+
+ /* TEST DEFAULT VALUE OCTAL ERROR */
+ schema = MODULE_CREATE_YIN("DF_OCT2",
+ "<leaf name=\"port\">"
+ " <default value=\"-0201\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0201\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_OCT2:port\".");
+
+ /* TEST DEFAULT VALUE OCTAL ERROR */
+ schema = MODULE_CREATE_YIN("DF_OCT3",
+ "<leaf name=\"port\">"
+ " <default value=\"0200\" />"
+ " <type name=\"int8\" />"
+ "</leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0200\" is out of type int8 min/max bounds.).",
+ "Schema location \"/DF_OCT3:port\".");
+}
+
+static void
+test_schema_print(void **state)
+{
+ const char *schema_yang, *schema_yin;
+ char *printed;
+ struct lys_module *mod;
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT0",
+ " description \"desc\";\n"
+ "leaf port {type int8 {range \"0 .. 50 | 127\";} default \"20\";}");
+ schema_yin = MODULE_CREATE_YIN("PRINT0",
+ "\n"
+ " <description>\n"
+ " <text>desc</text>\n"
+ " </description>\n"
+ " <leaf name=\"port\">\n"
+ " <type name=\"int8\">\n"
+ " <range value=\"0 .. 50 | 127\"/>\n"
+ " </type>\n"
+ " <default value=\"20\"/>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT1",
+ "\n"
+ " description\n"
+ " \"desc\";\n\n"
+ " leaf port {\n"
+ " type int8 {\n"
+ " range \"0 .. 50 | 127\";\n"
+ " }\n"
+ " default \"20\";\n"
+ " }\n");
+ schema_yin = MODULE_CREATE_YIN("PRINT1",
+ "<description>"
+ " <text>desc</text>"
+ "</description>"
+ "<leaf name=\"port\">"
+ " <type name=\"int8\">"
+ " <range value=\"0 .. 50 | 127\"/>"
+ " </type>"
+ "<default value=\"20\"/>"
+ "</leaf>");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT2",
+ " description \"desc\";\n"
+ "leaf port {type int8;}");
+ schema_yin = MODULE_CREATE_YIN("PRINT2",
+ "\n"
+ " <description>\n"
+ " <text>desc</text>\n"
+ " </description>\n"
+ " <leaf name=\"port\">\n"
+ " <type name=\"int8\"/>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT3",
+ "\n"
+ " leaf port {\n"
+ " type int8;\n"
+ " }\n");
+ schema_yin = MODULE_CREATE_YIN("PRINT3",
+ "<leaf name=\"port\">"
+ " <type name=\"int8\"/>"
+ "</leaf>");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+}
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+ struct lyd_node *tree;
+ const char *data;
+ /* variable for default value test */
+ struct lysc_node_container *lysc_root;
+ struct lyd_node_inner *lyd_root;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 105\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "+50", INT8, "50", 50);
+ TEST_SUCCESS_XML("defs", "50", INT8, "50", 50);
+ TEST_SUCCESS_XML("defs", "105", INT8, "105", 105);
+ TEST_SUCCESS_XML("defs", "0", INT8, "0", 0);
+ TEST_SUCCESS_XML("defs", "-0", INT8, "0", 0);
+ TEST_ERROR_XML("defs", "-1");
+ CHECK_LOG_CTX("Unsatisfied range - value \"-1\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_XML("defs", "51");
+ CHECK_LOG_CTX("Unsatisfied range - value \"51\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_XML("defs", "106");
+ CHECK_LOG_CTX("Unsatisfied range - value \"106\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_XML("defs", "104");
+ CHECK_LOG_CTX("Unsatisfied range - value \"104\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_XML("defs", "60");
+ CHECK_LOG_CTX("Unsatisfied range - value \"60\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type int8; }");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_XML("T0", "-128", INT8, "-128", -128);
+ TEST_SUCCESS_XML("T0", "-100", INT8, "-100", -100);
+ TEST_SUCCESS_XML("T0", "0", INT8, "0", 0);
+ TEST_SUCCESS_XML("T0", "10", INT8, "10", 10);
+ TEST_SUCCESS_XML("T0", "50", INT8, "50", 50);
+ TEST_SUCCESS_XML("T0", "127", INT8, "127", 127);
+ /* leading zeros */
+ TEST_SUCCESS_XML("T0", "-015", INT8, "-15", -15);
+ TEST_SUCCESS_XML("T0", "015", INT8, "15", 15);
+ TEST_ERROR_XML("T0", "-129");
+ CHECK_LOG_CTX("Value \"-129\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_XML("T0", "128");
+ CHECK_LOG_CTX("Value \"128\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_XML("T0", "256");
+ CHECK_LOG_CTX("Value \"256\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_XML("T0", "1024");
+ CHECK_LOG_CTX("Value \"1024\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+
+ /*
+ * default value
+ */
+ schema = MODULE_CREATE_YANG("T1",
+ "container cont {\n"
+ " leaf port {type int8 {range \"0 .. 50 | 105\";} default \"20\";}"
+ "}");
+ /* check using default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "<cont xmlns=\"urn:tests:" "T1" "\">" "</cont>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *)tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 1, 0, 0, 1, 1,
+ INT8, "20", 20);\
+ lyd_free_all(tree);
+
+ /* check rewriting default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "<cont xmlns=\"urn:tests:T1\">" "<port> 30 </port>" "</cont>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *)tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 0, 0, 0, 1, 1,
+ INT8, "30", 30);
+ lyd_free_all(tree);
+
+ /*
+ * specific error
+ */
+ schema = MODULE_CREATE_YANG("T2", "leaf port {type int8 {range \"0 .. 50 | 105\" {"
+ " error-app-tag \"range-violation\";"
+ " error-message \"invalid range of value\";"
+ "}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("T2", "120");
+ CHECK_LOG_CTX_APPTAG("invalid range of value", "Schema location \"/T2:port\", line number 1.", "range-violation");
+}
+
+static void
+test_data_json(void **state)
+{
+ const char *schema;
+ /* value for default test */
+ struct lysc_node_container *lysc_root;
+ struct lyd_node_inner *lyd_root;
+ const char *data;
+ struct lyd_node *tree;
+
+ /* parsing json data */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 105\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_JSON("defs", "50", INT8, "50", 50);
+ TEST_SUCCESS_JSON("defs", "50", INT8, "50", 50);
+ TEST_SUCCESS_JSON("defs", "105", INT8, "105", 105);
+ TEST_SUCCESS_JSON("defs", "0", INT8, "0", 0);
+ TEST_SUCCESS_JSON("defs", "-0", INT8, "0", 0);
+ TEST_ERROR_JSON("defs", "-1");
+ CHECK_LOG_CTX("Unsatisfied range - value \"-1\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_JSON("defs", "51");
+ CHECK_LOG_CTX("Unsatisfied range - value \"51\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_JSON("defs", "106");
+ CHECK_LOG_CTX("Unsatisfied range - value \"106\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_JSON("defs", "104");
+ CHECK_LOG_CTX("Unsatisfied range - value \"104\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+ TEST_ERROR_JSON("defs", "60");
+ CHECK_LOG_CTX("Unsatisfied range - value \"60\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type int8; }");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_JSON("T0", "-128", INT8, "-128", -128);
+ TEST_SUCCESS_JSON("T0", "-100", INT8, "-100", -100);
+ TEST_SUCCESS_JSON("T0", "0", INT8, "0", 0);
+ TEST_SUCCESS_JSON("T0", "10", INT8, "10", 10);
+ TEST_SUCCESS_JSON("T0", "50", INT8, "50", 50);
+ TEST_SUCCESS_JSON("T0", "127", INT8, "127", 127);
+ /* leading zeros */
+ TEST_ERROR_JSON("T0", "015");
+ TEST_ERROR_JSON("T0", "-015");
+ TEST_ERROR_JSON("defs", "+50");
+ TEST_ERROR_JSON("T0", "-129");
+ CHECK_LOG_CTX("Value \"-129\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_JSON("T0", "128");
+ CHECK_LOG_CTX("Value \"128\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_JSON("T0", "256");
+ CHECK_LOG_CTX("Value \"256\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+ TEST_ERROR_JSON("T0", "1024");
+ CHECK_LOG_CTX("Value \"1024\" is out of type int8 min/max bounds.",
+ "Schema location \"/T0:port\", line number 1.");
+
+ /*
+ * default value
+ */
+ schema = MODULE_CREATE_YANG("T1",
+ "container cont {\n"
+ " leaf port {type int8 {range \"0 .. 50 | 105\";} default \"20\";}"
+ "}");
+ /* check using default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "{\"T1:cont\":{}}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *)tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 1, 0, 0, 1, 1,
+ INT8, "20", 20);\
+ lyd_free_all(tree);
+
+ /* check rewriting default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "{\"T1:cont\":{\":port\":30}}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *)tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 0, 0, 0, 1, 1,
+ INT8, "30", 30);
+ lyd_free_all(tree);
+
+}
+
+static void
+test_data_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb", "leaf port {type int8;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "port", "-128");
+ TEST_SUCCESS_LYB("lyb", "port", "0");
+ TEST_SUCCESS_LYB("lyb", "port", "1");
+ TEST_SUCCESS_LYB("lyb", "port", "127");
+}
+
+static void
+test_diff(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 120\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ struct lyd_node *model_1, *model_2;
+ struct lyd_node *diff;
+ const char *expected_string;
+ const char *data_1 = "<port xmlns=\"urn:tests:defs\"> 5 </port>";
+ const char *data_2 = "<port xmlns=\"urn:tests:defs\"> 6 </port>";
+ const char *diff_expected = "<port xmlns=\"urn:tests:defs\" "
+ "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" "
+ "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"5\">"
+ "6</port>";
+
+ LYD_TREE_CREATE(data_1, model_1);
+ LYD_TREE_CREATE(data_2, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+
+ /* create data from diff */
+ diff_expected = "<port xmlns=\"urn:tests:defs\" "
+ "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" "
+ "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"5\">"
+ "120</port>";
+ LYD_TREE_CREATE(diff_expected, diff);
+ data_1 = "<port xmlns=\"urn:tests:defs\"> 5 </port>";
+ LYD_TREE_CREATE(data_1, model_1);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ expected_string = "<port xmlns=\"urn:tests:defs\">120</port>";
+
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ lyd_free_all(model_1);
+ lyd_free_all(diff);
+
+ /*
+ * check creating data out of range
+ */
+ diff_expected = "<port xmlns=\"urn:tests:defs\" "
+ "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" "
+ "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"5\">"
+ "121</port>";
+ CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, model_1);
+ CHECK_LOG_CTX("Unsatisfied range - value \"121\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ /*
+ * diff from default value
+ */
+ data_1 = "<cont xmlns=\"urn:tests:T0\"></cont>";
+ data_2 = "<cont xmlns=\"urn:tests:T0\"> <port> 6 </port> </cont>";
+ diff_expected = "<cont xmlns=\"urn:tests:T0\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"create\"><port>6</port></cont>";
+
+ schema = MODULE_CREATE_YANG("T0",
+ "container cont {\n"
+ " leaf port {type int8; default \"20\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ LYD_TREE_CREATE(data_1, model_1);
+ LYD_TREE_CREATE(data_2, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(diff);
+
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+}
+
+static void
+test_print(void **state)
+{
+ const char *schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50\";}}");
+ const char *expected_string;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ struct lyd_node *model_1;
+ const char *data_1 = "<port xmlns=\"urn:tests:defs\"> 50 </port>";
+
+ LYD_TREE_CREATE(data_1, model_1);
+
+ /* XML */
+ expected_string = "<port xmlns=\"urn:tests:defs\">50</port>";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+
+ /* JSON */
+ expected_string = "{\"defs:port\":50}";
+ CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+
+ lyd_free_all(model_1);
+}
+
+static void
+test_plugin_store(void **state)
+{
+ const char *val_text = NULL;
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value value = {0};
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_INT8]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ char *alloc;
+ const char *schema;
+ struct lysc_type lysc_type_test;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"-50 .. 50\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* check proper type */
+ assert_string_equal("libyang 2 - integers, version 1", type->id);
+
+ /* check store
+ * options = LY_TYPE_STORE_IMPLEMENT | LY_TYPE_STORE_DYNAMIC
+ * hint = LYD_VALHINT_DECNUM, LYD_VALHINT_HEXNUM, LYD_VALHINT_OCTNUM
+ */
+ val_text = "20";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, INT8, "20", 20);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "-20";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, INT8, "-20", -20);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "0xf";
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, INT8, "15", 15);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "1B";
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, INT8, "27", 27);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "-0xf";
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, INT8, "-15", -15);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "027";
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_OCTNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, INT8, "23", 23);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "-027";
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_OCTNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, INT8, "-23", -23);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /*
+ * minor tests
+ * dynamic alocated input text
+ */
+ val_text = "0xa";
+ alloc = (char *)malloc(strlen(val_text) + 1);
+
+ memcpy(alloc, val_text, strlen(val_text) + 1);
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, alloc, strlen(val_text),
+ LYPLG_TYPE_STORE_DYNAMIC, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ alloc = NULL;
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, INT8, "10", 10);
+ type->free(UTEST_LYCTX, &value);
+
+ /* wrong lysc_type of value */
+ lysc_type_test = *lysc_type;
+ lysc_type_test.basetype = LY_TYPE_UINT8;
+ val_text = "20";
+ ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EINT, ly_ret);
+
+ /*
+ * ERROR TESTS
+ */
+ val_text = "";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+ val_text = "";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, 1,
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+ val_text = "10 b";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+ val_text = "a";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+}
+
+static void
+test_plugin_compare(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_INT8]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+ /* different type */
+ const char *diff_type_text = "20";
+ struct lyd_value diff_type_val;
+ struct lysc_type *diff_type;
+ /* Value which are going to be created to tested */
+ const char *val_init[] = {"20", "30", "-30", "0", "-0", "20"};
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0", "typedef my_int_type {type int8; }"
+ "leaf p1 {type my_int_type;}"
+ "leaf p2 {type my_int_type;}"
+ "leaf p3 {type my_int_type{range \"0 .. 50\";}}"
+ "leaf p4 {type uint8;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /*
+ * BASIC TEST;
+ */
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[0]), &(values[0])));
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[0]), &(values[5])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[0]), &(values[1])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[1]), &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[1]), &(values[2])));
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[3]), &(values[4])));
+
+ /*
+ * SAME TYPE but different node
+ */
+ diff_type_text = "20";
+ diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_SUCCESS, type->compare(&diff_type_val, &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /*
+ * derivated type add some limitations
+ */
+ diff_type_text = "20";
+ diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /*
+ * different type (UINT8)
+ */
+ diff_type_text = "20";
+ diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /* delete values */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+static void
+test_plugin_print(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_INT8]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+ /* Value which are going to be created to tested */
+ const char *val_init[] = {"20", "0x4A", "-f", "0", "-0", "-20"};
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type int8;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* print value */
+ ly_bool dynamic = 0;
+
+ assert_string_equal("32", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("74", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("-15", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("0", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("0", type->print(UTEST_LYCTX, &(values[4]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("-32", type->print(UTEST_LYCTX, &(values[5]), LY_VALUE_XML, NULL, &dynamic, NULL));
+
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+static void
+test_plugin_dup(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_INT8]);
+ struct lysc_type *lysc_type[2];
+ const char *schema;
+ LY_ERR ly_ret;
+ /* Value which are going to be tested */
+ const char *val_init[] = {"20", "0x4A", "-f", "0", "-0x80", "-20"};
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type int8;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type[0] = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ schema = MODULE_CREATE_YANG("T1",
+ "typedef my_int_type {"
+ " type int8 {range \"-100 .. 100\";} default 20;"
+ "}"
+ "leaf port {type my_int_type; }");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type[1] = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type[it % 2], val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* print duplicate value */
+ struct lyd_value dup_value;
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[0]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, INT8, "32", 0x20);
+ assert_ptr_equal(dup_value.realtype, values[0].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[1]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, INT8, "74", 0x4a);
+ assert_ptr_equal(dup_value.realtype, values[1].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[2]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, INT8, "-15", -0xf);
+ assert_ptr_equal(dup_value.realtype, values[2].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[3]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, INT8, "0", 0x0);
+ assert_ptr_equal(dup_value.realtype, values[3].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[4]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, INT8, "-128", -0x80);
+ assert_ptr_equal(dup_value.realtype, values[4].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[5]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, INT8, "-32", -0x20);
+ assert_ptr_equal(dup_value.realtype, values[5].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ /* error tests */
+ assert_int_equal(LY_EINVAL, type->duplicate(NULL, &(values[0]), &dup_value));
+
+ for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema_yang),
+ UTEST(test_schema_yin),
+ UTEST(test_schema_print),
+ UTEST(test_data_xml),
+ UTEST(test_data_json),
+ UTEST(test_data_lyb),
+ UTEST(test_diff),
+ UTEST(test_print),
+
+ UTEST(test_plugin_store),
+ UTEST(test_plugin_compare),
+ UTEST(test_plugin_print),
+ UTEST(test_plugin_dup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/leafref.c b/tests/utests/types/leafref.c
new file mode 100644
index 0000000..c8d0cb6
--- /dev/null
+++ b/tests/utests/types/leafref.c
@@ -0,0 +1,222 @@
+/**
+ * @file leafref.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 1, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, RET) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, RET, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME1, DATA1, NODE_NAME2, DATA2) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME1 " xmlns=\"urn:tests:" MOD_NAME "\"><name>" DATA1 "</name></" NODE_NAME1 ">" \
+ "<" NODE_NAME2 " xmlns=\"urn:tests:" MOD_NAME "\">" DATA2 "</" NODE_NAME2 ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema, *schema2, *schema3, *data;
+ struct lyd_node *tree;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}"
+ "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}"
+ "leaf str-norestr {type string;}"
+ "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}"
+ "container cont {leaf leaftarget {type empty;}"
+ " list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}}"
+ " leaf-list leaflisttarget {type uint8; max-elements 5;}}"
+ "leaf-list leaflisttarget {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ schema2 = MODULE_CREATE_YANG("leafrefs", "import defs {prefix t;}"
+ "container c { container x {leaf x {type string;}} list l {"
+ " key \"id value\"; leaf id {type string;} leaf value {type string;}"
+ " leaf lr1 {type leafref {path \"../../../t:str-norestr\"; require-instance true;}}"
+ " leaf lr2 {type leafref {path \"../../l[id=current()/../../../t:str-norestr]\" +"
+ " \"[value=current()/../../../t:str-norestr]/value\"; require-instance true;}}"
+ " leaf lr3 {type leafref {path \"/t:list[t:id=current ( )/../../x/x]/t:targets\";}}"
+ "}}");
+ UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML2("<leaflisttarget xmlns=\"urn:tests:defs\">x</leaflisttarget>"
+ "<leaflisttarget xmlns=\"urn:tests:defs\">y</leaflisttarget>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "a:lref", "y", STRING, "y");
+
+ TEST_SUCCESS_XML2("<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>"
+ "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>x</targets><targets>y</targets></list>"
+ "<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>",
+ "defs", "xmlns:a=\"urn:tests:defs\"", "a:lref2", "y", STRING, "y");
+
+ data = "<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>"
+ "<c xmlns=\"urn:tests:leafrefs\"><l><id>x</id><value>x</value><lr1>y</lr1></l></c>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_child(lyd_child(tree->next->next)->next)->next->next,
+ 0, 0, 0, 1, 1, STRING, "y");
+ lyd_free_all(tree);
+
+ data = "<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>"
+ "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>c</targets><targets>d</targets></list>"
+ "<c xmlns=\"urn:tests:leafrefs\"><x><x>y</x></x>"
+ "<l><id>x</id><value>x</value><lr3>c</lr3></l></c>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_child(lyd_child(tree->next->next->next)->next)->next->next,
+ 0, 0, 0, 1, 1, STRING, "c");
+ lyd_free_all(tree);
+
+ schema3 = MODULE_CREATE_YANG("simple", "leaf l1 {type leafref {path \"../target\";}}"
+ "leaf target {type string;}");
+ UTEST_ADD_MODULE(schema3, LYS_IN_YANG, NULL, NULL);
+
+ data = "<l1 xmlns=\"urn:tests:simple\">&quot;*&quot;&#39;</l1>"
+ "<target xmlns=\"urn:tests:simple\">&quot;*&quot;&#39;</target>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_all(tree);
+
+ data = "<l1 xmlns=\"urn:tests:simple\">&quot;*&#39;&quot;</l1>"
+ "<target xmlns=\"urn:tests:simple\">&quot;*&#39;&quot;</target>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_all(tree);
+
+ /* invalid value */
+ TEST_ERROR_XML2("<leaflisttarget xmlns=\"urn:tests:defs\">x</leaflisttarget>",
+ "defs", "", "lref", "y", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"y\" - no target instance \"/leaflisttarget\" with the same value.",
+ "Data location \"/defs:lref\".", "instance-required");
+
+ TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>"
+ "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>x</targets><targets>y</targets></list>"
+ "<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>",
+ "defs", "", "lref2", "b", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"b\" - "
+ "no target instance \"../list[id = current()/../str-norestr]/targets\" with the same value.",
+ "Data location \"/defs:lref2\".", "instance-required");
+
+ TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>"
+ "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>x</targets><targets>y</targets></list>",
+ "defs", "", "lref2", "b", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"b\" - "
+ "no target instance \"../list[id = current()/../str-norestr]/targets\" with the same value.",
+ "Data location \"/defs:lref2\".", "instance-required");
+
+ TEST_ERROR_XML2("<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>",
+ "defs", "", "lref2", "b", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"b\" - "
+ "no target instance \"../list[id = current()/../str-norestr]/targets\" with the same value.",
+ "Data location \"/defs:lref2\".", "instance-required");
+
+ TEST_ERROR_XML2("<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>",
+ "leafrefs", "", "c", "<l><id>x</id><value>x</value><lr1>a</lr1></l>", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"a\" - no target instance \"../../../t:str-norestr\" with the same value.",
+ "Data location \"/leafrefs:c/l[id='x'][value='x']/lr1\".", "instance-required");
+
+ TEST_ERROR_XML2("<str-norestr xmlns=\"urn:tests:defs\">z</str-norestr>",
+ "leafrefs", "", "c", "<l><id>y</id><value>y</value></l><l><id>x</id><value>x</value><lr2>z</lr2></l>", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"z\" - no target instance \"../../l[id=current()/../../../t:str-norestr]"
+ "[value=current()/../../../t:str-norestr]/value\" with the same value.",
+ "Data location \"/leafrefs:c/l[id='x'][value='x']/lr2\".", "instance-required");
+
+ TEST_ERROR_XML2("",
+ "defs", "", "lref", "%n", LY_EVALID);
+ CHECK_LOG_CTX_APPTAG("Invalid leafref value \"%n\" - no target instance \"/leaflisttarget\" with the same value.",
+ "Data location \"/defs:lref\".", "instance-required");
+}
+
+static void
+test_data_json(void **state)
+{
+ const char *schema, *data;
+ struct lyd_node *tree;
+
+ /* json test */
+ schema = MODULE_CREATE_YANG("simple", "leaf l1 {type leafref {path \"../target\";}}"
+ "leaf target {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ data = "{"
+ " \"simple:l1\":\"\\\"*\\\"'\","
+ " \"simple:target\":\"\\\"*\\\"'\""
+ "}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_all(tree);
+
+ data = "{"
+ " \"simple:l1\":\"\\\"*'\\\"\","
+ " \"simple:target\":\"\\\"*'\\\"\""
+ "}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_all(tree);
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "list lst {key \"name\"; leaf name {type string;}}"
+ "leaf lref {type leafref {path \"../lst/name\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "lst", "key_str", "lref", "key_str");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_data_json),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/string.c b/tests/utests/types/string.c
new file mode 100644
index 0000000..d232e9d
--- /dev/null
+++ b/tests/utests/types/string.c
@@ -0,0 +1,1410 @@
+/**
+ * @file string.c
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @brief test for string values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...)\
+ {\
+ struct lyd_node *tree;\
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>";\
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);\
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0);\
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *) tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__);\
+ lyd_free_all(tree);\
+ }
+
+#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...)\
+ {\
+ struct lyd_node *tree;\
+ const char *data = "{\"" MOD_NAME ":port\":\"" DATA "\"}";\
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);\
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0);\
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *) tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__);\
+ lyd_free_all(tree);\
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA)\
+ {\
+ struct lyd_node *tree;\
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>";\
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);\
+ assert_null(tree);\
+ }
+
+#define TEST_ERROR_JSON(MOD_NAME, DATA)\
+ {\
+ struct lyd_node *tree;\
+ const char *data = "{\"" MOD_NAME ":port\":\"" DATA "\"}";\
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);\
+ assert_null(tree);\
+ }
+
+static void
+test_schema_yang(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_pattern *pattern;
+ struct lysc_range *range;
+
+ /* TEST BASE STRING */
+ schema = MODULE_CREATE_YANG("base", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_STRING, 0, 0);
+ 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, 0x0, 0, 0, "string", 0, 0, 1, 0, 0, 0);
+
+ /* TEST MODULE T0 */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type string"
+ "{length \"10 .. max\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_u64, 10);
+ assert_true(range->parts[0].max_u64 == 18446744073709551615ull);
+ 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, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T1 */
+ schema = MODULE_CREATE_YANG("T1", "leaf port {type string"
+ "{length \"min .. 20 | 50\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 0);
+ assert_int_equal(range->parts[0].max_u64, 20);
+ assert_int_equal(range->parts[1].min_u64, 50);
+ assert_int_equal(range->parts[1].max_u64, 50);
+ 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, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 20 | 50", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T2 */
+ schema = MODULE_CREATE_YANG("T2", "leaf port {type string"
+ "{length \"10 .. 20 | 50 .. 100 | 255\";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL);
+ assert_int_equal(range->parts[0].min_u64, 10);
+ assert_int_equal(range->parts[0].max_u64, 20);
+ assert_int_equal(range->parts[1].min_u64, 50);
+ assert_int_equal(range->parts[1].max_u64, 100);
+ assert_int_equal(range->parts[2].min_u64, 255);
+ assert_int_equal(range->parts[2].max_u64, 255);
+ 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, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. 20 | 50 .. 100 | 255", NULL, NULL, NULL, 0, NULL);
+
+ /* SUBTYPE MODULE T2 */
+ schema = MODULE_CREATE_YANG("TS0",
+ "typedef my_type {"
+ " type string {length \"10 .. 20 | 50 .. 100 | 255\";}"
+ "}"
+ "leaf port {type my_type {length \"min .. 15 | max\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 10);
+ assert_int_equal(range->parts[0].max_u64, 15);
+ assert_int_equal(range->parts[1].min_u64, 255);
+ assert_int_equal(range->parts[1].max_u64, 255);
+ 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, 0x10, 0, 1, "my_type", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 15 | max", NULL, NULL, NULL, 0, NULL);
+
+ /* ERROR TESTS NEGATIVE VALUE */
+ schema = MODULE_CREATE_YANG("ERR0", "leaf port {type string {length \"-1 .. 20\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR0:port");
+
+ schema = MODULE_CREATE_YANG("ERR1", "leaf port {type string {length \"100 .. 18446744073709551616\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/ERR1:port");
+
+ schema = MODULE_CREATE_YANG("ERR2", "leaf port {type string {length \"10 .. 20 | 20 .. 30\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EEXIST);
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "/ERR2:port");
+
+ schema = MODULE_CREATE_YANG("ERR3",
+ "typedef my_type {"
+ " type string;"
+ "}"
+ "leaf port {type my_type {length \"-1 .. 15\";}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR3:port");
+
+ /*
+ * PATTERN
+ */
+ schema = MODULE_CREATE_YANG("TPATTERN_0", "leaf port {type string"
+ "{pattern '[a-zA-Z_][a-zA-Z0-9\\-_.]*';}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, 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, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL);
+
+ schema = MODULE_CREATE_YANG("TPATTERN_1", "leaf port {type string{"
+ " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.]*' ;"
+ " pattern 'abc.*' {modifier invert-match;}"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, 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, 0x40, 0, 0, "string", 0, 2, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[1]), "\x15" "abc.*", NULL, NULL, NULL, 0, NULL);
+
+ schema = MODULE_CREATE_YANG("TPATTERN_2",
+ "typedef my_type {"
+ " type string{"
+ " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.]*' ;"
+ " pattern 'abc.*' {modifier invert-match;}"
+ "}}"
+ "leaf port {type my_type {pattern 'bcd.*';}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 3);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[2];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "bcd.*", 0, 0x0, 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "bcd.*", NULL, NULL, NULL, 0, NULL);
+
+ /*
+ * TEST pattern error
+ */
+ schema = MODULE_CREATE_YANG("TPATTERN_ERR_0", "leaf port {type string {"
+ "pattern '[a-zA-Z_[a-zA-Z0-9\\-_.*';"
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Regular expression \"[a-zA-Z_[a-zA-Z0-9\\-_.*\" is not valid (\"\": missing terminating ] for character class).",
+ "/TPATTERN_ERR_0:port");
+
+ schema = MODULE_CREATE_YANG("TDEFAULT_0",
+ "typedef my_type {"
+ " type string{"
+ " pattern \"[a-zA-Z_][a-zA-Z0-9\\\\-_.]*\";"
+ " length \"2 .. 5 | 10\";"
+ " }"
+ " default \"a1i-j\";"
+ "}"
+ "leaf port {type my_type;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, "a1i-j");
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 2);
+ assert_int_equal(range->parts[0].max_u64, 5);
+ assert_int_equal(range->parts[1].min_u64, 10);
+ assert_int_equal(range->parts[1].max_u64, 10);
+ 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ /* TEST pattern backslash
+ * The '[' character is escaped, thus character group is broken.
+ */
+
+ schema = MODULE_CREATE_YANG("TPATTERN_BC_ERR_1", "leaf port {type string {"
+ "pattern '\\[a]b';" /* pattern '\[a]b'; */
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Regular expression \"\\[a]b\" is not valid (\"]b\": character group doesn't begin with '[').",
+ "/TPATTERN_BC_ERR_1:port");
+
+ schema = MODULE_CREATE_YANG("TPATTERN_BC_ERR_2", "leaf port {type string {"
+ "pattern \"\\\\[a]b\";" /* pattern "\\[a]b"; */
+ "}}");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Regular expression \"\\[a]b\" is not valid (\"]b\": character group doesn't begin with '[').",
+ "/TPATTERN_BC_ERR_2:port");
+
+ /* PATTERN AND LENGTH */
+ schema = MODULE_CREATE_YANG("TPL_0",
+ "typedef my_type {"
+ " type string{"
+ " length \"2 .. 10\";"
+ " }"
+ "}"
+ "leaf port {type my_type{ pattern \"[a-zA-Z_][a-zA-Z0-9\\\\-_.]*\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_u64, 2);
+ assert_int_equal(range->parts[0].max_u64, 10);
+ 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*",
+ NULL, NULL, NULL, 0, NULL);
+}
+
+static void
+test_schema_yin(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lysc_node_leaf *lysc_leaf;
+ struct lysp_node_leaf *lysp_leaf;
+ struct lysc_pattern *pattern;
+ struct lysc_range *range;
+
+ /* TEST BASE STRING */
+ schema = MODULE_CREATE_YIN("base", "<leaf name=\"port\"> <type name=\"string\"/> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_STRING, 0, 0);
+ 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, 0x0, 0, 0, "string", 0, 0, 1, 0, 0, 0);
+
+ /* TEST MODULE T0 */
+ schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\"> <type name=\"string\">"
+ "<length value=\"10 .. max\"/>"
+ "</type> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL);
+ assert_int_equal(range->parts[0].min_u64, 10);
+ assert_true(range->parts[0].max_u64 == 18446744073709551615ull);
+ 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, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. max", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T1 */
+ schema = MODULE_CREATE_YIN("T1", "<leaf name=\"port\"> <type name=\"string\">"
+ " <length value=\"min .. 20 | 50\"/>"
+ "</type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 0);
+ assert_int_equal(range->parts[0].max_u64, 20);
+ assert_int_equal(range->parts[1].min_u64, 50);
+ assert_int_equal(range->parts[1].max_u64, 50);
+ 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, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 20 | 50", NULL, NULL, NULL, 0, NULL);
+
+ /* TEST MODULE T2 */
+ schema = MODULE_CREATE_YIN("T2", "<leaf name=\"port\"> <type name=\"string\">"
+ "<length value=\"10 .. 20 | 50 .. 100 | 255\"/>"
+ "</type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL);
+ assert_int_equal(range->parts[0].min_u64, 10);
+ assert_int_equal(range->parts[0].max_u64, 20);
+ assert_int_equal(range->parts[1].min_u64, 50);
+ assert_int_equal(range->parts[1].max_u64, 100);
+ assert_int_equal(range->parts[2].min_u64, 255);
+ assert_int_equal(range->parts[2].max_u64, 255);
+ 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, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. 20 | 50 .. 100 | 255", NULL, NULL, NULL, 0, NULL);
+
+ /* SUBTYPE MODULE T2 */
+ schema = MODULE_CREATE_YIN("TS0",
+ "<typedef name=\"my_type\">"
+ " <type name=\"string\"> <length value=\"10 .. 20 | 50 .. 100 | 255\"/> </type>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\">"
+ " <length value=\"min .. 15 | max\"/>"
+ "</type> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 10);
+ assert_int_equal(range->parts[0].max_u64, 15);
+ assert_int_equal(range->parts[1].min_u64, 255);
+ assert_int_equal(range->parts[1].max_u64, 255);
+ 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, 0x10, 0, 1, "my_type", 0, 0, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 15 | max", NULL, NULL, NULL, 0, NULL);
+
+ /* ERROR TESTS NEGATIVE VALUE */
+ schema = MODULE_CREATE_YIN("ERR0", "<leaf name=\"port\"> <type name=\"string\">"
+ "<length value =\"-1 .. 20\"/> </type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR0:port");
+
+ schema = MODULE_CREATE_YIN("ERR1", "<leaf name=\"port\"> <type name=\"string\">"
+ "<length value=\"100 .. 18446744073709551616\"/>"
+ "</type> </leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/ERR1:port");
+
+ schema = MODULE_CREATE_YIN("ERR2", "<leaf name=\"port\">"
+ "<type name=\"string\"> <length value=\"10 .. 20 | 20 .. 30\"/>"
+ "</type> </leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EEXIST);
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "/ERR2:port");
+
+ schema = MODULE_CREATE_YIN("ERR3",
+ "<typedef name=\"my_type\"> <type name=\"string\"/> </typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"> <length value=\"-1 .. 15\"/>"
+ "</type> </leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR3:port");
+
+ /*
+ * PATTERN
+ */
+ schema = MODULE_CREATE_YIN("TPATTERN_0", "<leaf name=\"port\"> <type name=\"string\">"
+ "<pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>"
+ "</type> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, 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, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL);
+
+ schema = MODULE_CREATE_YIN("TPATTERN_1", "<leaf name=\"port\"> <type name=\"string\">"
+ " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>"
+ " <pattern value=\"abc.*\"> <modifier value=\"invert-match\"/> </pattern>"
+ "</type> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, 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, 0x40, 0, 0, "string", 0, 2, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[1]), "\x15" "abc.*", NULL, NULL, NULL, 0, NULL);
+
+ schema = MODULE_CREATE_YIN("TPATTERN_2",
+ "<typedef name=\"my_type\">"
+ " <type name=\"string\">"
+ " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>"
+ " <pattern value=\"abc.*\"> <modifier value=\"invert-match\"/> </pattern>"
+ "</type> </typedef>"
+ "<leaf name=\"port\"><type name=\"my_type\"> <pattern value=\"bcd.*\"/> </type></leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL);
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 3);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, NULL);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[2];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "bcd.*", 0, 0x0, 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, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0);
+ CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "bcd.*", NULL, NULL, NULL, 0, NULL);
+
+ /*
+ * TEST pattern error
+ * */
+ schema = MODULE_CREATE_YIN("TPATTERN_ERR_0",
+ "<leaf name=\"port\"> <type name=\"string\">"
+ " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.*\"/>"
+ "</type> </leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Regular expression \"[a-zA-Z_][a-zA-Z0-9\\-_.*\" is not valid (\"\": missing terminating ] for character class).",
+ "/TPATTERN_ERR_0:port");
+
+ /*
+ * DEFAUT VALUE
+ */
+ schema = MODULE_CREATE_YIN("TDEFAULT_0",
+ "<typedef name=\"my_type\">"
+ " <type name=\"string\">"
+ " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>"
+ " <length value=\"2 .. 5 | 10\"/>"
+ " </type>"
+ " <default value=\"a1i-j\"/>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, "a1i-j");
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 1);
+ pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0];
+ CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL);
+ range = ((struct lysc_type_str *)lysc_leaf->type)->length;
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 2);
+ assert_int_equal(range->parts[0].max_u64, 5);
+ assert_int_equal(range->parts[1].min_u64, 10);
+ assert_int_equal(range->parts[1].max_u64, 10);
+ 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("TDEFAULT_1",
+ "<typedef name=\"my_type\">"
+ " <type name=\"string\">"
+ " </type>"
+ " <default value=\"a1i-j&lt;\"/>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
+ UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ lysc_leaf = (void *) mod->compiled->data;
+ CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, "a1i-j<");
+ CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 0);
+ CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL);
+ assert_int_equal(range->parts[0].min_u64, 2);
+ assert_int_equal(range->parts[0].max_u64, 5);
+ assert_int_equal(range->parts[1].min_u64, 10);
+ assert_int_equal(range->parts[1].max_u64, 10);
+ 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, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0);
+
+ schema = MODULE_CREATE_YIN("TDEFAULT_2",
+ "<typedef name=\"my_type\">"
+ " <type name=\"string\">"
+ " <length value=\"2\"/>"
+ " </type>"
+ " <default value=\"a1i-j&lt;\"/>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+
+ schema = MODULE_CREATE_YIN("TDEFAULT_2",
+ "<typedef name=\"my_type\">"
+ " <type name=\"string\">"
+ " <length value=\"2\"/>"
+ " </type>"
+ " <default value=\"a1i-j&lt;\"/>"
+ "</typedef>"
+ "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+
+ schema = MODULE_CREATE_YIN("TDEFAULT_3",
+ "<typedef name=\"my_type\">"
+ " <default value=\"a1i-j&lt;\"/>"
+ " <type name=\"string\">"
+ "</type> </typedef>"
+ "<leaf name=\"port\"><type name=\"my_type\"> <pattern value=\"bcd.*\"/> </type></leaf>");
+ UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+
+}
+
+static void
+test_schema_print(void **state)
+{
+ const char *schema_yang, *schema_yin;
+ char *printed;
+ struct lys_module *mod;
+
+ /* test print yang to yin */
+ schema_yang = MODULE_CREATE_YANG("PRINT0",
+ "leaf port {type string {"
+ "length \"min .. 20 | 50\";"
+ "pattern \"p.*\\\\\\\\\";"
+ "pattern 'p4.*' {modifier invert-match;}"
+ "}default \"p\\\"<\\\\\";}");
+ schema_yin = MODULE_CREATE_YIN("PRINT0",
+ " <leaf name=\"port\">\n"
+ " <type name=\"string\">\n"
+ " <length value=\"min .. 20 | 50\"/>\n"
+ " <pattern value=\"p.*\\\\\"/>\n"
+ " <pattern value=\"p4.*\">\n"
+ " <modifier value=\"invert-match\"/>\n"
+ " </pattern>\n"
+ " </type>\n"
+ " <default value=\"p&quot;&lt;\\\"/>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, schema_yin);
+ free(printed);
+
+ /* test print yin to yang */
+ schema_yang = MODULE_CREATE_YANG("PRINT1",
+ "\n"
+ " leaf port {\n"
+ " type string {\n"
+ " length \"min .. 20 | 50\";\n"
+ " pattern \"p.*\\\\\\\\\";\n"
+ " pattern \"p4.*\" {\n"
+ " modifier invert-match;\n"
+ " }\n"
+ " }\n"
+ " default \"p\\\"<\\\\\";\n"
+ " }\n");
+ schema_yin = MODULE_CREATE_YIN("PRINT1",
+ " <leaf name=\"port\">\n"
+ " <type name=\"string\">\n"
+ " <length value=\"min .. 20 | 50\"/>\n"
+ " <pattern value=\"p.*\\\\\"/>\n"
+ " <pattern value=\"p4.*\">\n"
+ " <modifier value=\"invert-match\"/>\n"
+ " </pattern>\n"
+ " </type>\n"
+ " <default value=\"p&quot;&lt;\\\"/>\n"
+ " </leaf>\n");
+
+ UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, schema_yang);
+ free(printed);
+}
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+ struct lyd_node *tree;
+ const char *data;
+ struct lysc_node_container *lysc_root;
+ struct lyd_node_inner *lyd_root;
+
+ /* NO RESTRICTION TESTS */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ /* space on start and new line */
+ TEST_SUCCESS_XML("T0", " 50 \n\t 64", STRING, " 50 \n\t 64");
+ /* nuber as string */
+ TEST_SUCCESS_XML("T0", "50", STRING, "50");
+ TEST_SUCCESS_XML("T0", "+250", STRING, "+250");
+ /* references */
+ TEST_SUCCESS_XML("T0", "&quot;", STRING, "\"");
+ TEST_SUCCESS_XML("T0", "|&amp;|", STRING, "|&|");
+ TEST_SUCCESS_XML("T0", "&apos;", STRING, "'");
+ TEST_SUCCESS_XML("T0", "&lt;", STRING, "<");
+ TEST_SUCCESS_XML("T0", "&gt;", STRING, ">");
+ TEST_SUCCESS_XML("T0", "&#x42f;&#1071;", STRING, "ЯЯ");
+ /* special characters */
+ TEST_SUCCESS_XML("T0", "\"", STRING, "\"");
+ TEST_SUCCESS_XML("T0", "'", STRING, "'");
+ TEST_SUCCESS_XML("T0", ">", STRING, ">");
+ TEST_SUCCESS_XML("T0", "", STRING, "");
+ TEST_SUCCESS_XML("T0", "&amp;&lt;lt;", STRING, "&<lt;");
+ /* CDATA IS NOT SUPPORTED
+ * TEST_SUCCESS_XML("T2", "<![CDATA[<greeting>Hello, world! & Wecome</greeting>]]>", STRING,
+ * "<greeting>Hello, world! & Wecome</greeting>");
+ * COMMENT IN MIDDLE OF TEXT IS NOT SUPPORTED
+ * TEST_SUCCESS_XML("T2", "this isn't <!--' this is comment '-->comment",
+ * STRING, "this isn't comment");
+ */
+
+ /* error */
+ TEST_ERROR_XML("T0", "< df");
+ CHECK_LOG_CTX("Child element \"df\" inside a terminal node \"port\" found.",
+ "Data location \"/T0:port\", line number 1.");
+ TEST_ERROR_XML("T0", "&text;");
+ CHECK_LOG_CTX("Entity reference \"&text;</po\" not supported, only predefined references allowed.", "Line number 1.");
+ TEST_ERROR_XML("T0", "\"&#x8;\"");
+ CHECK_LOG_CTX("Invalid character reference \"&#x8;\"</port\" (0x00000008).", "Line number 1.");
+
+ /* TEST INVERTED PATTERN ADN LENGTH */
+ schema = MODULE_CREATE_YANG("T1", "leaf port {type string {"
+ " length \"5 .. 10 | 20\";"
+ " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.<]*' ;"
+ " pattern 'p4.*' {modifier invert-match;"
+ " error-app-tag \"pattern-violation\"; error-message \"invalid pattern of value\";}"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ /* inverted value */
+ TEST_SUCCESS_XML("T1", "abcde", STRING, "abcde");
+ TEST_SUCCESS_XML("T1", "abcde&lt;", STRING, "abcde<");
+ TEST_ERROR_XML("T1", "p4abc");
+ CHECK_LOG_CTX_APPTAG("invalid pattern of value", "Schema location \"/T1:port\", line number 1.", "pattern-violation");
+ /* size 20 */
+ TEST_SUCCESS_XML("T1", "ahojahojahojahojahoj", STRING, "ahojahojahojahojahoj");
+ TEST_SUCCESS_XML("T1", "abc-de", STRING, "abc-de");
+ /* ERROR LENGTH */
+ TEST_ERROR_XML("T1", "p4a&lt;");
+ CHECK_LOG_CTX("Unsatisfied length - string \"p4a<\" length is not allowed.",
+ "Schema location \"/T1:port\", line number 1.");
+
+ /* TEST DEFAULT VALUE */
+ schema = MODULE_CREATE_YANG("T2",
+ "container cont {\n"
+ " leaf port {type string {length \"0 .. 50 | 105\";} default \"test\";}"
+ "}");
+ /* using default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "<cont xmlns=\"urn:tests:" "T2" "\">" "</cont>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *) tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 1, 0, 0, 1, 1,
+ STRING, "test");\
+ lyd_free_all(tree);
+
+ /* rewriting default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "<cont xmlns=\"urn:tests:T2\">" "<port> 52 </port>" "</cont>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *) tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 0, 0, 0, 1, 1,
+ STRING, " 52 ");
+ lyd_free_all(tree);
+
+ /* WHIT STRING TEST */
+ schema = MODULE_CREATE_YANG("T_WHITE", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_XML("T_WHITE", " \n\t", STRING, " \n\t");
+ TEST_SUCCESS_XML("T_WHITE", " \n&lt;\t", STRING, " \n<\t");
+
+ /* UTF-8 length and pattern*/
+ schema = MODULE_CREATE_YANG("T_UTF8", "leaf port {type string {"
+ " length \"5 .. 10\";"
+ " pattern '[€]{5,7}' ;"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_XML("T_UTF8", "€€€€€", STRING, "€€€€€");
+ TEST_ERROR_XML("T_UTF8", "€€€");
+ CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.",
+ "Schema location \"/T_UTF8:port\", line number 1.");
+ TEST_ERROR_XML("T_UTF8", "€€€€€€€€");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".",
+ "Schema location \"/T_UTF8:port\", line number 1.");
+
+ /* ANCHOR TEST ^$ is implicit */
+ schema = MODULE_CREATE_YANG("T_ANCHOR", "leaf port {type string {"
+ " pattern 'a.*b' ;"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_ERROR_XML("T_ANCHOR", "abc");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"abc\" does not conform to \"a.*b\".",
+ "Schema location \"/T_ANCHOR:port\", line number 1.");
+ TEST_ERROR_XML("T_ANCHOR", "cab");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"cab\" does not conform to \"a.*b\".",
+ "Schema location \"/T_ANCHOR:port\", line number 1.");
+}
+
+static void
+test_data_json(void **state)
+{
+ const char *schema;
+ struct lyd_node *tree;
+ const char *data;
+ struct lysc_node_container *lysc_root;
+ struct lyd_node_inner *lyd_root;
+
+ /* NO RESTRICTION TESTS */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_JSON("T0", "this is text", STRING, "this is text");
+ /* space on start and new line */
+ TEST_SUCCESS_JSON("T0", " 50 \\n\\t 64", STRING, " 50 \n\t 64");
+ /* nuber as string */
+ TEST_SUCCESS_JSON("T0", "50", STRING, "50");
+ TEST_SUCCESS_JSON("T0", "+250", STRING, "+250");
+ /* references */
+ TEST_SUCCESS_JSON("T0", "\\\"", STRING, "\"");
+ TEST_SUCCESS_JSON("T0", "|&|", STRING, "|&|");
+ TEST_SUCCESS_JSON("T0", "<", STRING, "<");
+ TEST_SUCCESS_JSON("T0", ">", STRING, ">");
+ TEST_SUCCESS_JSON("T0", "\\u042F", STRING, "Я");
+ TEST_SUCCESS_JSON("T0", "\\u042FFF", STRING, "ЯFF");
+ /* special characters */
+ TEST_SUCCESS_JSON("T0", "'", STRING, "'");
+ TEST_SUCCESS_JSON("T0", ">", STRING, ">");
+ TEST_SUCCESS_JSON("T0", "", STRING, "");
+ TEST_SUCCESS_JSON("T0", "\\\" \\\\ \\r \\/ \\n \\t \\u20ac", STRING, "\" \\ \r / \n \t €");
+
+ /* ERROR invalid json string */
+ TEST_ERROR_JSON("T0", "\n");
+ CHECK_LOG_CTX("Invalid character in JSON string \"\n\" (0x0000000a).",
+ "Line number 1.");
+ /* backspace and form feed are valid JSON escape sequences, but the control characters they represents are not allowed values for YANG string type */
+ TEST_ERROR_JSON("T0", "\\b");
+ CHECK_LOG_CTX("Invalid character reference \"\\b\" (0x00000008).", "Line number 1.");
+
+ TEST_ERROR_JSON("T0", "\\f");
+ CHECK_LOG_CTX("Invalid character reference \"\\f\" (0x0000000c).", "Line number 1.");
+
+ TEST_ERROR_JSON("T0", "\"");
+ CHECK_LOG_CTX("Unexpected character \"\"\" after JSON string.", "Line number 1.");
+
+ TEST_ERROR_JSON("T0", "aabb \\x");
+ CHECK_LOG_CTX("Invalid character escape sequence \\x.", "Line number 1.");
+
+ /* TEST INVERTED PATTERN ADN LENGTH */
+ schema = MODULE_CREATE_YANG("T1", "leaf port {type string {"
+ " length \"5 .. 10 | 20\";"
+ " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.<]*\\n[a-zA-Z0-9\\-_.<]*' ;"
+ " pattern 'p4.*\\n' {modifier invert-match;}"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ /* inverted value */
+ TEST_SUCCESS_JSON("T1", "a\\nbcde", STRING, "a\nbcde");
+ TEST_ERROR_JSON("T1", "p4abc\\n");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"p4abc\n\" does not conform to inverted \"p4.*\\n\".",
+ "Schema location \"/T1:port\", line number 1.");
+ /* size 20 */
+ TEST_SUCCESS_JSON("T1", "ahojahojaho\\njahojaho", STRING, "ahojahojaho\njahojaho");
+ TEST_SUCCESS_JSON("T1", "abc\\n-de", STRING, "abc\n-de");
+ /* ERROR LENGTH */
+ TEST_ERROR_JSON("T1", "p4a\u042F");
+ CHECK_LOG_CTX("Unsatisfied length - string \"p4aЯ\" length is not allowed.",
+ "Schema location \"/T1:port\", line number 1.");
+
+ /* TEST DEFAULT VALUE */
+ schema = MODULE_CREATE_YANG("T_DEFAULT2",
+ "container cont {\n"
+ " leaf port {type string {length \"0 .. 50 | 105\";} default \"test\";}"
+ "}");
+ /* using default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "{\"T_DEFAULT2:cont\":{}}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *) tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 1, 0, 0, 1, 1,
+ STRING, "test");\
+ lyd_free_all(tree);
+
+ /* rewriting default value */
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ data = "{\"T_DEFAULT2:cont\":{\":port\": \" 52 \"}}";\
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lysc_root = (void *)tree->schema;
+ CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0);
+ lyd_root = ((struct lyd_node_inner *) tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 0, 0, 0, 1, 1,
+ STRING, " 52 ");
+ lyd_free_all(tree);
+
+ /* UTF-8 length and pattern*/
+ schema = MODULE_CREATE_YANG("T_UTF8", "leaf port {type string {"
+ " length \"5 .. 10\";"
+ " pattern '[€]{5,7}' ;"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_JSON("T_UTF8", "€€€€€", STRING, "€€€€€");
+ TEST_ERROR_JSON("T_UTF8", "€€€");
+ CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.",
+ "Schema location \"/T_UTF8:port\", line number 1.");
+ TEST_ERROR_JSON("T_UTF8", "€€€€€€€€");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".",
+ "Schema location \"/T_UTF8:port\", line number 1.");
+
+ /* ANCHOR TEST ^$ is implicit */
+ schema = MODULE_CREATE_YANG("T_ANCHOR", "leaf port {type string {"
+ " pattern 'a.*b' ;"
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_ERROR_JSON("T_ANCHOR", "abc");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"abc\" does not conform to \"a.*b\".",
+ "Schema location \"/T_ANCHOR:port\", line number 1.");
+ TEST_ERROR_JSON("T_ANCHOR", "cb");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"cb\" does not conform to \"a.*b\".",
+ "Schema location \"/T_ANCHOR:port\", line number 1.");
+}
+
+static void
+test_data_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "port", "");
+ TEST_SUCCESS_LYB("lyb", "port", "a");
+ TEST_SUCCESS_LYB("lyb", "port", "abcdefghijklmnopqrstuvwxyz");
+}
+
+static void
+test_diff(void **state)
+{
+ (void) state;
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("T_DIFF", "leaf port {type string {length \"6 .. 50 | 120\";}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ struct lyd_node *model_1, *model_2;
+ struct lyd_node *diff;
+ const char *data_1 = "<port xmlns=\"urn:tests:T_DIFF\"> text abc &lt; </port>";
+ const char *data_2 = "<port xmlns=\"urn:tests:T_DIFF\"> text abc &gt; </port>";
+ const char *diff_expected = "<port xmlns=\"urn:tests:T_DIFF\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"replace\" yang:orig-default=\"false\""
+ " yang:orig-value=\" text abc &lt; \"> text abc &gt; </port>";
+
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+ lyd_free_all(diff);
+
+ /* create data from diff */
+ diff_expected = "<port xmlns=\"urn:tests:T_DIFF\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"replace\" yang:orig-default=\"false\""
+ " yang:orig-value=\" 10^20 \">jjjjjjj</port>";
+
+ CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, diff);
+ data_1 = "<port xmlns=\"urn:tests:T_DIFF\"> 10^20 </port>";
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ const char *expected = "<port xmlns=\"urn:tests:T_DIFF\">jjjjjjj</port>";
+
+ CHECK_LYD_STRING_PARAM(model_1, expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ lyd_free_all(model_1);
+ lyd_free_all(diff);
+
+ /* check creating data breaking restrictions */
+ diff_expected = "<port xmlns=\"urn:tests:T_DIFF\" "
+ "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" "
+ "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\" 555 555\">"
+ "121</port>";
+ CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, model_1);
+ CHECK_LOG_CTX("Unsatisfied length - string \"121\" length is not allowed.",
+ "Schema location \"/T_DIFF:port\", line number 1.");
+
+ /* diff from default value */
+ data_1 = "<cont xmlns=\"urn:tests:T_DIFF1\"></cont>";
+ data_2 = "<cont xmlns=\"urn:tests:T_DIFF1\"> <port> 6 </port> </cont>";
+ diff_expected = "<cont xmlns=\"urn:tests:T_DIFF1\""
+ " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\""
+ " yang:operation=\"create\"><port> 6 </port></cont>";
+
+ schema = MODULE_CREATE_YANG("T_DIFF1",
+ "container cont {\n"
+ " leaf port {type string; default \" 20\n30 \";}"
+ "}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+ CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2);
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff));
+ assert_non_null(diff);
+ CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+ assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff));
+ CHECK_LYD(model_1, model_2);
+ lyd_free_all(diff);
+
+ lyd_free_all(model_1);
+ lyd_free_all(model_2);
+}
+
+static void
+test_print(void **state)
+{
+ (void) state;
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("T_PRINT", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ struct lyd_node *model_1;
+ const char *data_1 = "<port xmlns=\"urn:tests:T_PRINT\"> &lt; hello &gt; </port>";
+
+ CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1);
+
+ /* XML */
+ const char *expected_xml = "<port xmlns=\"urn:tests:T_PRINT\"> &lt; hello &gt; </port>";
+
+ CHECK_LYD_STRING_PARAM(model_1, expected_xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+
+ /* JSON */
+ const char *expected_json = "{\"T_PRINT:port\":\" < hello > \"}";
+
+ CHECK_LYD_STRING_PARAM(model_1, expected_json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK);
+
+ lyd_free_all(model_1);
+}
+
+static void
+test_plugin_store(void **state)
+{
+ (void) state;
+
+ const char *val_text = NULL;
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value value = {0};
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_STRING]);
+ struct lysc_type *lysc_type;
+ char *alloc_text;
+ unsigned int alloc_text_size;
+ LY_ERR ly_ret;
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type string {length \"0 .. 10\";"
+ "pattern '[0-9\\n<>\\\"\\|]*' ;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *) mod->compiled->data)->type;
+
+ /* check proper type */
+ assert_string_equal("libyang 2 - string, version 1", type->id);
+
+ /* check store */
+ val_text = "20";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "20");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "150\n";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "150\n");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "<\"150>\n";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "<\"150>\n");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "<\"150>\n|hi how are you";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, 8,
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "<\"150>\n|");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ val_text = "";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "");
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /*
+ * minor tests
+ * dynamic alocated input text
+ */
+ val_text = "<250>";
+ alloc_text_size = strlen(val_text);
+ alloc_text = (char *) malloc(alloc_text_size + 1);
+ memcpy(alloc_text, val_text, alloc_text_size + 1);
+
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, alloc_text, alloc_text_size,
+ LYPLG_TYPE_STORE_DYNAMIC, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ alloc_text = NULL;
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ CHECK_LYD_VALUE(value, STRING, "<250>");
+ type->free(UTEST_LYCTX, &value);
+
+ /* wrong lysc_type of value */
+ struct lysc_type lysc_type_test = *lysc_type;
+
+ lysc_type_test.basetype = LY_TYPE_UINT8;
+ val_text = "20";
+ ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EINVAL, ly_ret);
+ ly_err_free(err);
+
+ /* TEST pattern backslash */
+
+ schema = MODULE_CREATE_YANG("TPATTERN_BC_1", "leaf port {type string {"
+ "pattern '\\\\[a]b';" /* pattern '\\[a]b'; */
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ val_text = "\\ab";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "\\ab");
+ type->free(UTEST_LYCTX, &value);
+
+ schema = MODULE_CREATE_YANG("TPATTERN_BC_2", "leaf port {type string {"
+ "pattern \"\\\\\\\\[a]b\";" /* pattern "\\\\[a]b"; */
+ "}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ val_text = "\\ab";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, STRING, "\\ab");
+ type->free(UTEST_LYCTX, &value);
+
+ /* ERROR TESTS */
+
+ val_text = "10 \"| bcdei";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+ val_text = "012345678901";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+ val_text = "10";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ ly_err_free(err);
+
+}
+
+static void
+test_plugin_compare(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_STRING]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+
+ /* different type */
+ const char *diff_type_text = "20";
+ struct lyd_value diff_type_val;
+ struct lysc_type *diff_type;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0", "typedef my_int_type {type string; }"
+ "leaf p1 {type my_int_type;}"
+ "leaf p2 {type my_int_type;}"
+ "leaf p3 {type string;}"
+ "leaf p4 {type uint8;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *) mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ const char *val_init[] = {"hi", "hello", "hi", "hello", "hell", "hh"};
+
+ for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* BASIC TEST; */
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[0]), &(values[0])));
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[0]), &(values[2])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[0]), &(values[1])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[1]), &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&(values[1]), &(values[2])));
+ assert_int_equal(LY_SUCCESS, type->compare(&(values[1]), &(values[3])));
+
+ /* SAME TYPE but different node */
+ diff_type_text = "hi";
+ diff_type = ((struct lysc_node_leaf *) mod->compiled->data->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_SUCCESS, type->compare(&diff_type_val, &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /* original type */
+ diff_type_text = "hi";
+ diff_type = ((struct lysc_node_leaf *) mod->compiled->data->next->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /* different type (UINT8) */
+ diff_type_text = "20";
+ diff_type = ((struct lysc_node_leaf *) mod->compiled->data->next->next->next)->type;
+ ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
+ assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
+ type->free(UTEST_LYCTX, &(diff_type_val));
+
+ /* delete values */
+ for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+static void
+test_plugin_print(void **state)
+{
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_STRING]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+
+ /* create schema. Prepare common used variables */
+ const char *schema = MODULE_CREATE_YANG("defs", "leaf port {type string;}");
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *) mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ const char *val_init[] = {"20", "0x4A", "<|>", "\""};
+
+ for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* print value */
+ ly_bool dynamic = 0;
+
+ assert_string_equal("20", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("0x4A", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("<|>", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL));
+ assert_string_equal("\"", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL));
+
+ for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+static void
+test_plugin_dup(void **state)
+{
+
+ struct ly_err_item *err = NULL;
+ struct lys_module *mod;
+ struct lyd_value values[10];
+ struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_STRING]);
+ struct lysc_type *lysc_type[2];
+ const char *schema;
+ LY_ERR ly_ret;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("T0", "leaf port {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type[0] = ((struct lysc_node_leaf *) mod->compiled->data)->type;
+
+ schema = MODULE_CREATE_YANG("T1",
+ "typedef my_int_type {"
+ " type string {length \"1 .. 100\";} default 20;"
+ "}"
+ "leaf port {type my_int_type; }");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type[1] = ((struct lysc_node_leaf *) mod->compiled->data)->type;
+
+ /* CREATE VALUES */
+ const char *val_init[] = {"20", "0x4A", "<\">", "0x4A"};
+
+ for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ ly_ret = type->store(UTEST_LYCTX, lysc_type[it % 2], val_init[it], strlen(val_init[it]),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err);
+ assert_int_equal(LY_SUCCESS, ly_ret);
+ }
+
+ /* print duplicate value */
+ struct lyd_value dup_value;
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[0]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, STRING, "20");
+ assert_ptr_equal(dup_value.realtype, values[0].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[1]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, STRING, "0x4A");
+ assert_ptr_equal(dup_value.realtype, values[1].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[2]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, STRING, "<\">");
+ assert_ptr_equal(dup_value.realtype, values[2].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[3]), &dup_value));
+ CHECK_LYD_VALUE(dup_value, STRING, "0x4A");
+ assert_ptr_equal(dup_value.realtype, values[3].realtype);
+ type->free(UTEST_LYCTX, &dup_value);
+
+ /* error tests */
+ assert_int_equal(LY_EINVAL, type->duplicate(NULL, &(values[0]), &dup_value));
+
+ for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
+ type->free(UTEST_LYCTX, &(values[it]));
+ }
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_schema_yang),
+ UTEST(test_schema_yin),
+ UTEST(test_schema_print),
+ UTEST(test_data_xml),
+ UTEST(test_data_json),
+ UTEST(test_data_lyb),
+ UTEST(test_diff),
+ UTEST(test_print),
+
+ UTEST(test_plugin_store),
+ UTEST(test_plugin_compare),
+ UTEST(test_plugin_print),
+ UTEST(test_plugin_dup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/uint16.c b/tests/utests/types/uint16.c
new file mode 100644
index 0000000..7cbb014
--- /dev/null
+++ b/tests/utests/types/uint16.c
@@ -0,0 +1,75 @@
+/**
+ * @file uint16.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for uint16 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type uint16 {range 150..200;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("defs", "\n 1500 \t\n ");
+ CHECK_LOG_CTX("Unsatisfied range - value \"1500\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 3.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/uint32.c b/tests/utests/types/uint32.c
new file mode 100644
index 0000000..e3e6377
--- /dev/null
+++ b/tests/utests/types/uint32.c
@@ -0,0 +1,75 @@
+/**
+ * @file uint32.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for uint32 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type uint32;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("defs", "-10");
+ CHECK_LOG_CTX("Value \"-10\" is out of type uint32 min/max bounds.",
+ "Schema location \"/defs:port\", line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/uint64.c b/tests/utests/types/uint64.c
new file mode 100644
index 0000000..401c37c
--- /dev/null
+++ b/tests/utests/types/uint64.c
@@ -0,0 +1,83 @@
+/**
+ * @file uint64.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for uint64 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type uint64;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_ERROR_XML("defs", "");
+ CHECK_LOG_CTX("Invalid type uint64 empty value.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ TEST_ERROR_XML("defs", " ");
+ CHECK_LOG_CTX("Invalid type uint64 empty value.",
+ "Schema location \"/defs:port\", line number 1.");
+
+ TEST_ERROR_XML("defs", "10 xxx");
+ CHECK_LOG_CTX("Invalid type uint64 value \"10 xxx\".",
+ "Schema location \"/defs:port\", line number 1.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/uint8.c b/tests/utests/types/uint8.c
new file mode 100644
index 0000000..dad4039
--- /dev/null
+++ b/tests/utests/types/uint8.c
@@ -0,0 +1,77 @@
+/**
+ * @file uint8.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief test for uint8 values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* GLOBAL INCLUDE HEADERS */
+#include <ctype.h>
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+#include "plugins_internal.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf port {type uint8 {range 150..200;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML("defs", "\n 150 \t\n ", UINT8, "150", 150);
+
+ TEST_ERROR_XML("defs", "\n 15 \t\n ");
+ CHECK_LOG_CTX("Unsatisfied range - value \"15\" is out of the allowed range.",
+ "Schema location \"/defs:port\", line number 3.");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
new file mode 100644
index 0000000..9a0705a
--- /dev/null
+++ b/tests/utests/types/union.c
@@ -0,0 +1,136 @@
+/**
+ * @file union.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+#include "path.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 1, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, RET) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, RET, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+ const enum ly_path_pred_type val1[] = {LY_PATH_PREDTYPE_LEAFLIST};
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "identity ident1; identity ident2 {base ident1;}"
+ "leaf un1 {type union {"
+ " type leafref {path /int8; require-instance true;}"
+ " type leafref {path /int64; require-instance true;}"
+ " type union { type identityref {base ident1;} type instance-identifier {require-instance true;} }"
+ " type string {length 1..20;}}}"
+ "leaf int8 {type int8 {range 10..20;}}"
+ "leaf int64 {type int64;}"
+ "leaf-list llist {type string;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">12</int8>",
+ "defs", "", "un1", "12", UNION, "12", INT8, "12", 12);
+
+ TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">12</int8>",
+ "defs", "", "un1", "2", UNION, "2", STRING, "2");
+
+ TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">10</int8>",
+ "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident2", UNION, "defs:ident2", IDENT, "defs:ident2", "ident2");
+
+ TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">10</int8>",
+ "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident55", UNION, "x:ident55", STRING, "x:ident55");
+
+ TEST_SUCCESS_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>"
+ "<llist xmlns=\"urn:tests:defs\">y</llist>",
+ "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "/x:llist[.='y']", UNION, "/defs:llist[.='y']",
+ INST, "/defs:llist[.='y']", val1);
+
+ TEST_SUCCESS_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>"
+ "<llist xmlns=\"urn:tests:defs\">y</llist>",
+ "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "/x:llist[3]", UNION, "/x:llist[3]",
+ STRING, "/x:llist[3]");
+
+ /* invalid value */
+ TEST_ERROR_XML2("",
+ "defs", "", "un1", "123456789012345678901", LY_EVALID);
+ CHECK_LOG_CTX("Invalid union value \"123456789012345678901\" - no matching subtype found.",
+ "Schema location \"/defs:un1\", line number 1.");
+}
+
+static void
+test_plugin_lyb(void **state)
+{
+ const char *schema;
+
+ schema = MODULE_CREATE_YANG("lyb",
+ "leaf int8 {type int8 {range 10..20;}}"
+ "leaf un1 {type union {"
+ " type leafref {path /int8; require-instance true;}"
+ " type string;}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ TEST_SUCCESS_LYB("lyb", "un1", "12");
+ TEST_SUCCESS_LYB("lyb", "un1", "some_string");
+ TEST_SUCCESS_LYB("lyb", "un1", "");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_plugin_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
new file mode 100644
index 0000000..6ce7671
--- /dev/null
+++ b/tests/utests/types/yang_types.c
@@ -0,0 +1,223 @@
+/**
+ * @file yang_types.c
+ * @author Michal Vaško <mvasko@cesnet.cz>
+ * @brief test for ietf-yang-types values
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define _UTEST_MAIN_
+#include "../utests.h"
+
+#include <stdlib.h>
+
+#include "compat.h"
+#include "libyang.h"
+
+#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<module name=\"" MOD_NAME "\"\n" \
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \
+ " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \
+ " <yang-version value=\"1.1\"/>\n" \
+ " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \
+ " <prefix value=\"pref\"/>\n" \
+ NODES \
+ "</module>\n"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+ "module " MOD_NAME " {\n" \
+ " yang-version 1.1;\n" \
+ " namespace \"urn:tests:" MOD_NAME "\";\n" \
+ " prefix pref;\n" \
+ " import ietf-yang-types {\n" \
+ " prefix yang;\n" \
+ " }\n" \
+ NODES \
+ "}\n"
+
+#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \
+ { \
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
+ lyd_free_all(tree); \
+ }
+
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+ {\
+ struct lyd_node *tree; \
+ const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ assert_null(tree); \
+ }
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+ { \
+ struct lyd_node *tree_1; \
+ struct lyd_node *tree_2; \
+ char *xml_out, *data; \
+ data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+ assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+ assert_non_null(tree_2); \
+ CHECK_LYD(tree_1, tree_2); \
+ free(xml_out); \
+ lyd_free_all(tree_1); \
+ lyd_free_all(tree_2); \
+ }
+
+static void
+test_data_xml(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("a",
+ "leaf l {type yang:date-and-time;}"
+ "leaf l2 {type yang:xpath1.0;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+ schema = MODULE_CREATE_YANG("b",
+ "");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* date-and-time */
+#if defined (HAVE_TM_GMTOFF) && defined (HAVE_TIME_H_TIMEZONE)
+ TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T21:15:15.88888-02:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T06:14:15-02:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T20:15:15-02:00");
+
+ /* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */
+ TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T00:59:59-02:00");
+ TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1969-12-31T23:59:59-02:00");
+
+ /* canonize */
+ TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-01T23:15:15-02:00");
+
+ /* fractional hours */
+ TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T16:45:15.88888-02:00");
+#else
+ /* Tests run with a TZ offset of +02:00, but this platform cannot represent that in time_t,
+ * so libyang always returns unspecified TZ. */
+ TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T23:15:15.88888-00:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T08:14:15-00:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T22:15:15-00:00");
+
+ /* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */
+ TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T02:59:59-00:00");
+ TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1970-01-01T01:59:59-00:00");
+
+ /* canonize */
+ TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-02T01:15:15-00:00");
+
+ /* fractional hours */
+ TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T18:45:15.88888-00:00");
+#endif
+
+ /* unknown timezone -- timezone conversion MUST NOT happen */
+ TEST_SUCCESS_XML("a", "l", "2017-02-01T00:00:00-00:00", STRING, "2017-02-01T00:00:00-00:00");
+ TEST_SUCCESS_XML("a", "l", "2021-02-29T00:00:00-00:00", STRING, "2021-03-01T00:00:00-00:00");
+
+ TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00");
+ CHECK_LOG_CTX("Unsatisfied pattern - \"2005-05-31T23:15:15.-08:00\" does not conform to "
+ "\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".",
+ "Schema location \"/a:l\", line number 1.");
+
+ /* xpath1.0 */
+ TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']", STRING, "/a:l2[.='4']");
+ TEST_SUCCESS_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l2",
+ "/yl:yang-library/yl:datastore/yl:name = 'ds:running'", STRING,
+ "/ietf-yang-library:yang-library/datastore/name='ietf-datastores:running'");
+ TEST_SUCCESS_XML("a\" xmlns:a1=\"urn:tests:a\" xmlns:a2=\"urn:tests:a\" xmlns:bb=\"urn:tests:b", "l2",
+ "/a1:node1/a2:node2[a1:node3/bb:node4]/bb:node5 | bb:node6 and (bb:node7)", STRING,
+ "/a:node1/node2[node3/b:node4]/b:node5 | b:node6 and (b:node7)");
+ TEST_SUCCESS_XML("a", "l2", "/l2[. = '4']", STRING, "/l2[.='4']");
+
+ TEST_ERROR_XML("a", "l2", "/a:l2[. = '4']");
+ CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location \"/a:l2\", line number 1.");
+ TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l2",
+ "/yl:yang-library/yl:datastore/yl::name");
+ CHECK_LOG_CTX("Storing value failed.", "Schema location \"/a:l2\", line number 1.",
+ "Invalid character 'y'[31] of expression '/yl:yang-library/yl:datastore/yl::name'.",
+ "Schema location \"/a:l2\", line number 1.");
+}
+
+static void
+test_print(void **state)
+{
+ const char *schema = MODULE_CREATE_YANG("a", "leaf l {type yang:xpath1.0;}");
+ const char *data, *expected;
+ struct lyd_node *tree;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* XML data */
+ data = "<l xmlns=\"urn:tests:a\" xmlns:aa=\"urn:tests:a\">/aa:l[. = '/aa:l']</l>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+
+ /* XML print */
+ expected = "<l xmlns=\"urn:tests:a\" xmlns:pref=\"urn:tests:a\">/pref:l[.='/pref:l']</l>";
+ CHECK_LYD_STRING_PARAM(tree, expected, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+
+ /* JSON print */
+ expected = "{\"a:l\":\"/a:l[.='/a:l']\"}";
+ CHECK_LYD_STRING_PARAM(tree, expected, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+
+ lyd_free_tree(tree);
+
+ /* JSON data */
+ data = "{\"a:l\":\"/a:l/k/m[. = '/a:l']\"}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+
+ /* XML print */
+ expected = "<l xmlns=\"urn:tests:a\" xmlns:pref=\"urn:tests:a\">/pref:l/pref:k/pref:m[.='/pref:l']</l>";
+ CHECK_LYD_STRING_PARAM(tree, expected, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+
+ /* JSON print */
+ expected = "{\"a:l\":\"/a:l/k/m[. = '/a:l']\"}";
+ CHECK_LYD_STRING_PARAM(tree, expected, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+
+ lyd_free_tree(tree);
+}
+
+static void
+test_lyb(void **state)
+{
+ const char *schema;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("a",
+ "leaf l {type yang:date-and-time;}"
+ "leaf l2 {type yang:xpath1.0;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* date-and-time */
+ TEST_SUCCESS_LYB("a", "l", "2005-05-25T23:15:15.88888Z");
+ TEST_SUCCESS_LYB("a", "l", "2005-05-31T23:15:15-08:59");
+ TEST_SUCCESS_LYB("a", "l", "2005-05-01T20:15:15-00:00");
+
+ /* xpath1.0 */
+ TEST_SUCCESS_LYB("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_data_xml),
+ UTEST(test_print),
+ UTEST(test_lyb),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/utests.h b/tests/utests/utests.h
new file mode 100644
index 0000000..877d048
--- /dev/null
+++ b/tests/utests/utests.h
@@ -0,0 +1,1505 @@
+/**
+ * @file utests.h
+ * @author Radek IÅ¡a <isa@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief this file contains macros for simplification test writing
+ *
+ * Copyright (c) 2021 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef _UTESTS_H_
+#define _UTESTS_H_
+
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <cmocka.h>
+
+#include <string.h>
+
+#include "libyang.h"
+#include "plugins_exts/metadata.h"
+#include "plugins_internal.h"
+#include "plugins_types.h"
+#include "tests_config.h"
+#include "tree_schema_internal.h"
+
+/**
+ * TESTS OVERVIEW
+ *
+ * To include utest's environment, just include "utests.h" in the test's source
+ * code. In case it is the main source code for a cmocka test group (there is a
+ * main() function), define _UTEST_MAIN_ before including this header.
+ *
+ * TESTS VARIABLES
+ *
+ * Checking macros use internal storage to store various variables necessary
+ * during the checking. It is possible to access these variables using the
+ * following macros:
+ *
+ * UTEST_LYCTX - libyang context
+ * UTEST_IN - input handler
+ * UTEST_OUT - output handler
+ *
+ * All these variables are cleaned with test's teardown.
+ *
+ * TESTS SETUP
+ *
+ * CMocka's CMUnitTest list definition macros (cmoka_unit_test*()) are replaced
+ * by UTEST macro with possibility to specify own setup and teardown functions:
+ *
+ * UTEST(test_func) - only implicit setup and teardown functions are used
+ * UTEST(test_func, setup) - implicit teardown but own setup
+ * UTEST(test_func, setup, teardown) - both setup and teardown are test-specific
+ *
+ * Note that the tests environment always provide (and need) internal setup and
+ * teardown functions. In case the test-specific setup or teardown are used, they
+ * are supposed to include UTEST_SETUP at the setup beginning and UTEST_TEARDOWN
+ * at the teardown end.
+ *
+ * Libyang context is part of the prepared environment. To add a schema into the
+ * context (despite it is in the test-specific setup or in test function itself),
+ * use UTEST_ADD_MODULE macro.
+ *
+ * LOGGING
+ *
+ * There are 2 macros to check content of the log from the previously called
+ * libyang function. CHECK_LOG macro test only the last error message and path
+ * stored directly via logging callback. CHECK_LOG_CTX gets error message and
+ * path from the libyang context (in case the function does not store the error
+ * information into the libyang context, the message cannot be checked this way).
+ * libyang is set to store multiple error information, so multiple couples of
+ * error message and path can be provided to be checked (the first couple
+ * corresponds to the latest error). The macro also cleanups the errors list, so
+ * it is fine to check that there is no error after succeeding successful
+ * function call.
+ */
+
+/**
+ * @brief Test's context to provide common storage for various variables.
+ */
+struct utest_context {
+ struct ly_ctx *ctx; /**< libyang context */
+
+ char *err_msg; /**< Directly logged error message */
+ char *err_path; /**< Directly logged error path */
+
+ struct ly_in *in; /**< Input handler */
+ struct ly_out *out; /**< Outpu handler */
+
+ char *orig_tz; /**< Original "TZ" environment variable value */
+};
+
+/**
+ * @brief Shortcut to access utest_context.
+ */
+#define _UC ((struct utest_context *)*state)
+
+/**
+ * @brief libyang context provider.
+ */
+#define UTEST_LYCTX (_UC->ctx)
+
+/**
+ * @brief Context's input handler provider
+ */
+#define UTEST_IN (_UC->in)
+
+/**
+ * @brief Context's input handler provider
+ */
+#define UTEST_OUT (_UC->out)
+
+/**
+ * @brief Parse (and validate) data from the input handler as a YANG data tree.
+ *
+ * @param[in] INPUT The input data in the specified @p format to parse (and validate)
+ * @param[in] INPUT_FORMAT Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
+ * @param[in] PARSE_OPTIONS Options for parser, see @ref dataparseroptions.
+ * @param[in] VALIDATE_OPTIONS Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] RET expected return status
+ * @param[out] OUT_NODE Resulting data tree built from the input data. Note that NULL can be a valid result as a
+ * representation of an empty YANG data tree.
+ */
+#define CHECK_PARSE_LYD_PARAM(INPUT, INPUT_FORMAT, PARSE_OPTIONS, VALIDATE_OPTIONS, RET, OUT_NODE) \
+ { \
+ LY_ERR _r = lyd_parse_data_mem(_UC->ctx, INPUT, INPUT_FORMAT, PARSE_OPTIONS, VALIDATE_OPTIONS, &OUT_NODE); \
+ if (_r != RET) { \
+ if (_r) { \
+ fail_msg("%s != 0x%d; MSG: %s", #RET, _r, ly_err_last(_UC->ctx)->msg); \
+ } else { \
+ fail_msg("%s != 0x%d", #RET, _r); \
+ } \
+ } \
+ }
+
+/**
+ * @brief Check if lyd_node and his subnodes have correct values. Print lyd_node and his sunodes int o string in json or xml format.
+ * @param[in] NODE pointer to lyd_node
+ * @param[in] TEXT expected output string in json or xml format.
+ * @param[in] FORMAT format of input text. LYD_JSON, LYD_XML
+ * @param[in] PARAM options [Data printer flags](@ref dataprinterflags).
+ */
+#define CHECK_LYD_STRING_PARAM(NODE, TEXT, FORMAT, PARAM) \
+ { \
+ char *str; \
+ LY_ERR _r = lyd_print_mem(&str, NODE, FORMAT, PARAM); \
+ if (_r) { \
+ fail_msg("Print err 0x%d; MSG: %s", _r, ly_err_last(_UC->ctx)->msg); \
+ } \
+ assert_string_equal(str, TEXT); \
+ free(str); \
+ }
+
+/**
+ * @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;
+ * @param[in] NODE_1 pointer to lyd_node
+ * @param[in] NODE_2 pointer to lyd_node
+ */
+#define CHECK_LYD(NODE_1, NODE_2) \
+ { \
+ 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_non_null(str1); \
+ assert_non_null(str2); \
+ assert_string_equal(str1, str2); \
+ free(str1); \
+ free(str2); \
+ }
+
+/*
+ * SUPPORT MACROS
+ */
+
+/**
+ * @brief Internal macro witch assert that two given string are equal or are both null.
+ *
+ * @param[in] STRING string to check
+ * @param[in] TEXT string to compare
+ */
+#define CHECK_STRING(STRING, TEXT)\
+ if (TEXT == NULL) { \
+ assert_null(STRING); \
+ } else { \
+ assert_non_null(STRING); \
+ assert_string_equal(STRING, TEXT); \
+ }
+
+/**
+ * @brief Internal macro witch assert that pointer is null when flag is 0.
+ *
+ * @param[in] POINTER pointer to check
+ * @param[in] FLAG 0 -> pointer is NULL, 1 -> pointer is not null
+ */
+#define CHECK_POINTER(POINTER, FLAG) \
+ assert_true(FLAG == 0 ? POINTER == NULL : POINTER != NULL)
+
+/**
+ * @brief Internal macro check size of [sized array](@ref sizedarrays)'s
+ *
+ * @param[in] ARRAY pointer to [sized array](@ref sizedarrays)
+ * @param[in] SIZE expected [sized array](@ref sizedarrays) size of array
+ */
+#define CHECK_ARRAY(ARRAY, SIZE) \
+ assert_true((SIZE == 0) ? \
+ (ARRAY == NULL) : \
+ (ARRAY != NULL && SIZE == LY_ARRAY_COUNT(ARRAY)));
+
+/*
+ * LIBYANG NODE CHECKING
+ */
+
+/**
+ * @brief check compileted type
+ *
+ * @param[in] NODE pointer to lysc_type value
+ * @param[in] TYPE expected type [LY_DATA_TYPE](@ref LY_DATA_TYPE)
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ */
+#define CHECK_LYSC_TYPE(NODE, TYPE, EXTS) \
+ assert_non_null(NODE); \
+ assert_int_equal((NODE)->basetype, TYPE); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_ptr_equal((NODE)->plugin, lyplg_type_plugin_find("", NULL, ly_data_type2str[TYPE]))
+
+/**
+ * @brief check compileted numeric type
+ *
+ * @param[in] NODE pointer to lysc_type_num value
+ * @param[in] TYPE expected type [LY_DATA_TYPE](@ref LY_DATA_TYPE)
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ * @warning only integer types INT, UINT, NUM
+ */
+#define CHECK_LYSC_TYPE_NUM(NODE, TYPE, EXTS, RANGE) \
+ CHECK_LYSC_TYPE(NODE, TYPE, EXTS);\
+ CHECK_POINTER((NODE)->range, RANGE)
+
+/**
+ * @brief check compiled string type
+ *
+ * @param[in] NODE pointer to lysc_type_num value
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ * @param[in] LENGTH 0 -> node dosnt have length limitation, 1 -> node have length limitation
+ * @param[in] PATTERNS expected number of patterns [sized array](@ref sizedarrays)
+ * @warning only integer types INT, UINT, NUM
+ */
+#define CHECK_LYSC_TYPE_STR(NODE, EXTS, LENGTH, PATTERNS) \
+ CHECK_LYSC_TYPE(NODE, LY_TYPE_STRING, EXTS); \
+ CHECK_POINTER((NODE)->length, LENGTH); \
+ CHECK_ARRAY((NODE)->patterns, PATTERNS)
+
+/**
+ * @brief check compiled bits type
+ *
+ * @param[in] NODE pointer to lysc_type_num value
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ * @param[in] BITS expected number of bits
+ * @warning only integer types INT, UINT, NUM
+ */
+#define CHECK_LYSC_TYPE_BITS(NODE, EXTS, BITS) \
+ CHECK_LYSC_TYPE(NODE, LY_TYPE_BITS, EXTS); \
+ CHECK_ARRAY((NODE)->bits, BITS)
+
+#define CHECK_LYSC_TYPE_BITENUM_ITEM(NODE, POSITION, DSC, EXTS, FLAGS, NAME, REF)\
+ assert_non_null(NODE); \
+ assert_int_equal((NODE)->position, POSITION); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_STRING((NODE)->name, NAME); \
+ CHECK_STRING((NODE)->ref, REF) \
+
+/**
+ * @brief check range
+ *
+ * @param[in] NODE pointer to lysc_range value
+ * @param[in] DSC expected descriptin (string)
+ * @param[in] EAPPTAG expected string reprezenting error-app-tag value
+ * @param[in] EMSG expected string reprezenting error message
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ * @param[in] PARTS expected [sized array](@ref sizedarrays) number of rang limitations
+ * @param[in] REF expected reference
+ */
+#define CHECK_LYSC_RANGE(NODE, DSC, EAPPTAG, EMSG, EXTS, PARTS, REF) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_STRING((NODE)->eapptag, EAPPTAG); \
+ CHECK_STRING((NODE)->emsg, EMSG); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ CHECK_ARRAY((NODE)->parts, PARTS); \
+ CHECK_STRING((NODE)->ref, REF)
+
+/**
+ * @brief check pattern
+ *
+ * @param[in] NODE pointer to lysc_pattern value
+ * @param[in] DSC expected descriptin (string)
+ * @param[in] EAPPTAG expected string reprezenting error-app-tag value
+ * @param[in] EMSG expected string reprezenting error message
+ * @param[in] EEXPR expected string reprezenting original, not compiled, regular expression
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ * @param[in] INVERTED if regular expression is inverted.
+ * @param[in] REF expected reference
+ */
+#define CHECK_LYSC_PATTERN(NODE, DSC, EAPPTAG, EMSG, EXPR, EXTS, INVERTED, REF) \
+ assert_non_null(NODE); \
+ assert_non_null((NODE)->code); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_STRING((NODE)->eapptag, EAPPTAG); \
+ CHECK_STRING((NODE)->emsg, EMSG); \
+ CHECK_STRING((NODE)->expr, EXPR); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->inverted, INVERTED); \
+ CHECK_STRING((NODE)->ref, REF)
+
+/**
+ * @brief assert that lysp_action_inout structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_action_inout variable
+ * @param[in] DATA 0 -> check if pointer to data is NULL, 1 -> check if pointer to data is not null
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list
+ * @param[in] GROUPINGS expected [sized array](@ref sizedarrays) size of grouping list
+ * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of must restriction list
+ * @param[in] NODETYPE node type. LYS_INPUT or LYS_OUTPUT
+ * @param[in] PARENT 0 -> check if node is root, 1 -> check if node is not root
+ * @param[in] TYPEDEFS expected [sized array](@ref sizedarrays) size of typedefs list
+ */
+#define CHECK_LYSP_ACTION_INOUT(NODE, DATA, EXTS, GROUPINGS, MUSTS, NODETYPE, PARENT, TYPEDEFS) \
+ assert_non_null(NODE); \
+ CHECK_POINTER((NODE)->child, DATA); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ CHECK_POINTER((NODE)->groupings, GROUPINGS); \
+ CHECK_ARRAY((NODE)->musts, MUSTS); \
+ assert_int_equal((NODE)->nodetype, NODETYPE); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ CHECK_ARRAY((NODE)->typedefs, TYPEDEFS);
+
+/**
+ * @brief assert that lysp_action structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_action variable
+ * @param[in] DSC expected description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extension list
+ * @param[in] FLAGS expected [schema node flags](@ref snodeflags)
+ * @param[in] GROUPINGS expected [sized array](@ref sizedarrays) size of grouping list
+ * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of if-feature expressions list
+ * @param[in] INPUT_* ::LYSP_ACTION_INOUT_CHECK
+ * @param[in] NAME expected name
+ * @param[in] NODETYPE node type. LYS_RPC or LYS_ACTION
+ * @param[in] OUTPUT_* ::LYSP_ACTION_INOUT_CHECK
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] REF expected reference
+ * @param[in] TYPEDEFS expected [sized array](@ref sizedarrays) size of list of typedefs
+ */
+#define CHECK_LYSP_ACTION(NODE, DSC, EXTS, FLAGS, GROUPINGS, IFFEATURES, \
+ INPUT_DATA, INPUT_EXTS, INPUT_GROUPINGS, INPUT_MUSTS, \
+ INPUT_PARENT, INPUT_TYPEDEFS, \
+ NAME, NODETYPE, \
+ OUTPUT_DATA, OUTPUT_EXTS, OUTPUT_GROUPINGS, OUTPUT_MUSTS, \
+ OUTPUT_PARENT, OUTPUT_TYPEDEFS, \
+ PARENT, REF, TYPEDEFS) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_POINTER((NODE)->groupings, GROUPINGS); \
+ CHECK_ARRAY((NODE)->iffeatures, IFFEATURES); \
+ CHECK_LYSP_ACTION_INOUT(&((NODE)->input), INPUT_DATA, INPUT_EXTS, INPUT_GROUPINGS, \
+ INPUT_MUSTS, LYS_INPUT, INPUT_PARENT, INPUT_TYPEDEFS); \
+ assert_string_equal((NODE)->name, NAME); \
+ assert_int_equal((NODE)->nodetype, NODETYPE); \
+ CHECK_LYSP_ACTION_INOUT(&((NODE)->output), OUTPUT_DATA, OUTPUT_EXTS, OUTPUT_GROUPINGS, \
+ OUTPUT_MUSTS, LYS_OUTPUT, OUTPUT_PARENT, OUTPUT_TYPEDEFS); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ CHECK_STRING((NODE)->ref, REF); \
+ CHECK_ARRAY((NODE)->typedefs, TYPEDEFS) \
+
+/**
+ * @brief assert that lysp_when structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_when variable
+ * @param[in] COND expected string specifid condition
+ * @param[in] DSC expected string description statement
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension array
+ * @param[in] REF expected string reference
+ */
+#define CHECK_LYSP_WHEN(NODE, COND, DSC, EXTS, REF) \
+ assert_non_null(NODE); \
+ assert_string_equal((NODE)->cond, COND); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ if (REF == NULL) { \
+ assert_null((NODE)->ref); \
+ } else { \
+ assert_non_null((NODE)->ref); \
+ assert_string_equal((NODE)->ref, REF); \
+ }
+
+/**
+ * @brief assert that lysp_restr structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_restr variable
+ * @param[in] ARG_STR expected string. The restriction expression/value
+ * @param[in] DSC expected descrition
+ * @param[in] EAPPTAG expected string reprezenting error-app-tag value
+ * @param[in] EMSG expected string reprezenting error message
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension array
+ * @param[in] REF expected reference
+ */
+
+#define CHECK_LYSP_RESTR(NODE, ARG_STR, DSC, EAPPTAG, EMSG, EXTS, REF) \
+ assert_non_null(NODE); \
+ assert_non_null((NODE)->arg.mod); \
+ assert_string_equal((NODE)->arg.str, ARG_STR); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_STRING((NODE)->eapptag, EAPPTAG); \
+ CHECK_STRING((NODE)->emsg, EMSG); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ CHECK_STRING((NODE)->ref, REF);
+
+/**
+ * @brief assert that lysp_import structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_import variable
+ * @param[in] DSC expected description or NULL
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extensions
+ * @param[in] NAME expected name of imported module
+ * @param[in] PREFIX expected prefix for the data from the imported schema
+ * @param[in] REF expected reference
+ * @param[in] REV expected reprezenting date in format "11-10-2020"
+ */
+#define CHECK_LYSP_IMPORT(NODE, DSC, EXTS, NAME, PREFIX, REF, REV) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_string_equal((NODE)->name, NAME); \
+ assert_string_equal((NODE)->prefix, PREFIX); \
+ CHECK_STRING((NODE)->ref, REF); \
+ CHECK_STRING((NODE)->rev, REV); \
+
+/**
+ * @brief assert that lysp_ext structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_ext_instance variable
+ * @param[in] ARGNAME expected argument name
+ * @param[in] COMPILED 0 -> compiled data dosnt exists, 1 -> compiled data exists
+ * @param[in] DSC expected string reprezent description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension instances
+ * @param[in] FLAGS expected LYS_STATUS_* and LYS_YINELEM_* values (@ref snodeflags)
+ * @param[in] NAME expected name
+ * @param[in] REF expected ref
+ */
+#define CHECK_LYSP_EXT(NODE, ARGNAME, COMPILED, DSC, EXTS, FLAGS, NAME, REF) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->argname, ARGNAME); \
+ CHECK_POINTER((NODE)->compiled, COMPILED); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ assert_string_equal((NODE)->name, NAME); \
+ CHECK_STRING((NODE)->ref, REF);
+
+/**
+ * @brief assert that lysp_ext_instance structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_ext_instance variable
+ * @param[in] ARGUMENT expected optional value of the extension's argument
+ * @param[in] CHILD 0 -> node doesnt have child, 1 -> node have children
+ * @param[in] PARENT_STMT expected value identifying placement of the extension instance
+ * @param[in] PARENT_STMT_INDEX expected indentifi index
+ * @param[in] FORMAT expected format
+ */
+#define CHECK_LYSP_EXT_INSTANCE(NODE, ARGUMENT, CHILD, PARENT_STMT, PARENT_STMT_INDEX, NAME, FORMAT) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->argument, ARGUMENT); \
+ CHECK_POINTER((NODE)->child, CHILD); \
+ assert_int_equal((NODE)->parent_stmt, PARENT_STMT); \
+ assert_int_equal((NODE)->parent_stmt_index, PARENT_STMT_INDEX); \
+ assert_string_equal((NODE)->name, NAME); \
+ assert_int_equal((NODE)->format, FORMAT);
+
+/**
+ * @brief assert that lysp_stmt structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_stmt variable
+ * @param[in] ARG expected statemet argumet
+ * @param[in] CHILD 0 -> node doesnt have child, 1 -> node have children
+ * @param[in] FLAGS expected statement flags, can be set to LYS_YIN_ATTR
+ * @param[in] KW expected numeric respresentation of the stmt value
+ * @param[in] NEXT 0 -> pointer is NULL, 1 -> pointer is not null
+ * @param[in] STMS expected identifier of the statement
+ */
+#define CHECK_LYSP_STMT(NODE, ARG, CHILD, FLAGS, KW, NEXT, STMT) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->arg, ARG); \
+ CHECK_POINTER((NODE)->child, CHILD); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ assert_int_equal((NODE)->kw, KW); \
+ CHECK_POINTER((NODE)->next, NEXT); \
+ assert_string_equal((NODE)->stmt, STMT); \
+
+/**
+ * @brief assert that lysp_type_enum structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_type_enum variable
+ * @param[in] DSC expected description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS only LYS_STATUS_ and LYS_SET_VALUE values are allowed
+ * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] NAME expected name
+ * @param[in] REF expected reference statement
+ * @param[in] VALUE expected enum's value or bit's position
+ */
+#define CHECK_LYSP_TYPE_ENUM(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, REF, VALUE) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_ARRAY((NODE)->iffeatures, IFFEATURES); \
+ CHECK_STRING((NODE)->name, NAME); \
+ CHECK_STRING((NODE)->ref, REF); \
+ assert_int_equal(VALUE, (NODE)->value);
+
+/**
+ * @brief assert that lysp_type_enum structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_type variable
+ * @param[in] BASES expected [sized array](@ref sizedarrays) size of list of indentifiers
+ * @param[in] BITS expected [sized array](@ref sizedarrays) size of list of bits
+ * @param[in] COMPILED 0 -> pointer to compiled type is null, 1 -> pointer to compilet type is valid
+ * @param[in] ENUMS expected [sized array](@ref sizedarrays) size of list of enums-stmts
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension instances
+ * @param[in] FLAGS expected flags
+ * @param[in] FRACTION_DIGITS expected number of fraction digits decimal64
+ * @param[in] LENGTH expected 0 -> there isnt any restriction on length, 1 -> type is restricted on length (string, binary)
+ * @param[in] NAME expected name of type
+ * @param[in] PATH 0 -> no pointer to parsed path, 1 -> pointer to parsed path is valid
+ * @param[in] PATTERNS expected [sized array](@ref sizedarrays) size of list of patterns for string
+ * @param[in] PMOD expected submodule where type is defined 0 -> pointer is null, 1 -> pointer is not null
+ * @param[in] RANGE expected [sized array](@ref sizedarrays) size of list of range restriction
+ * @param[in] REQUIRE_INSTANCE expected require instance flag
+ * @param[in] TYPES expected [sized array](@ref sizedarrays) size of list of sub-types
+ */
+#define CHECK_LYSP_TYPE(NODE, BASES, BITS, COMPILED, ENUMS, EXTS, FLAGS, FRACTIONS_DIGITS, \
+ LENGTH, NAME, PATH, PATTERNS, PMOD, RANGE, REQUIRE_INSTANCE, TYPES) \
+ assert_non_null(NODE);\
+ CHECK_ARRAY((NODE)->bases, BASES); \
+ CHECK_ARRAY((NODE)->bits, BITS); \
+ CHECK_POINTER((NODE)->compiled, COMPILED); \
+ CHECK_ARRAY((NODE)->enums, ENUMS); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ assert_int_equal((NODE)->fraction_digits, FRACTIONS_DIGITS); \
+ CHECK_POINTER((NODE)->length, LENGTH); \
+ CHECK_STRING((NODE)->name, NAME); \
+ CHECK_POINTER((NODE)->path, PATH); \
+ CHECK_ARRAY((NODE)->patterns, PATTERNS); \
+ CHECK_POINTER((NODE)->pmod, PMOD); \
+ CHECK_POINTER((NODE)->range, RANGE); \
+ assert_int_equal((NODE)->require_instance, REQUIRE_INSTANCE); \
+ CHECK_ARRAY((NODE)->types , TYPES)
+
+/**
+ * @brief assert that lysp_node structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_node variable
+ * @param[in] DSC expected description statement
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] NAME expected name
+ * @param[in] NEXT 0 pointer is null, 1 pointer is not null
+ * @param[in] NODETYPE node type LYS_UNKNOWN, LYS_CONTAINER, LYS_CHOICE, LYS_LEAF, LYS_LEAFLIST,
+ * LYS_LIST, LYS_ANYXML, LYS_ANYDATA, LYS_CASE, LYS_RPC, LYS_ACTION, LYS_NOTIF,
+ * LYS_USES, LYS_INPUT, LYS_OUTPUT, LYS_GROUPING, LYS_AUGMENT
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] REF expected reference statement
+ * @param[in] WHEN 0-> pointer is null, 1 -> pointer is not null
+ */
+#define CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, NODETYPE, PARENT, REF, WHEN) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_ARRAY((NODE)->iffeatures, IFFEATURES); \
+ CHECK_STRING((NODE)->name, NAME); \
+ CHECK_POINTER((NODE)->next, NEXT); \
+ assert_int_equal((NODE)->nodetype, NODETYPE); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ CHECK_STRING((NODE)->ref, REF); \
+ CHECK_POINTER(lysp_node_when((struct lysp_node *)NODE), WHEN);
+
+/**
+ * @brief assert that lysp_node structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_node variable
+ * @param[in] DSC expected description statement
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] NAME expected name
+ * @param[in] NEXT 0 pointer is null, 1 pointer is not null
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] REF expected reference statement
+ * @param[in] WHEN 0-> pointer is null, 1 -> pointer is not null
+ * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of list of must restriction
+ * @param[in] UNITS expected string reprezenting units
+ * @param[in] DFLT 0-> node dosn't have default value. 1 -> node have default value
+ */
+#define CHECK_LYSP_NODE_LEAF(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, \
+ PARENT, REF, WHEN, MUSTS, UNITS, DFLT) \
+ CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, LYS_LEAF, PARENT, REF, WHEN); \
+ CHECK_ARRAY((NODE)->musts, MUSTS); \
+ CHECK_STRING((NODE)->units, UNITS); \
+ CHECK_STRING((NODE)->dflt.str, DFLT);
+
+/**
+ * @brief assert that lysc_notif structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_notif variable
+ * @param[in] DATA 0 pointer is null, 1 pointer is not null
+ * @param[in] DSC expected description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] MODULE 0 pointer is null, 1 pointer is not null
+ * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of list of must restriction
+ * @param[in] NAME expected name
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null
+ * @param[in] REF expected reference
+ * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements
+ */
+#define CHECK_LYSC_NOTIF(NODE, DATA, DSC, EXTS, FLAGS, MODULE, MUSTS, NAME, PARENT, PRIV, REF, WHEN) \
+ assert_non_null(NODE); \
+ CHECK_POINTER((NODE)->child, DATA); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_POINTER((NODE)->module, MODULE); \
+ CHECK_ARRAY((NODE)->musts, MUSTS); \
+ assert_string_equal((NODE)->name, NAME); \
+ assert_int_equal((NODE)->nodetype, LYS_NOTIF); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ CHECK_POINTER((NODE)->priv, PRIV); \
+ CHECK_STRING((NODE)->ref, REF); \
+ CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN);
+
+/**
+ * @brief assert that lysc_action_inout structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_notif variable
+ * @param[in] DATA 0 pointer is null, 1 pointer is not null
+ * @param[in] MUST expected [sized array](@ref sizedarrays) size of list of must restrictions
+ * @param[in] NODETYPE LYS_INPUT or LYS_OUTPUT
+ */
+#define CHECK_LYSC_ACTION_INOUT(NODE, DATA, MUST, NODETYPE) \
+ assert_non_null(NODE); \
+ CHECK_POINTER((NODE)->child, DATA); \
+ CHECK_ARRAY((NODE)->musts, MUST); \
+ assert_int_equal((NODE)->nodetype, NODETYPE);
+
+/**
+ * @brief assert that lysc_action structure members are correct
+ *
+ * @param[in] NODE pointer to lysp_action variable
+ * @param[in] DSC string description statement
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] INPUT_DATA 0 pointer is null, 1 pointer is not null
+ * @param[in] INPUT_MUST expected [sized array](@ref sizedarrays) size of input list of must restrictions
+ * @param[in] INPUT_EXTS expected [sized array](@ref sizedarrays) size of the input extension instances of input
+ * @param[in] MODULE 0 pointer is null, 1 pointer is not null
+ * @param[in] NAME expected name
+ * @param[in] NODETYPE LYS_RPC, LYS_ACTION
+ * @param[in] OUTPUT_DATA 0 pointer is null, 1 pointer is not null
+ * @param[in] OUTPUT_MUST expected [sized array](@ref sizedarrays) size of output list of must restrictions
+ * @param[in] OUTPUT_EXTS expected [sized array](@ref sizedarrays) size of the output extension instances of input
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null
+ * @param[in] REF expected reference
+ * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements
+ */
+#define CHECK_LYSC_ACTION(NODE, DSC, EXTS, FLAGS, INPUT_DATA, INPUT_MUST, INPUT_EXTS, MODULE, NAME, NODETYPE, \
+ OUTPUT_DATA, OUTPUT_MUST, OUTPUT_EXTS, PARENT, PRIV, REF, WHEN) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_LYSC_ACTION_INOUT(&(NODE)->input, INPUT_DATA, INPUT_MUST, LYS_INPUT); \
+ CHECK_ARRAY((NODE)->input.exts, INPUT_EXTS); \
+ CHECK_POINTER((NODE)->module, MODULE); \
+ assert_string_equal((NODE)->name, NAME); \
+ assert_int_equal((NODE)->nodetype, NODETYPE); \
+ CHECK_LYSC_ACTION_INOUT(&(NODE)->output, OUTPUT_DATA, OUTPUT_MUST, LYS_OUTPUT); \
+ CHECK_ARRAY((NODE)->output.exts, OUTPUT_EXTS); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ CHECK_POINTER((NODE)->priv, PRIV); \
+ CHECK_STRING((NODE)->ref, REF); \
+ CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN);
+
+/**
+ * @brief assert that lysc_node structure members are correct
+ *
+ * @param[in] NODE pointer to lysc_node variable
+ * @param[in] DSC expected description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] MODULE 0 pointer is null, 1 pointer is not null
+ * @param[in] NAME expected name
+ * @param[in] NEXT 0 pointer is null, 1 pointer is not null
+ * @param[in] NODETYPE type of the node LYS_UNKNOWN, LYS_CONTAINER, LYS_CHOICE, LYS_LEAF,
+ * LYS_LEAFLIST, LYS_LIST, LYS_ANYXML, LYS_ANYDATA, LYS_CASE, LYS_RPC,
+ * LYS_ACTION, LYS_NOTIF, LYS_USES, LYS_INPUT, LYS_OUTPUT, LYS_GROUPING,
+ * LYS_AUGMENT
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null
+ * @param[in] REF expected reference
+ * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements
+ */
+#define CHECK_LYSC_NODE(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, NODETYPE, PARENT, PRIV, REF, WHEN) \
+ assert_non_null(NODE); \
+ CHECK_STRING((NODE)->dsc, DSC); \
+ CHECK_ARRAY((NODE)->exts, EXTS); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_POINTER((NODE)->module, MODULE); \
+ assert_string_equal((NODE)->name, NAME); \
+ CHECK_POINTER((NODE)->next, NEXT); \
+ assert_int_equal((NODE)->nodetype, NODETYPE); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ assert_non_null((NODE)->prev); \
+ CHECK_POINTER((NODE)->priv, PRIV); \
+ CHECK_STRING((NODE)->ref, REF); \
+ CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN);
+
+/**
+ * @brief assert that lysc_node_leaf structure members are correct
+ *
+ * @param[in] NODE pointer to lysc_node variable
+ * @param[in] DSC expected description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] MODULE 0 pointer is null, 1 pointer is not null
+ * @param[in] NAME expected name
+ * @param[in] NEXT 0 pointer is null, 1 pointer is not null
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null
+ * @param[in] REF expected reference
+ * @param[in] ACTIONS 1 if is set pointer to structure lysc_node_action other 0
+ * @param[in] CHILD 1 if is set pointer to child other 0
+ * @param[in] MAX possible maximum elements in list
+ * @param[in] MIN possible minimum elements in list
+ * @param[in] MUSTS [sized array](@ref sizedarrays) number of must node elements in array
+ * @param[in] NOTIFS 1 if is set pointer to any notifs node
+ * @param[in] UNIQUES [sized array](@ref sizedarrays) number of unique nodes element in array
+ * @param[in] WHEN [sized array](@ref sizedarrays) size of when node array
+ */
+#define CHECK_LYSC_NODE_LIST(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, \
+ PARENT, PRIV, REF, ACTIONS, CHILD, MAX, MIN, MUSTS, NOTIFS, UNIQUES, WHEN) \
+ CHECK_LYSC_NODE(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, LYS_LIST, PARENT, PRIV, REF, WHEN); \
+ CHECK_POINTER((NODE)->actions, ACTIONS); \
+ CHECK_POINTER((NODE)->child, CHILD); \
+ assert_int_equal((NODE)->max, MAX); \
+ assert_int_equal((NODE)->min, MIN); \
+ CHECK_ARRAY((NODE)->musts, MUSTS); \
+ CHECK_POINTER((NODE)->notifs, NOTIFS); \
+ CHECK_ARRAY((NODE)->uniques, UNIQUES); \
+ CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN)
+
+/**
+ * @brief assert that lysc_node_leaf structure members are correct
+ *
+ * @param[in] NODE pointer to lysc_node variable
+ * @param[in] DSC expected description
+ * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances
+ * @param[in] FLAGS [schema node flags](@ref snodeflags)
+ * @param[in] MODULE 0 pointer is null, 1 pointer is not null
+ * @param[in] NAME expected name
+ * @param[in] NEXT 0 pointer is null, 1 pointer is not null
+ * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root
+ * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null
+ * @param[in] REF expected reference
+ * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements
+ * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of list of must restriction
+ * @param[in] UNITS expected string reprezenting units
+ * @param[in] DFLT 0-> node dosn't have default value. 1 -> node have default value
+ */
+#define CHECK_LYSC_NODE_LEAF(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, \
+ PARENT, PRIV, REF, WHEN, MUSTS, UNITS, DFLT) \
+ CHECK_LYSC_NODE(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, LYS_LEAF, PARENT, PRIV, REF, WHEN); \
+ CHECK_ARRAY((NODE)->musts, MUSTS); \
+ assert_non_null((NODE)->type); \
+ CHECK_STRING((NODE)->units, UNITS); \
+ CHECK_POINTER((NODE)->dflt, DFLT);
+
+/**
+ * @brief assert that lyd_meta structure members are correct
+ *
+ * @param[in] NODE pointer to lyd_meta variable
+ * @param[in] ANNOTATION 0 pointer is null, 1 pointer is not null
+ * @param[in] NAME expected name
+ * @param[in] NEXT 0 pointer is null, 1 pointer is not null
+ * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT
+ * part of text reprezenting LY_DATA_TYPE.
+ * @param[in] ... ::CHECK_LYD_VALUE
+ */
+#define CHECK_LYD_META(NODE, ANNOTATION, NAME, NEXT, PARENT, TYPE_VAL, ...) \
+ assert_non_null(NODE); \
+ CHECK_POINTER((NODE)->annotation, ANNOTATION); \
+ assert_string_equal((NODE)->name, NAME); \
+ CHECK_POINTER((NODE)->next, NEXT); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ CHECK_LYD_VALUE((NODE)->value, TYPE_VAL, __VA_ARGS__);
+
+/**
+ * @brief assert that lyd_node_term structure members are correct
+ *
+ * @param[in] NODE pointer to lyd_node_term variable
+ * @param[in] FLAGS expected [data node flags](@ref dnodeflags)
+ * @param[in] META 0 -> meta is not prezent, 1 -> meta is prezent
+ * @param[in] NEXT 0 -> next node is not prezent, 1 -> next node is prezent
+ * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT
+ * part of text reprezenting LY_DATA_TYPE.
+ * @param[in] ... ::CHECK_LYD_VALUE
+ */
+#define CHECK_LYD_NODE_TERM(NODE, FLAGS, META, NEXT, PARENT, SCHEMA, VALUE_TYPE, ...) \
+ assert_non_null(NODE); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_POINTER((NODE)->meta, META); \
+ CHECK_POINTER((NODE)->next, NEXT); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ assert_non_null((NODE)->prev); \
+ CHECK_POINTER((NODE)->schema, SCHEMA); \
+ CHECK_LYD_VALUE((NODE)->value, VALUE_TYPE, __VA_ARGS__);
+
+/**
+ * @brief assert that lyd_node_any structure members are correct
+ *
+ * @param[in] NODE pointer to lyd_node_term variable
+ * @param[in] FLAGS expected [data node flags](@ref dnodeflags)
+ * @param[in] META 0 meta isnt present , 1 meta is present
+ * @param[in] PARENT 0 it is root node , 1 node have parent
+ * @param[in] VALUE_TYPE value type ::lyd_node_any.value
+ */
+#define CHECK_LYD_NODE_ANY(NODE, FLAGS, META, PARENT, VALUE_TYPE) \
+ assert_non_null(NODE); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_POINTER((NODE)->meta, META); \
+ CHECK_POINTER((NODE)->meta, PARENT); \
+ assert_non_null((NODE)->prev); \
+ assert_non_null((NODE)->schema); \
+ assert_int_equal((NODE)->value_type, VALUE_TYPE);
+
+/**
+ * @brief assert that lyd_node_opaq structure members are correct
+ *
+ * @param[in] NODE pointer to lyd_node_opaq variable
+ * @param[in] ATTR 0 if pointer is null ,1 if pointer is not null
+ * @param[in] CHILD 0 if pointer is null ,1 if pointer is not null
+ * @param[in] FORMAT LY_PREF_XML or LY_PREF_JSON
+ * @param[in] VAL_PREFS 0 if pointer is null ,1 if pointer is not null
+ * @param[in] NAME expected name
+ * @param[in] value expected orignal value
+ */
+#define CHECK_LYD_NODE_OPAQ(NODE, ATTR, CHILD, FORMAT, NAME, NEXT, PARENT, PREFIX, VAL_PREFS, VALUE) \
+ assert_non_null(NODE); \
+ CHECK_POINTER((NODE)->attr, ATTR); \
+ CHECK_POINTER((NODE)->child, CHILD); \
+ assert_ptr_equal((NODE)->ctx, UTEST_LYCTX); \
+ assert_int_equal((NODE)->flags, 0); \
+ assert_true((NODE)->format == FORMAT); \
+ assert_int_equal((NODE)->hash, 0); \
+ assert_string_equal((NODE)->name.name, NAME); \
+ assert_non_null((NODE)->prev); \
+ assert_null((NODE)->schema); \
+ CHECK_POINTER((NODE)->val_prefix_data, VAL_PREFS); \
+ assert_string_equal((NODE)->value, VALUE);
+
+/**
+ * @brief assert that lyd_node_opaq structure members are correct
+ *
+ * @param[in] NODE pointer to lyd_node_opaq variable
+ * @param[in] CHILD 1 if node has children other 0
+ * @param[in] HILD_HT 1 if node has children hash table other 0
+ * @param[in] META 1 if node has metadata other 0
+ * @param[in] FLAGS expected flag
+ * @param[in] NEXT 1 if next node is present other 0
+ * @param[in] PARENT 1 if node has parent other 0
+ * @param[in] PRIV 1 if node has private data other 0
+ * @param[in] SCHEMA 1 if node has schema other 0
+*/
+#define CHECK_LYD_NODE_INNER(NODE, CHILD, CHILD_HT, META, FLAGS, NEXT, PARENT, PRIV, SCHEMA) \
+ assert_non_null(NODE); \
+ CHECK_POINTER((NODE)->child, CHILD); \
+ CHECK_POINTER((NODE)->children_ht, CHILD_HT); \
+ CHECK_POINTER((NODE)->meta, META); \
+ assert_int_equal((NODE)->flags, FLAGS); \
+ CHECK_POINTER((NODE)->parent, PARENT); \
+ assert_non_null((NODE)->prev); \
+ CHECK_POINTER((NODE)->next, NEXT); \
+ CHECK_POINTER((NODE)->priv, PRIV); \
+ CHECK_POINTER((NODE)->schema, SCHEMA)
+
+/**
+ * @brief assert that lyd_value structure members are correct
+ *
+ * @param[in] NODE lyd_value
+ * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT
+ * part of text reprezenting LY_DATA_TYPE.
+ * @param[in] ... Unspecified parameters. Type and numbers of parameters are specified
+ * by type of value. These parameters are passed to macro
+ * CHECK_LYD_VALUE_ ## TYPE_VAL.
+ */
+#define CHECK_LYD_VALUE(NODE, TYPE_VAL, ...) \
+ CHECK_LYD_VALUE_ ## TYPE_VAL (NODE, __VA_ARGS__);
+
+/*
+ * LYD VALUES CHECKING SPECIALIZATION
+ */
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type EMPTY
+ * Example CHECK_LYD_VALUE(node->value, EMPTY, "");
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ */
+#define CHECK_LYD_VALUE_EMPTY(NODE, CANNONICAL_VAL) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal((NODE).realtype->basetype, LY_TYPE_EMPTY);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type UNION
+ * Example CHECK_LYD_VALUE(node->value, UNION, "12", INT8, "12", 12);
+ * @warning type of subvalue cannot be UNION. Example of calling
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT
+ * @param[in] ... Unspecified parameters. Type and numbers of parameters are specified
+ * by type of value. These parameters are passed to macro
+ * CHECK_LYD_VALUE_ ## TYPE_VAL.
+ */
+#define CHECK_LYD_VALUE_UNION(NODE, CANNONICAL_VAL, TYPE_VAL, ...) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_UNION, (NODE).realtype->basetype); \
+ assert_non_null((NODE).subvalue); \
+ assert_non_null((NODE).subvalue->prefix_data); \
+ CHECK_LYD_VALUE_ ## TYPE_VAL ((NODE).subvalue->value, __VA_ARGS__)
+
+/**
+ * @brief Internal macro. Get 1st variadic argument.
+ */
+#define _GETARG1(ARG1, ...) ARG1
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BITS
+ * Example arr[] = {"a", "b"}; CHECK_LYD_VALUE(node->value, BITS, "a b", arr);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected array of bits names
+ */
+#define CHECK_LYD_VALUE_BITS(NODE, ...) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, _GETARG1(__VA_ARGS__, DUMMY)); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_BITS, (NODE).realtype->basetype); \
+ { \
+ const char *arr[] = { __VA_ARGS__ }; \
+ LY_ARRAY_COUNT_TYPE arr_size = (sizeof(arr) / sizeof(arr[0])) - 1; \
+ struct lyd_value_bits *_val; \
+ LYD_VALUE_GET(&(NODE), _val); \
+ assert_int_equal(arr_size, LY_ARRAY_COUNT(_val->items)); \
+ for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \
+ assert_string_equal(arr[it + 1], _val->items[it]->name); \
+ } \
+ }
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type INST
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected array of enum ly_path_pred_type
+ * @brief Example enum arr[] = {0x0, 0x1}; CHECK_LYD_VALUE(node->value, INST, "test/d", arr);
+ */
+#define CHECK_LYD_VALUE_INST(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_INST, (NODE).realtype->basetype); \
+ { \
+ LY_ARRAY_COUNT_TYPE arr_size = sizeof(VALUE) / sizeof(VALUE[0]); \
+ assert_int_equal(arr_size, LY_ARRAY_COUNT((NODE).target)); \
+ for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \
+ assert_int_equal(VALUE[it], (NODE).target[it].pred_type); \
+ } \
+ }
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type ENUM.
+ * Example CHECK_LYD_VALUE(node->value, ENUM, "item_name", "item_name");
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected enum item name
+ */
+#define CHECK_LYD_VALUE_ENUM(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_ENUM, (NODE).realtype->basetype); \
+ assert_string_equal(VALUE, (NODE).enum_item->name);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type INT8
+ * Example CHECK_LYD_VALUE(node->value, INT8, "12", 12);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected inteager value (-128 to 127).
+ */
+#define CHECK_LYD_VALUE_INT8(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_INT8, (NODE).realtype->basetype); \
+ assert_int_equal(VALUE, (NODE).int8);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type INT16
+ * Example CHECK_LYD_VALUE(node->value, INT8, "12", 12);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected inteager value.
+ */
+#define CHECK_LYD_VALUE_INT16(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_INT16, (NODE).realtype->basetype); \
+ assert_int_equal(VALUE, (NODE).int16);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type UINT8.
+ * Example CHECK_LYD_VALUE(node->value, UINT8, "12", 12);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected inteager (0 to 255).
+ */
+#define CHECK_LYD_VALUE_UINT8(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_UINT8, (NODE).realtype->basetype); \
+ assert_int_equal(VALUE, (NODE).uint8);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type UINT32.
+ * Example CHECK_LYD_VALUE(node->value, UINT32, "12", 12);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected inteager (0 to MAX_UINT32).
+ */
+#define CHECK_LYD_VALUE_UINT32(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_UINT32, (NODE).realtype->basetype); \
+ assert_int_equal(VALUE, (NODE).uint32);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type STRING.
+ * Example CHECK_LYD_VALUE(node->value, STRING, "text");
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ */
+#define CHECK_LYD_VALUE_STRING(NODE, CANNONICAL_VAL) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_STRING, (NODE).realtype->basetype);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type LEAFREF
+ * Example CHECK_LYD_VALUE(node->value, LEAFREF, "");
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ */
+#define CHECK_LYD_VALUE_LEAFREF(NODE, CANNONICAL_VAL) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_LEAFREF, (NODE).realtype->basetype); \
+ assert_non_null((NODE).ptr)
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type DEC64
+ * Example CHECK_LYD_VALUE(node->value, DEC64, "125", 125);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected value 64bit inteager
+*/
+#define CHECK_LYD_VALUE_DEC64(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_DEC64, (NODE).realtype->basetype); \
+ assert_int_equal(VALUE, (NODE).dec64);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BINARY.
+ * Example CHECK_LYD_VALUE(node->value, BINARY, "aGVs\nbG8=");
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected value data
+ * @param[in] SIZE expected value data size
+*/
+#define CHECK_LYD_VALUE_BINARY(NODE, CANNONICAL_VAL, VALUE, SIZE) \
+ { \
+ struct lyd_value_binary *_val; \
+ LYD_VALUE_GET(&(NODE), _val); \
+ assert_int_equal(_val->size, SIZE); \
+ assert_int_equal(0, memcmp(_val->data, VALUE, SIZE)); \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_BINARY, (NODE).realtype->basetype); \
+ }
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BOOL.
+ * Example CHECK_LYD_VALUE(node->value, BOOL, "true", 1);
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected boolean value 0,1
+*/
+#define CHECK_LYD_VALUE_BOOL(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_BOOL, (NODE).realtype->basetype); \
+ assert_int_equal(VALUE, (NODE).boolean);
+
+/**
+ * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type IDENT.
+ * Example CHECK_LYD_VALUE(node->value, IDENT, "types:gigabit-ethernet", "gigabit-ethernet");
+ *
+ * @param[in] NODE lyd_value variable
+ * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected ident name
+*/
+#define CHECK_LYD_VALUE_IDENT(NODE, CANNONICAL_VAL, VALUE) \
+ assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
+ assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
+ assert_non_null((NODE).realtype); \
+ assert_int_equal(LY_TYPE_IDENT, (NODE).realtype->basetype); \
+ assert_string_equal(VALUE, (NODE).ident->name);
+
+/**
+ * @brief Macro testing parser when parsing incorrect module;
+ *
+ * @param[in] DATA String storing the schema module representation.
+ * @param[in] FORMAT Schema format of the @p DATA
+ * @param[in] FEATURES Array of module's features to enable
+ * @param[in] RET_VAL ly_in_new_memory return error value
+ */
+#define UTEST_INVALID_MODULE(DATA, FORMAT, FEATURES, RET_VAL) \
+ { \
+ struct lys_module *mod; \
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(DATA, &_UC->in)); \
+ assert_int_equal(RET_VAL, lys_parse(_UC->ctx, _UC->in, FORMAT, FEATURES, &mod)); \
+ assert_null(mod); \
+ } \
+ ly_in_free(_UC->in, 0); \
+ _UC->in = NULL; \
+
+/**
+ * @brief Add module (from a string) into the used libyang context.
+ *
+ * @param[in] DATA String storing the schema module representation.
+ * @param[in] FORMAT Schema format of the @p DATA
+ * @param[in] FEATURES Array of module's features to enable
+ * @param[out] MOD Optional parameter as a pointer to variable to store the resulting module.
+ */
+#define UTEST_ADD_MODULE(DATA, FORMAT, FEATURES, MOD) \
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(DATA, &_UC->in)); \
+ { \
+ LY_ERR __r = lys_parse(_UC->ctx, _UC->in, FORMAT, FEATURES, MOD); \
+ if (__r != LY_SUCCESS) { \
+ print_message("[ MSG ] Module parsing failed:\n"); \
+ for (struct ly_err_item *e = ly_err_first(_UC->ctx); e; e = e->next) { \
+ print_message("[ MSG ] \t%s Path %s\n", e->msg, e->path); \
+ } \
+ fail(); \
+ } \
+ } \
+ ly_in_free(_UC->in, 0); \
+ _UC->in = NULL
+
+/**
+ * @brief Internal macro to compare error info record with the expected error message and path.
+ * If NULL is provided as MSG, no error info record (NULL) is expected.
+ *
+ * @param[in] ERR Error information record from libyang context.
+ * @param[in] MSG Expected error message.
+ * @param[in] PATH Expected error path.
+ */
+#define _CHECK_LOG_CTX(ERR, MSG, PATH) \
+ if (!MSG) { \
+ assert_null(ERR); \
+ } else { \
+ assert_non_null(ERR); \
+ CHECK_STRING((ERR)->msg, MSG); \
+ CHECK_STRING((ERR)->path, PATH); \
+ }
+
+/**`
+ * @brief Internal macro to check the last libyang's context error.
+ */
+#define _CHECK_LOG_CTX1(MSG, PATH) \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx), MSG, PATH)
+
+/**
+ * @brief Internal macro to check the last two libyang's context error.
+ */
+#define _CHECK_LOG_CTX2(MSG1, PATH1, MSG2, PATH2) \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx), MSG1, PATH1); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev, MSG2, PATH2)
+
+/**
+ * @brief Internal macro to check the last three libyang's context error.
+ */
+#define _CHECK_LOG_CTX3(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3) \
+ _CHECK_LOG_CTX2(MSG1, PATH1, MSG2, PATH2); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev, MSG3, PATH3)
+
+/**
+ * @brief Internal macro to check the last three libyang's context error.
+ */
+#define _CHECK_LOG_CTX4(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4) \
+ _CHECK_LOG_CTX3(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev, MSG4, PATH4)
+
+/**
+ * @brief Internal macro to check the last three libyang's context error.
+ */
+#define _CHECK_LOG_CTX5(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5) \
+ _CHECK_LOG_CTX4(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev, MSG5, PATH5)
+
+/**
+ * @brief Internal macro to check the last three libyang's context error.
+ */
+#define _CHECK_LOG_CTX6(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6) \
+ _CHECK_LOG_CTX5(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev->prev, MSG6, PATH6)
+
+/**
+ * @brief Internal macro to check the last three libyang's context error.
+ */
+#define _CHECK_LOG_CTX7(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6, MSG7, PATH7) \
+ _CHECK_LOG_CTX6(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev->prev->prev, MSG7, PATH7)
+
+/**
+ * @brief Internal macro to check the last three libyang's context error.
+ */
+#define _CHECK_LOG_CTX8(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6, MSG7, PATH7, MSG8, PATH8) \
+ _CHECK_LOG_CTX7(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6, MSG7, PATH7); \
+ _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev->prev->prev->prev, MSG8, PATH8)
+
+/**
+ * @brief Internal helper macro to select _CHECK_LOG_CTX* macro according to the provided parameters.
+ */
+#define _GET_CHECK_LOG_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, NAME, ...) NAME
+
+/**
+ * @brief Check expected error(s) in libyang context.
+ *
+ * Macro has variadic parameters expected to be provided in pairs of error message and error path starting
+ * from the latest error. Current limit is checking at most 3 last errors. After checking, macro cleans up
+ * all the errors from the libyang context.
+ *
+ * @param[in] MSG Expected error message.
+ * @param[in] PATH Expected error path.
+ */
+#define CHECK_LOG_CTX(...) \
+ _GET_CHECK_LOG_MACRO(__VA_ARGS__, _CHECK_LOG_CTX8, _INVAL, _CHECK_LOG_CTX7, _INVAL, \
+ _CHECK_LOG_CTX6, _INVAL, _CHECK_LOG_CTX5, _INVAL, _CHECK_LOG_CTX4, _INVAL, \
+ _CHECK_LOG_CTX3, _INVAL, _CHECK_LOG_CTX2, _INVAL, _CHECK_LOG_CTX1, DUMMY)(__VA_ARGS__); \
+ ly_err_clean(_UC->ctx, NULL)
+
+/**
+ * @brief Check expected error in libyang context including error-app-tag.
+ *
+ * @param[in] MSG Expected error message.
+ * @param[in] PATH Expected error path.
+ * @param[in] APPTAG Expected error-app-tag.
+ */
+#define CHECK_LOG_CTX_APPTAG(MSG, PATH, APPTAG) \
+ if (!MSG) { \
+ assert_null(ly_err_last(_UC->ctx)); \
+ } else { \
+ assert_non_null(ly_err_last(_UC->ctx)); \
+ CHECK_STRING(ly_err_last(_UC->ctx)->msg, MSG); \
+ CHECK_STRING(ly_err_last(_UC->ctx)->path, PATH); \
+ CHECK_STRING(ly_err_last(_UC->ctx)->apptag, APPTAG); \
+ } \
+ ly_err_clean(_UC->ctx, NULL)
+
+/**
+ * @brief Clean up the logging callback's storage.
+ */
+#define UTEST_LOG_CLEAN \
+ free(_UC->err_msg); \
+ free(_UC->err_path); \
+ _UC->err_msg = NULL; \
+ _UC->err_path = NULL;
+
+/**
+ * @brief Check expected error directly logged via logging callback.
+ * Useful mainly for messages logged by functions without access to libyang context.
+ * @param[in] MSG Expected error message.
+ * @param[in] PATH Expected error path.
+ */
+#define CHECK_LOG(MSG, PATH) \
+ CHECK_STRING(_UC->err_msg, MSG); \
+ CHECK_STRING(_UC->err_path, PATH); \
+ UTEST_LOG_CLEAN
+
+#ifdef _UTEST_MAIN_
+/*
+ * Functions inlined into each C source file including this header with _UTEST_MAIN_ defined
+ */
+
+/**
+ * @brief Global variable holding the tests context to simplify access to it.
+ */
+struct utest_context *current_utest_context;
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+/**
+ * @brief Logging callback for libyang.
+ */
+static void
+_utest_logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+ (void) level; /* unused */
+
+ if (ENABLE_LOGGER_CHECKING == 0) {
+ printf("\tERROR:\n\t\tMESSAGE: %s\n\t\tPATH: %s\n\t\tLEVEL: %i\n", msg, path, level);
+ } else {
+ free(current_utest_context->err_msg);
+ current_utest_context->err_msg = msg ? strdup(msg) : NULL;
+ free(current_utest_context->err_path);
+ current_utest_context->err_path = path ? strdup(path) : NULL;
+ }
+}
+
+/**
+ * @brief Generic utest's setup
+ */
+static int
+utest_setup(void **state)
+{
+ char *cur_tz;
+
+ /* setup the logger */
+ ly_set_log_clb(_utest_logger, 1);
+ ly_log_options(LY_LOLOG | LY_LOSTORE);
+
+ current_utest_context = calloc(1, sizeof *current_utest_context);
+ assert_non_null(current_utest_context);
+ *state = current_utest_context;
+
+ /* libyang context */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &current_utest_context->ctx));
+
+ /* clean all errors from the setup - usually warnings regarding the plugins directories */
+ UTEST_LOG_CLEAN;
+
+ /* backup timezone, if any */
+ cur_tz = getenv("TZ");
+ if (cur_tz) {
+ current_utest_context->orig_tz = strdup(cur_tz);
+ }
+
+ /* set CET */
+ setenv("TZ", "CET+02:00", 1);
+
+ return 0;
+}
+
+/**
+ * @brief macro to include generic utest's setup into the test-specific setup.
+ *
+ * Place at the beginning of the test-specific setup
+ */
+#define UTEST_SETUP \
+ assert_int_equal(0, utest_setup(state))
+
+/**
+ * @brief Generic utest's teardown
+ */
+static int
+utest_teardown(void **state)
+{
+ *state = NULL;
+
+ /* libyang context */
+ ly_ctx_destroy(current_utest_context->ctx);
+
+ if (current_utest_context->orig_tz) {
+ /* restore TZ */
+ setenv("TZ", current_utest_context->orig_tz, 1);
+ }
+
+ /* utest context */
+ ly_in_free(current_utest_context->in, 0);
+ free(current_utest_context->err_msg);
+ free(current_utest_context->err_path);
+ free(current_utest_context->orig_tz);
+ free(current_utest_context);
+ current_utest_context = NULL;
+
+ return 0;
+}
+
+/**
+ * @brief macro to include generic utest's teardown into the test-specific teardown.
+ *
+ * Place at the end of the test-specific teardown
+ */
+#define UTEST_TEARDOWN \
+ assert_int_equal(0, utest_teardown(state))
+
+/**
+ * @brief Internal macro for utest setup with test-specific setup and teardown
+ */
+#define _UTEST_SETUP_TEARDOWN(FUNC, SETUP, TEARDOWN) \
+ cmocka_unit_test_setup_teardown(FUNC, SETUP, TEARDOWN)
+
+/**
+ * @brief Internal macro for utest setup with test-specific setup and generic teardown
+ */
+#define _UTEST_SETUP(FUNC, SETUP) \
+ cmocka_unit_test_setup_teardown(FUNC, SETUP, utest_teardown)
+
+/**
+ * @brief Internal macro for utest setup with generic setup and teardown
+ */
+#define _UTEST(FUNC) \
+ cmocka_unit_test_setup_teardown(FUNC, utest_setup, utest_teardown)
+
+/**
+ * @brief Internal helper macro to select _UTEST* macro according to the provided parameters.
+ */
+#define _GET_UTEST_MACRO(_1, _2, _3, NAME, ...) NAME
+
+/**
+ * @brief Macro to specify test function using utest environment. Macro has variadic parameters
+ * to provide test-specific setup/teardown functions:
+ *
+ * UTEST(test_func) - only implicit setup and teardown functions are used
+ * UTEST(test_func, setup) - implicit teardown but own setup
+ * UTEST(test_func, setup, teardown) - both setup and teardown are test-specific
+ */
+#define UTEST(...) \
+ _GET_UTEST_MACRO(__VA_ARGS__, _UTEST_SETUP_TEARDOWN, _UTEST_SETUP, _UTEST, DUMMY)(__VA_ARGS__)
+
+#else /* _UTEST_MAIN_ */
+
+extern struct utest_context *current_utest_context;
+
+#endif /* _UTEST_MAIN_ */
+
+#endif /* _UTESTS_H_ */
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..b92f80c
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,15 @@
+# config file for tools
+configure_file(${PROJECT_SOURCE_DIR}/tools/config.h.in ${PROJECT_BINARY_DIR}/tools/config.h @ONLY)
+
+if(WIN32)
+ find_library(GETOPT_LIBRARY NAMES getopt REQUIRED)
+ find_path(GETOPT_INCLUDE_DIR NAMES getopt.h REQUIRED)
+ message(STATUS "Found <getopt.h> at ${GETOPT_INCLUDE_DIR}, library at ${GETOPT_LIBRARY}")
+endif()
+
+add_subdirectory(lint)
+add_subdirectory(re)
+
+set(format_sources
+ ${format_sources}
+ PARENT_SCOPE)
diff --git a/tools/config.h.in b/tools/config.h.in
new file mode 100644
index 0000000..603a42c
--- /dev/null
+++ b/tools/config.h.in
@@ -0,0 +1,20 @@
+/**
+ * @file config.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief various variables detected by cmake
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef YANGLINT_CONFIG_H_
+#define YANGLINT_CONFIG_H_
+
+#define PROJECT_VERSION "@LIBYANG_VERSION@" /**< libyang project version string */
+
+#endif /* YANGLINT_CONFIG_H_ */
diff --git a/tools/lint/CMakeLists.txt b/tools/lint/CMakeLists.txt
new file mode 100644
index 0000000..32cdcbf
--- /dev/null
+++ b/tools/lint/CMakeLists.txt
@@ -0,0 +1,90 @@
+# yanglint
+
+if(WIN32)
+ set(YANGLINT_INTERACTIVE OFF)
+else()
+ set(YANGLINT_INTERACTIVE ON)
+endif()
+
+set(lintsrc
+ main_ni.c
+ cmd.c
+ cmd_add.c
+ cmd_clear.c
+ cmd_data.c
+ cmd_list.c
+ cmd_feature.c
+ cmd_load.c
+ cmd_print.c
+ cmd_searchpath.c
+ common.c
+)
+if(YANGLINT_INTERACTIVE)
+ set(lintsrc ${lintsrc}
+ main.c
+ completion.c
+ configuration.c
+ linenoise/linenoise.c)
+else()
+ set(lintsrc ${lintsrc}
+ main_ni_only.c)
+endif()
+
+set(format_sources
+ ${format_sources}
+ ${CMAKE_CURRENT_SOURCE_DIR}/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/*.h
+ PARENT_SCOPE)
+
+add_executable(yanglint ${lintsrc} ${compatsrc})
+target_link_libraries(yanglint yang)
+install(TARGETS yanglint DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/tools/lint/yanglint.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+target_include_directories(yanglint BEFORE PRIVATE ${PROJECT_BINARY_DIR})
+
+if(WIN32)
+ target_include_directories(yanglint PRIVATE ${GETOPT_INCLUDE_DIR})
+ target_link_libraries(yanglint ${GETOPT_LIBRARY})
+endif()
+
+#
+# tests
+#
+function(add_yanglint_test)
+ cmake_parse_arguments(ADDTEST "" "NAME;VIA;SCRIPT" "" ${ARGN})
+ set(TEST_NAME yanglint_${ADDTEST_NAME})
+
+ if(${ADDTEST_VIA} STREQUAL "bash")
+ set(WRAPPER /usr/bin/env bash)
+ elseif(${ADDTEST_VIA} STREQUAL "expect")
+ set(WRAPPER ${PATH_EXPECT})
+ else()
+ message(FATAL_ERROR "build: unexpected wrapper '${ADDTEST_VIA}'")
+ endif()
+
+ add_test(NAME ${TEST_NAME} COMMAND ${WRAPPER} ${CMAKE_CURRENT_SOURCE_DIR}/tests/${ADDTEST_SCRIPT})
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANGLINT=${PROJECT_BINARY_DIR}/yanglint")
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANG_MODULES_DIR=${PROJECT_SOURCE_DIR}/tests/modules/yang")
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
+endfunction(add_yanglint_test)
+
+if(ENABLE_TESTS)
+ # tests of non-interactive mode using shunit2
+ find_program(PATH_SHUNIT NAMES shunit2)
+ if(NOT PATH_SHUNIT)
+ message(WARNING "'shunit2' not found! The yanglint(1) non-interactive tests will not be available.")
+ else()
+ add_yanglint_test(NAME ni_list VIA bash SCRIPT shunit2/list.sh)
+ add_yanglint_test(NAME ni_feature VIA bash SCRIPT shunit2/feature.sh)
+ endif()
+
+ # tests of interactive mode using expect
+ find_program(PATH_EXPECT NAMES expect)
+ if(NOT PATH_EXPECT)
+ message(WARNING "'expect' not found! The yanglint(1) interactive tests will not be available.")
+ elseif(YANGLINT_INTERACTIVE)
+ add_yanglint_test(NAME in_list VIA expect SCRIPT expect/list.exp)
+ add_yanglint_test(NAME in_feature VIA expect SCRIPT expect/feature.exp)
+ add_yanglint_test(NAME in_completion VIA expect SCRIPT expect/completion.exp)
+ endif()
+endif()
diff --git a/tools/lint/cmd.c b/tools/lint/cmd.c
new file mode 100644
index 0000000..10e7446
--- /dev/null
+++ b/tools/lint/cmd.c
@@ -0,0 +1,259 @@
+/**
+ * @file cmd.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool general commands
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "common.h"
+#include "compat.h"
+#include "libyang.h"
+
+COMMAND commands[];
+extern int done;
+
+#ifndef NDEBUG
+
+void
+cmd_debug_help(void)
+{
+ printf("Usage: debug (dict | xpath)+\n");
+}
+
+void
+cmd_debug(struct ly_ctx **UNUSED(ctx), const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ uint32_t dbg_groups = 0;
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ cmd_debug_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+ if (argc == optind) {
+ /* no argument */
+ cmd_debug_help();
+ goto cleanup;
+ }
+
+ for (int i = 0; i < argc - optind; i++) {
+ if (!strcasecmp("dict", argv[optind + i])) {
+ dbg_groups |= LY_LDGDICT;
+ } else if (!strcasecmp("xpath", argv[optind + i])) {
+ dbg_groups |= LY_LDGXPATH;
+ } else {
+ YLMSG_E("Unknown debug group \"%s\"\n", argv[optind + 1]);
+ goto cleanup;
+ }
+ }
+
+ ly_log_dbg_groups(dbg_groups);
+
+cleanup:
+ free_cmdline(argv);
+}
+
+#endif
+
+void
+cmd_verb_help(void)
+{
+ printf("Usage: verb (error | warning | verbose | debug)\n");
+}
+
+void
+cmd_verb(struct ly_ctx **UNUSED(ctx), const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ cmd_verb_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (argc - optind > 1) {
+ YLMSG_E("Only a single verbosity level can be set.\n");
+ cmd_verb_help();
+ goto cleanup;
+ } else if (argc == optind) {
+ /* no argument - print current value */
+ LY_LOG_LEVEL level = ly_log_level(LY_LLERR);
+
+ ly_log_level(level);
+ printf("Current verbosity level: ");
+ if (level == LY_LLERR) {
+ printf("error\n");
+ } else if (level == LY_LLWRN) {
+ printf("warning\n");
+ } else if (level == LY_LLVRB) {
+ printf("verbose\n");
+ } else if (level == LY_LLDBG) {
+ printf("debug\n");
+ }
+ goto cleanup;
+ }
+
+ if (!strcasecmp("error", argv[optind]) || !strcmp("0", argv[optind])) {
+ ly_log_level(LY_LLERR);
+ } else if (!strcasecmp("warning", argv[optind]) || !strcmp("1", argv[optind])) {
+ ly_log_level(LY_LLWRN);
+ } else if (!strcasecmp("verbose", argv[optind]) || !strcmp("2", argv[optind])) {
+ ly_log_level(LY_LLVRB);
+ } else if (!strcasecmp("debug", argv[optind]) || !strcmp("3", argv[optind])) {
+ ly_log_level(LY_LLDBG);
+ } else {
+ YLMSG_E("Unknown verbosity \"%s\"\n", argv[optind]);
+ goto cleanup;
+ }
+
+cleanup:
+ free_cmdline(argv);
+}
+
+void
+cmd_quit(struct ly_ctx **UNUSED(ctx), const char *UNUSED(cmdline))
+{
+ done = 1;
+ return;
+}
+
+void
+cmd_help_help(void)
+{
+ printf("Usage: help [cmd ...]\n");
+}
+
+void
+cmd_help(struct ly_ctx **UNUSED(ctx), const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ cmd_help_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (argc == optind) {
+generic_help:
+ printf("Available commands:\n");
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (commands[i].helpstring != NULL) {
+ printf(" %-15s %s\n", commands[i].name, commands[i].helpstring);
+ }
+ }
+ } else {
+ /* print specific help for the selected command(s) */
+
+ for (int c = 0; c < argc - optind; ++c) {
+ int8_t match = 0;
+
+ /* get the command of the specified name */
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (strcmp(argv[optind + c], commands[i].name) == 0) {
+ match = 1;
+ if (commands[i].help_func != NULL) {
+ commands[i].help_func();
+ } else {
+ printf("%s: %s\n", argv[optind + c], commands[i].helpstring);
+ }
+ break;
+ }
+ }
+ if (!match) {
+ /* if unknown command specified, print the list of commands */
+ printf("Unknown command \'%s\'\n", argv[optind + c]);
+ goto generic_help;
+ }
+ }
+ }
+
+cleanup:
+ free_cmdline(argv);
+}
+
+COMMAND commands[] = {
+ {"help", cmd_help, cmd_help_help, "Display commands description"},
+ {"add", cmd_add, cmd_add_help, "Add a new module from a specific file"},
+ {"load", cmd_load, cmd_load_help, "Load a new schema from the searchdirs"},
+ {"print", cmd_print, cmd_print_help, "Print a module"},
+ {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
+ {"list", cmd_list, cmd_list_help, "List all the loaded modules"},
+ {"feature", cmd_feature, cmd_feature_help, "Print all features of module(s) with their state"},
+ {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for schemas"},
+ {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded modules"},
+ {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
+#ifndef NDEBUG
+ {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
+#endif
+ {"quit", cmd_quit, NULL, "Quit the program"},
+ /* synonyms for previous commands */
+ {"?", cmd_help, NULL, "Display commands description"},
+ {"exit", cmd_quit, NULL, "Quit the program"},
+ {NULL, NULL, NULL, NULL}
+};
diff --git a/tools/lint/cmd.h b/tools/lint/cmd.h
new file mode 100644
index 0000000..9f6f88d
--- /dev/null
+++ b/tools/lint/cmd.h
@@ -0,0 +1,69 @@
+/**
+ * @file cmd.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool commands header
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef COMMANDS_H_
+#define COMMANDS_H_
+
+#include "libyang.h"
+
+/**
+ * @brief command information
+ */
+typedef struct {
+ char *name; /* User printable name of the function. */
+
+ void (*func)(struct ly_ctx **ctx, const char *); /* Function to call to do the command. */
+ void (*help_func)(void); /* Display command help. */
+ char *helpstring; /* Documentation for this function. */
+} COMMAND;
+
+/**
+ * @brief The list of available commands.
+ */
+extern COMMAND commands[];
+
+/* cmd_add.c */
+void cmd_add(struct ly_ctx **ctx, const char *cmdline);
+void cmd_add_help(void);
+
+/* cmd_clear.c */
+void cmd_clear(struct ly_ctx **ctx, const char *cmdline);
+void cmd_clear_help(void);
+
+/* cmd_data.c */
+void cmd_data(struct ly_ctx **ctx, const char *cmdline);
+void cmd_data_help(void);
+
+/* cmd_list.c */
+void cmd_list(struct ly_ctx **ctx, const char *cmdline);
+void cmd_list_help(void);
+
+/* cmd_feature.c */
+void cmd_feature(struct ly_ctx **ctx, const char *cmdline);
+void cmd_feature_help(void);
+
+/* cmd_load.c */
+void cmd_load(struct ly_ctx **ctx, const char *cmdline);
+void cmd_load_help(void);
+
+/* cmd_print.c */
+void cmd_print(struct ly_ctx **ctx, const char *cmdline);
+void cmd_print_help(void);
+
+/* cmd_searchpath.c */
+void cmd_searchpath(struct ly_ctx **ctx, const char *cmdline);
+void cmd_searchpath_help(void);
+
+#endif /* COMMANDS_H_ */
diff --git a/tools/lint/cmd_add.c b/tools/lint/cmd_add.c
new file mode 100644
index 0000000..bbfdfd6
--- /dev/null
+++ b/tools/lint/cmd_add.c
@@ -0,0 +1,176 @@
+/**
+ * @file cmd_add.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'add' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_add_help(void)
+{
+ printf("Usage: add [-iD] <schema1> [<schema2> ...]\n"
+ " Add a new module from a specific file.\n\n"
+ " -D, --disable-searchdir\n"
+ " Do not implicitly search in current working directory for\n"
+ " the import schema modules. If specified a second time, do not\n"
+ " even search in the module directory (all modules must be \n"
+ " explicitly specified).\n"
+ " -F FEATURES, --features=FEATURES\n"
+ " Features to support, default all in all implemented modules.\n"
+ " Specify separately for each module.\n"
+ " <modname>:[<feature>,]*\n"
+ " -i, --make-implemented\n"
+ " Make the imported modules \"referenced\" from any loaded\n"
+ " <schema> module also implemented. If specified a second time,\n"
+ " all the modules are set implemented.\n");
+}
+
+void
+cmd_add(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"disable-searchdir", no_argument, NULL, 'D'},
+ {"features", required_argument, NULL, 'F'},
+ {"help", no_argument, NULL, 'h'},
+ {"make-implemented", no_argument, NULL, 'i'},
+ {NULL, 0, NULL, 0}
+ };
+ uint16_t options_ctx = 0;
+ const char *all_features[] = {"*", NULL};
+ struct ly_set fset = {0};
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "D:F:hi", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'D': /* --disable--search */
+ if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
+ YLMSG_W("The -D option specified too many times.\n");
+ }
+ if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
+ options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
+ options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
+ } else {
+ options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ }
+ break;
+
+ case 'F': /* --features */
+ if (parse_features(optarg, &fset)) {
+ goto cleanup;
+ }
+ break;
+
+ case 'h':
+ cmd_add_help();
+ goto cleanup;
+
+ case 'i': /* --make-implemented */
+ if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
+ options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
+ options_ctx |= LY_CTX_ALL_IMPLEMENTED;
+ } else {
+ options_ctx |= LY_CTX_REF_IMPLEMENTED;
+ }
+ break;
+
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (argc == optind) {
+ /* no argument */
+ cmd_add_help();
+ goto cleanup;
+ }
+
+ if (!fset.count) {
+ /* no features, enable all of them */
+ options_ctx |= LY_CTX_ENABLE_IMP_FEATURES;
+ }
+
+ if (options_ctx) {
+ ly_ctx_set_options(*ctx, options_ctx);
+ }
+
+ for (int i = 0; i < argc - optind; i++) {
+ /* process the schema module files */
+ LY_ERR ret;
+ uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
+ char *dir, *module;
+ const char **features = NULL;
+ struct ly_in *in = NULL;
+
+ if (parse_schema_path(argv[optind + i], &dir, &module)) {
+ goto cleanup;
+ }
+
+ /* add temporarily also the path of the module itself */
+ if (ly_ctx_set_searchdir(*ctx, dirname(dir)) == LY_EEXIST) {
+ path_unset = 0;
+ }
+
+ /* get features list for this module */
+ if (!fset.count) {
+ features = all_features;
+ } else {
+ get_features(&fset, module, &features);
+ }
+
+ /* temporary cleanup */
+ free(dir);
+ free(module);
+
+ /* prepare input handler */
+ ret = ly_in_new_filepath(argv[optind + i], 0, &in);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* parse the file */
+ ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, NULL);
+ ly_in_free(in, 1);
+ ly_ctx_unset_searchdir_last(*ctx, path_unset);
+
+ if (ret) {
+ /* libyang printed the error messages */
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (options_ctx) {
+ ly_ctx_unset_options(*ctx, options_ctx);
+ }
+ ly_set_erase(&fset, free_features);
+ free_cmdline(argv);
+}
diff --git a/tools/lint/cmd_clear.c b/tools/lint/cmd_clear.c
new file mode 100644
index 0000000..5eed6ff
--- /dev/null
+++ b/tools/lint/cmd_clear.c
@@ -0,0 +1,99 @@
+/**
+ * @file cmd_clear.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'clear' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_clear_help(void)
+{
+ printf("Usage: clear [-i] [-y]\n"
+ " Replace the current context with an empty one, searchpaths\n"
+ " are not kept.\n\n"
+ " -i, --make-implemented\n"
+ " Make the imported modules \"referenced\" from any loaded\n"
+ " module also implemented. If specified a second time, all the\n"
+ " modules are set implemented.\n"
+ " -y, --yang-library\n"
+ " Load and implement internal \"ietf-yang-library\" YANG module.\n"
+ " Note that this module includes definitions of mandatory state\n"
+ " data that can result in unexpected data validation errors.\n");
+#if 0
+ " If <yang-library-data> path specified, load the modules\n"
+ " according to the provided yang library data.\n"
+#endif
+}
+
+void
+cmd_clear(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"make-implemented", no_argument, NULL, 'i'},
+ {"yang-library", no_argument, NULL, 'y'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ uint16_t options_ctx = LY_CTX_NO_YANGLIBRARY;
+ struct ly_ctx *ctx_new;
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "iyh", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'i':
+ if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
+ options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
+ options_ctx |= LY_CTX_ALL_IMPLEMENTED;
+ } else {
+ options_ctx |= LY_CTX_REF_IMPLEMENTED;
+ }
+ break;
+ case 'y':
+ options_ctx &= ~LY_CTX_NO_YANGLIBRARY;
+ break;
+ case 'h':
+ cmd_clear_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (ly_ctx_new(NULL, options_ctx, &ctx_new)) {
+ YLMSG_W("Failed to create context.\n");
+ goto cleanup;
+ }
+
+ ly_ctx_destroy(*ctx);
+ *ctx = ctx_new;
+
+cleanup:
+ free_cmdline(argv);
+}
diff --git a/tools/lint/cmd_data.c b/tools/lint/cmd_data.c
new file mode 100644
index 0000000..25449f5
--- /dev/null
+++ b/tools/lint/cmd_data.c
@@ -0,0 +1,328 @@
+/**
+ * @file cmd_data.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'data' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_data_help(void)
+{
+ printf("Usage: data [-emn] [-t TYPE]\n"
+ " [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
+ " data [-n] -t (rpc | notif | reply) [-O FILE]\n"
+ " [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
+ " data [-en] [-t TYPE] [-F FORMAT] -x XPATH [-o OUTFILE] <data1> ...\n"
+ " Parse, validate and optionally print data instances\n\n"
+
+ " -t TYPE, --type=TYPE\n"
+ " Specify data tree type in the input data file(s):\n"
+ " data - Complete datastore with status data (default type).\n"
+ " config - Configuration datastore (without status data).\n"
+ " get - Result of the NETCONF <get> operation.\n"
+ " getconfig - Result of the NETCONF <get-config> operation.\n"
+ " edit - Content of the NETCONF <edit-config> operation.\n"
+ " rpc - Content of the NETCONF <rpc> message, defined as YANG's\n"
+ " RPC/Action input statement.\n"
+ " reply - Reply to the RPC/Action. Note that the reply data are\n"
+ " expected inside a container representing the original\n"
+ " RPC/Action. This is necessary to identify appropriate\n"
+ " data definitions in the schema module.\n"
+ " notif - Notification instance (content of the <notification>\n"
+ " element without <eventTime>).\n\n"
+
+ " -e, --present Validate only with the schema modules whose data actually\n"
+ " exist in the provided input data files. Takes effect only\n"
+ " with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
+ " mandatory nodes from modules which data are not present in the\n"
+ " provided input data files.\n"
+ " -m, --merge Merge input data files into a single tree and validate at\n"
+ " once.The option has effect only for 'data' and 'config' TYPEs.\n"
+ " In case of using -x option, the data are always merged.\n"
+ " -n, --not-strict\n"
+ " Do not require strict data parsing (silently skip unknown data),\n"
+ " has no effect for schemas.\n\n"
+ " -O FILE, --operational=FILE\n"
+ " Provide optional data to extend validation of the 'rpc',\n"
+ " 'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
+ " the :running configuration datastore and state data\n"
+ " (operational datastore) referenced from the RPC/Notification.\n\n"
+
+ " -f FORMAT, --format=FORMAT\n"
+ " Print the data in one of the following formats:\n"
+ " xml, json, lyb\n"
+ " Note that the LYB format requires the -o option specified.\n"
+ " -F FORMAT, --in-format=FORMAT\n"
+ " Load the data in one of the following formats:\n"
+ " xml, json, lyb\n"
+ " If input format not specified, it is detected from the file extension.\n"
+ " -d MODE, --default=MODE\n"
+ " Print data with default values, according to the MODE\n"
+ " (to print attributes, ietf-netconf-with-defaults model\n"
+ " must be loaded):\n"
+ " all - Add missing default nodes.\n"
+ " all-tagged - Add missing default nodes and mark all the default\n"
+ " nodes with the attribute.\n"
+ " trim - Remove all nodes with a default value.\n"
+ " implicit-tagged - Add missing nodes and mark them with the attribute.\n"
+ " -o OUTFILE, --output=OUTFILE\n"
+ " Write the output to OUTFILE instead of stdout.\n\n"
+
+ " -x XPATH, --xpath=XPATH\n"
+ " Evaluate XPATH expression and print the nodes satysfying the.\n"
+ " expression. The output format is specific and the option cannot\n"
+ " be combined with the -f and -d options. Also all the data\n"
+ " inputs are merged into a single data tree where the expression\n"
+ " is evaluated, so the -m option is always set implicitly.\n\n");
+
+}
+
+void
+cmd_data(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"defaults", required_argument, NULL, 'd'},
+ {"present", no_argument, NULL, 'e'},
+ {"format", required_argument, NULL, 'f'},
+ {"in-format", required_argument, NULL, 'F'},
+ {"help", no_argument, NULL, 'h'},
+ {"merge", no_argument, NULL, 'm'},
+ {"output", required_argument, NULL, 'o'},
+ {"operational", required_argument, NULL, 'O'},
+ {"not-strict", no_argument, NULL, 'n'},
+ {"type", required_argument, NULL, 't'},
+ {"xpath", required_argument, NULL, 'x'},
+ {NULL, 0, NULL, 0}
+ };
+
+ uint8_t data_merge = 0;
+ uint32_t options_print = 0;
+ uint32_t options_parse = YL_DEFAULT_DATA_PARSE_OPTIONS;
+ uint32_t options_validate = 0;
+ enum lyd_type data_type = 0;
+ uint8_t data_type_set = 0;
+ LYD_FORMAT outformat = LYD_UNKNOWN;
+ LYD_FORMAT informat = LYD_UNKNOWN;
+ struct ly_out *out = NULL;
+ struct cmdline_file *operational = NULL;
+ struct ly_set inputs = {0};
+ struct ly_set xpaths = {0};
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "d:ef:F:hmo:O:r:nt:x:", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'd': /* --default */
+ if (!strcasecmp(optarg, "all")) {
+ options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
+ } else if (!strcasecmp(optarg, "all-tagged")) {
+ options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
+ } else if (!strcasecmp(optarg, "trim")) {
+ options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
+ } else if (!strcasecmp(optarg, "implicit-tagged")) {
+ options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
+ } else {
+ YLMSG_E("Unknown default mode %s\n", optarg);
+ cmd_data_help();
+ goto cleanup;
+ }
+ break;
+ case 'f': /* --format */
+ if (!strcasecmp(optarg, "xml")) {
+ outformat = LYD_XML;
+ } else if (!strcasecmp(optarg, "json")) {
+ outformat = LYD_JSON;
+ } else if (!strcasecmp(optarg, "lyb")) {
+ outformat = LYD_LYB;
+ } else {
+ YLMSG_E("Unknown output format %s\n", optarg);
+ cmd_data_help();
+ goto cleanup;
+ }
+ break;
+ case 'F': /* --in-format */
+ if (!strcasecmp(optarg, "xml")) {
+ informat = LYD_XML;
+ } else if (!strcasecmp(optarg, "json")) {
+ informat = LYD_JSON;
+ } else if (!strcasecmp(optarg, "lyb")) {
+ informat = LYD_LYB;
+ } else {
+ YLMSG_E("Unknown input format %s\n", optarg);
+ cmd_data_help();
+ goto cleanup;
+ }
+ break;
+ case 'o': /* --output */
+ if (out) {
+ YLMSG_E("Only a single output can be specified.\n");
+ goto cleanup;
+ } else {
+ if (ly_out_new_filepath(optarg, &out)) {
+ YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
+ goto cleanup;
+ }
+ }
+ break;
+ case 'O': { /* --operational */
+ struct ly_in *in;
+ LYD_FORMAT f;
+
+ if (operational) {
+ YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
+ goto cleanup;
+ }
+ if (get_input(optarg, NULL, &f, &in)) {
+ goto cleanup;
+ }
+ operational = fill_cmdline_file(NULL, in, optarg, f);
+ break;
+ } /* case 'O' */
+
+ case 'e': /* --present */
+ options_validate |= LYD_VALIDATE_PRESENT;
+ break;
+ case 'm': /* --merge */
+ data_merge = 1;
+ break;
+ case 'n': /* --not-strict */
+ options_parse &= ~LYD_PARSE_STRICT;
+ break;
+ case 't': /* --type */
+ if (data_type_set) {
+ YLMSG_E("The data type (-t) cannot be set multiple times.\n");
+ goto cleanup;
+ }
+
+ if (!strcasecmp(optarg, "config")) {
+ options_parse |= LYD_PARSE_NO_STATE;
+ options_validate |= LYD_VALIDATE_NO_STATE;
+ } else if (!strcasecmp(optarg, "get")) {
+ options_parse |= LYD_PARSE_ONLY;
+ } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config") || !strcasecmp(optarg, "edit")) {
+ options_parse |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+ } else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
+ data_type = LYD_TYPE_RPC_YANG;
+ } else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
+ data_type = LYD_TYPE_REPLY_YANG;
+ } else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
+ data_type = LYD_TYPE_NOTIF_YANG;
+ } else if (!strcasecmp(optarg, "data")) {
+ /* default option */
+ } else {
+ YLMSG_E("Unknown data tree type %s.\n", optarg);
+ cmd_data_help();
+ goto cleanup;
+ }
+
+ data_type_set = 1;
+ break;
+
+ case 'x': /* --xpath */
+ if (ly_set_add(&xpaths, optarg, 0, NULL)) {
+ YLMSG_E("Storing XPath \"%s\" failed.\n", optarg);
+ goto cleanup;
+ }
+ break;
+
+ case 'h': /* --help */
+ cmd_data_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (optind == argc) {
+ YLMSG_E("Missing the data file to process.\n");
+ goto cleanup;
+ }
+
+ if (data_merge) {
+ if (data_type || (options_parse & LYD_PARSE_ONLY)) {
+ /* switch off the option, incompatible input data type */
+ data_merge = 0;
+ } else {
+ /* postpone validation after the merge of all the input data */
+ options_parse |= LYD_PARSE_ONLY;
+ }
+ } else if (xpaths.count) {
+ data_merge = 1;
+ }
+
+ if (xpaths.count && outformat) {
+ YLMSG_E("The --format option cannot be combined with --xpath option.\n");
+ cmd_data_help();
+ goto cleanup;
+ }
+
+ if (operational && !data_type) {
+ YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notifications input data types.\n");
+ free_cmdline_file(operational);
+ operational = NULL;
+ }
+
+ /* process input data files provided as standalone command line arguments */
+ for (int i = 0; i < argc - optind; i++) {
+ struct ly_in *in;
+
+ if (get_input(argv[optind + i], NULL, &informat, &in)) {
+ goto cleanup;
+ }
+
+ if (!fill_cmdline_file(&inputs, in, argv[optind + i], informat)) {
+ ly_in_free(in, 1);
+ goto cleanup;
+ }
+ }
+
+ /* default output stream */
+ if (!out) {
+ if (ly_out_new_file(stdout, &out)) {
+ YLMSG_E("Unable to set stdout as output.\n");
+ goto cleanup;
+ }
+ }
+
+ /* parse, validate and print data */
+ if (process_data(*ctx, data_type, data_merge, outformat, out, options_parse, options_validate, options_print,
+ operational, NULL, &inputs, &xpaths)) {
+ goto cleanup;
+ }
+
+cleanup:
+ ly_out_free(out, NULL, 0);
+ ly_set_erase(&inputs, free_cmdline_file);
+ ly_set_erase(&xpaths, NULL);
+ free_cmdline_file(operational);
+ free_cmdline(argv);
+}
diff --git a/tools/lint/cmd_feature.c b/tools/lint/cmd_feature.c
new file mode 100644
index 0000000..6b332ab
--- /dev/null
+++ b/tools/lint/cmd_feature.c
@@ -0,0 +1,135 @@
+/**
+ * @file cmd_feature.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief 'feature' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_feature_help(void)
+{
+ printf("Usage: feature [-f] <module> [<module>]*\n"
+ " feature -a [-f]\n"
+ " Print features of all the modules with state of each one.\n\n"
+ " -f <module1, module2, ...>, --feature-param <module1, module2, ...>\n"
+ " Generate features parameter for the command \"add\" \n"
+ " in the form of -F <module-name>:<features>\n"
+ " -a, --all \n"
+ " Print features of all implemented modules.\n");
+}
+
+void
+cmd_feature(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ char *features_output = NULL;
+ int opt, opt_index, i;
+ ly_bool generate_features = 0, print_all = 0;
+ struct ly_set set = {0};
+ const struct lys_module *mod;
+ struct ly_out *out = NULL;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"all", no_argument, NULL, 'a'},
+ {"feature-param", no_argument, NULL, 'f'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "haf", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ cmd_feature_help();
+ goto cleanup;
+ case 'a':
+ print_all = 1;
+ break;
+ case 'f':
+ generate_features = 1;
+ break;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (ly_out_new_file(stdout, &out)) {
+ YLMSG_E("Unable to print to the standard output.\n");
+ goto cleanup;
+ }
+
+ if (print_all) {
+ if (print_all_features(out, *ctx, generate_features, &features_output)) {
+ YLMSG_E("Printing all features failed.\n");
+ goto cleanup;
+ }
+ if (generate_features) {
+ printf("%s\n", features_output);
+ }
+ goto cleanup;
+ }
+
+ if (argc == optind) {
+ YLMSG_E("Missing modules to print.\n");
+ goto cleanup;
+ }
+
+ for (i = 0; i < argc - optind; i++) {
+ /* always erase the set, so the previous module's features don't carry over to the next module's features */
+ ly_set_erase(&set, NULL);
+
+ mod = ly_ctx_get_module_latest(*ctx, argv[optind + i]);
+ if (!mod) {
+ YLMSG_E("Module \"%s\" not found.\n", argv[optind + i]);
+ goto cleanup;
+ }
+
+ /* collect features of the module */
+ if (collect_features(mod, &set)) {
+ goto cleanup;
+ }
+
+ if (generate_features) {
+ if (generate_features_output(mod, &set, &features_output)) {
+ goto cleanup;
+ }
+ /* don't print features and their state of each module if generating features parameter */
+ continue;
+ }
+
+ print_features(out, mod, &set);
+ }
+
+ if (generate_features) {
+ printf("%s\n", features_output);
+ }
+
+cleanup:
+ free_cmdline(argv);
+ ly_out_free(out, NULL, 0);
+ ly_set_erase(&set, NULL);
+ free(features_output);
+}
diff --git a/tools/lint/cmd_list.c b/tools/lint/cmd_list.c
new file mode 100644
index 0000000..ec7a021
--- /dev/null
+++ b/tools/lint/cmd_list.c
@@ -0,0 +1,89 @@
+/**
+ * @file cmd_list.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'list' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_list_help(void)
+{
+ printf("Usage: list [-f (xml | json)]\n"
+ " Print the list of modules in the current context\n\n"
+ " -f FORMAT, --format=FORMAT\n"
+ " Print the list as ietf-yang-library data in the specified\n"
+ " data FORMAT. If format not specified, a simple list is\n"
+ " printed with an indication of imported (i) / implemented (I)\n"
+ " modules.\n");
+}
+
+void
+cmd_list(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"format", required_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ LYD_FORMAT format = LYD_UNKNOWN;
+ struct ly_out *out = NULL;
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "f:h", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'f': /* --format */
+ if (!strcasecmp(optarg, "xml")) {
+ format = LYD_XML;
+ } else if (!strcasecmp(optarg, "json")) {
+ format = LYD_JSON;
+ } else {
+ YLMSG_E("Unknown output format %s\n", optarg);
+ cmd_list_help();
+ goto cleanup;
+ }
+ break;
+ case 'h':
+ cmd_list_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (!ly_out_new_file(stdout, &out)) {
+ print_list(out, *ctx, format);
+ ly_out_free(out, NULL, 0);
+ } else {
+ YLMSG_E("Unable to print to the standard output.\n");
+ }
+
+cleanup:
+ free_cmdline(argv);
+}
diff --git a/tools/lint/cmd_load.c b/tools/lint/cmd_load.c
new file mode 100644
index 0000000..f5883e9
--- /dev/null
+++ b/tools/lint/cmd_load.c
@@ -0,0 +1,139 @@
+/**
+ * @file cmd_load.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'load' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_load_help(void)
+{
+ printf("Usage: load [-i] <module-name1>[@<revision>] [<module-name2>[@revision] ...]\n"
+ " Add a new module of the specified name, yanglint will find\n"
+ " them in searchpaths. if the <revision> of the module not\n"
+ " specified, the latest revision available is loaded.\n\n"
+ " -F FEATURES, --features=FEATURES\n"
+ " Features to support, default all in all implemented modules.\n"
+ " <modname>:[<feature>,]*\n"
+ " -i, --make-implemented\n"
+ " Make the imported modules \"referenced\" from any loaded\n"
+ " <schema> module also implemented. If specified a second time,\n"
+ " all the modules are set implemented.\n");
+}
+
+void
+cmd_load(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"features", required_argument, NULL, 'F'},
+ {"help", no_argument, NULL, 'h'},
+ {"make-implemented", no_argument, NULL, 'i'},
+ {NULL, 0, NULL, 0}
+ };
+ uint16_t options_ctx = 0;
+ const char *all_features[] = {"*", NULL};
+ struct ly_set fset = {0};
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "F:hi", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'F': /* --features */
+ if (parse_features(optarg, &fset)) {
+ goto cleanup;
+ }
+ break;
+
+ case 'h':
+ cmd_load_help();
+ goto cleanup;
+
+ case 'i': /* --make-implemented */
+ if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
+ options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
+ options_ctx |= LY_CTX_ALL_IMPLEMENTED;
+ } else {
+ options_ctx |= LY_CTX_REF_IMPLEMENTED;
+ }
+ break;
+
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (argc == optind) {
+ /* no argument */
+ cmd_add_help();
+ goto cleanup;
+ }
+
+ if (!fset.count) {
+ /* no features, enable all of them */
+ options_ctx |= LY_CTX_ENABLE_IMP_FEATURES;
+ }
+
+ if (options_ctx) {
+ ly_ctx_set_options(*ctx, options_ctx);
+ }
+
+ for (int i = 0; i < argc - optind; i++) {
+ /* process the schema module files */
+ char *revision;
+ const char **features = NULL;
+
+ /* get revision */
+ revision = strchr(argv[optind + i], '@');
+ if (revision) {
+ revision[0] = '\0';
+ ++revision;
+ }
+
+ /* get features list for this module */
+ if (!fset.count) {
+ features = all_features;
+ } else {
+ get_features(&fset, argv[optind + i], &features);
+ }
+
+ /* load the module */
+ if (!ly_ctx_load_module(*ctx, argv[optind + i], revision, features)) {
+ /* libyang printed the error messages */
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (options_ctx) {
+ ly_ctx_unset_options(*ctx, options_ctx);
+ }
+ ly_set_erase(&fset, free_features);
+ free_cmdline(argv);
+}
diff --git a/tools/lint/cmd_print.c b/tools/lint/cmd_print.c
new file mode 100644
index 0000000..c1a5359
--- /dev/null
+++ b/tools/lint/cmd_print.c
@@ -0,0 +1,264 @@
+/**
+ * @file cmd_print.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'print' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_print_help(void)
+{
+ printf("Usage: print [-f (yang | yin | tree [-q -P PATH -L LINE_LENGTH ] | info [-q -P PATH])]\n"
+ " [-o OUTFILE] [<module-name1>[@revision]] ...\n"
+ " Print a schema module. The <module-name> is not required\n"
+ " only in case the -P option is specified.\n\n"
+ " -f FORMAT, --format=FORMAT\n"
+ " Print the module in the specified FORMAT. If format not\n"
+ " specified, the 'tree' format is used.\n"
+ " -L LINE_LENGTH, --tree-line-length=LINE_LENGTH\n"
+ " The limit of the maximum line length on which the 'tree'\n"
+ " format will try to be printed.\n"
+ " -P PATH, --schema-node=PATH\n"
+ " Print only the specified subtree of the schema.\n"
+ " The PATH is the XPath subset mentioned in documentation as\n"
+ " the Path format. The option can be combined with --single-node\n"
+ " option to print information only about the specified node.\n"
+ " -q, --single-node\n"
+ " Supplement to the --schema-node option to print information\n"
+ " only about a single node specified as PATH argument.\n"
+ " -o OUTFILE, --output=OUTFILE\n"
+ " Write the output to OUTFILE instead of stdout.\n");
+}
+
+static LY_ERR
+cmd_print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ const struct lysp_submodule *submodule;
+
+ submodule = revision ?
+ ly_ctx_get_submodule(*ctx, name, revision) :
+ ly_ctx_get_submodule_latest(*ctx, name);
+
+ erc = submodule ?
+ lys_print_submodule(out, submodule, format, line_length, options) :
+ LY_ENOTFOUND;
+
+ return erc;
+}
+
+static LY_ERR
+cmd_print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ struct lys_module *module;
+
+ module = revision ?
+ ly_ctx_get_module(*ctx, name, revision) :
+ ly_ctx_get_module_latest(*ctx, name);
+
+ erc = module ?
+ lys_print_module(out, module, format, line_length, options) :
+ LY_ENOTFOUND;
+
+ return erc;
+}
+
+static void
+cmd_print_modules(int argc, char **argv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ char *name, *revision;
+ ly_bool search_submodul;
+ const int stop = argc - optind;
+
+ for (int i = 0; i < stop; i++) {
+ name = argv[optind + i];
+ /* get revision */
+ revision = strchr(name, '@');
+ if (revision) {
+ revision[0] = '\0';
+ ++revision;
+ }
+
+ erc = cmd_print_module(out, ctx, name, revision, format, line_length, options);
+
+ if (erc == LY_ENOTFOUND) {
+ search_submodul = 1;
+ erc = cmd_print_submodule(out, ctx, name, revision, format, line_length, options);
+ } else {
+ search_submodul = 0;
+ }
+
+ if (erc == LY_SUCCESS) {
+ /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
+ if ((format == LYS_OUT_TREE) && (i + 1 < stop)) {
+ ly_print(out, "\n");
+ }
+ continue;
+ } else if (erc == LY_ENOTFOUND) {
+ if (revision) {
+ YLMSG_E("No (sub)module \"%s\" in revision %s found.\n", name, revision);
+ } else {
+ YLMSG_E("No (sub)module \"%s\" found.\n", name);
+ }
+ break;
+ } else {
+ if (search_submodul) {
+ YLMSG_E("Unable to print submodule %s.\n", name);
+ } else {
+ YLMSG_E("Unable to print module %s.\n", name);
+ }
+ break;
+ }
+ }
+}
+
+void
+cmd_print(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"format", required_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {"tree-line-length", required_argument, NULL, 'L'},
+ {"output", required_argument, NULL, 'o'},
+ {"schema-node", required_argument, NULL, 'P'},
+ {"single-node", no_argument, NULL, 'q'},
+ {NULL, 0, NULL, 0}
+ };
+ uint16_t options_print = 0;
+ const char *node_path = NULL;
+ LYS_OUTFORMAT format = LYS_OUT_TREE;
+ struct ly_out *out = NULL;
+ ly_bool out_stdout = 0;
+ size_t line_length = 0;
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "f:hL:o:P:q", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'o': /* --output */
+ if (out) {
+ if (ly_out_filepath(out, optarg) != NULL) {
+ YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
+ goto cleanup;
+ }
+ } else {
+ if (ly_out_new_filepath(optarg, &out)) {
+ YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
+ goto cleanup;
+ }
+ }
+ break;
+
+ case 'f': /* --format */
+ if (!strcasecmp(optarg, "yang")) {
+ format = LYS_OUT_YANG;
+ } else if (!strcasecmp(optarg, "yin")) {
+ format = LYS_OUT_YIN;
+ } else if (!strcasecmp(optarg, "info")) {
+ format = LYS_OUT_YANG_COMPILED;
+ } else if (!strcasecmp(optarg, "tree")) {
+ format = LYS_OUT_TREE;
+ } else {
+ YLMSG_E("Unknown output format %s\n", optarg);
+ cmd_print_help();
+ goto cleanup;
+ }
+ break;
+
+ case 'L': /* --tree-line-length */
+ line_length = atoi(optarg);
+ break;
+
+ case 'P': /* --schema-node */
+ node_path = optarg;
+ break;
+
+ case 'q': /* --single-node */
+ options_print |= LYS_PRINT_NO_SUBSTMT;
+ break;
+
+ case 'h':
+ cmd_print_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ /* file name */
+ if ((argc == optind) && !node_path) {
+ YLMSG_E("Missing the name of the module to print.\n");
+ goto cleanup;
+ }
+
+ if ((format != LYS_OUT_TREE) && line_length) {
+ YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
+ goto cleanup;
+ }
+
+ if (!out) {
+ if (ly_out_new_file(stdout, &out)) {
+ YLMSG_E("Could not use stdout to print output.\n");
+ goto cleanup;
+ }
+ out_stdout = 1;
+ }
+
+ if (format == LYS_OUT_TREE) {
+ /* print tree from lysc_nodes */
+ ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
+ }
+
+ if (node_path) {
+ const struct lysc_node *node;
+
+ node = find_schema_path(*ctx, node_path);
+ if (!node) {
+ YLMSG_E("The requested schema node \"%s\" does not exists.\n", node_path);
+ goto cleanup;
+ }
+
+ if (lys_print_node(out, node, format, 0, options_print)) {
+ YLMSG_E("Unable to print schema node %s.\n", node_path);
+ goto cleanup;
+ }
+ } else {
+ cmd_print_modules(argc, argv, out, ctx, format, line_length, options_print);
+ goto cleanup;
+ }
+
+cleanup:
+ free_cmdline(argv);
+ ly_out_free(out, NULL, out_stdout ? 0 : 1);
+}
diff --git a/tools/lint/cmd_searchpath.c b/tools/lint/cmd_searchpath.c
new file mode 100644
index 0000000..529e05d
--- /dev/null
+++ b/tools/lint/cmd_searchpath.c
@@ -0,0 +1,90 @@
+/**
+ * @file cmd_searchpath.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief 'searchpath' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+void
+cmd_searchpath_help(void)
+{
+ printf("Usage: searchpath [--clear] [<modules-dir-path> ...]\n"
+ " Set paths of directories where to search for imports and\n"
+ " includes of the schema modules. The current working directory\n"
+ " and the path of the module being added is used implicitly.\n"
+ " The 'load' command uses these paths to search even for the\n"
+ " schema modules to be loaded.\n");
+}
+
+void
+cmd_searchpath(struct ly_ctx **ctx, const char *cmdline)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int opt, opt_index;
+ struct option options[] = {
+ {"clear", no_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ int8_t cleared = 0;
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, "ch", options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ ly_ctx_unset_searchdir(*ctx, NULL);
+ cleared = 1;
+ break;
+ case 'h':
+ cmd_searchpath_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ if (!cleared && (argc == optind)) {
+ /* no argument - print the paths */
+ const char * const *dirs = ly_ctx_get_searchdirs(*ctx);
+
+ printf("List of the searchpaths:\n");
+ for (uint32_t i = 0; dirs[i]; ++i) {
+ printf(" %s\n", dirs[i]);
+ }
+ goto cleanup;
+ }
+
+ for (int i = 0; i < argc - optind; i++) {
+ if (ly_ctx_set_searchdir(*ctx, argv[optind + i])) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ free_cmdline(argv);
+}
diff --git a/tools/lint/common.c b/tools/lint/common.c
new file mode 100644
index 0000000..fc9b1cd
--- /dev/null
+++ b/tools/lint/common.c
@@ -0,0 +1,868 @@
+/**
+ * @file common.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode.
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
+
+#include "common.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "compat.h"
+#include "libyang.h"
+
+int
+parse_schema_path(const char *path, char **dir, char **module)
+{
+ char *p;
+
+ assert(dir);
+ assert(module);
+
+ /* split the path to dirname and basename for further work */
+ *dir = strdup(path);
+ *module = strrchr(*dir, '/');
+ if (!(*module)) {
+ *module = *dir;
+ *dir = strdup("./");
+ } else {
+ *module[0] = '\0'; /* break the dir */
+ *module = strdup((*module) + 1);
+ }
+ /* get the pure module name without suffix or revision part of the filename */
+ if ((p = strchr(*module, '@'))) {
+ /* revision */
+ *p = '\0';
+ } else if ((p = strrchr(*module, '.'))) {
+ /* fileformat suffix */
+ *p = '\0';
+ }
+
+ return 0;
+}
+
+int
+get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
+{
+ struct stat st;
+
+ /* check that the filepath exists and is a regular file */
+ if (stat(filepath, &st) == -1) {
+ YLMSG_E("Unable to use input filepath (%s) - %s.\n", filepath, strerror(errno));
+ return -1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ YLMSG_E("Provided input file (%s) is not a regular file.\n", filepath);
+ return -1;
+ }
+
+ if ((format_schema && !*format_schema) || (format_data && !*format_data)) {
+ /* get the file format */
+ if (get_format(filepath, format_schema, format_data)) {
+ return -1;
+ }
+ }
+
+ if (ly_in_new_filepath(filepath, 0, in)) {
+ YLMSG_E("Unable to process input file.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+free_features(void *flist)
+{
+ struct schema_features *rec = (struct schema_features *)flist;
+
+ if (rec) {
+ free(rec->mod_name);
+ if (rec->features) {
+ for (uint32_t u = 0; rec->features[u]; ++u) {
+ free(rec->features[u]);
+ }
+ free(rec->features);
+ }
+ free(rec);
+ }
+}
+
+void
+get_features(struct ly_set *fset, const char *module, const char ***features)
+{
+ /* get features list for this module */
+ for (uint32_t u = 0; u < fset->count; ++u) {
+ struct schema_features *sf = (struct schema_features *)fset->objs[u];
+
+ if (!strcmp(module, sf->mod_name)) {
+ /* matched module - explicitly set features */
+ *features = (const char **)sf->features;
+ sf->applied = 1;
+ return;
+ }
+ }
+
+ /* features not set so disable all */
+ *features = NULL;
+}
+
+int
+parse_features(const char *fstring, struct ly_set *fset)
+{
+ struct schema_features *rec;
+ uint32_t count;
+ char *p, **fp;
+
+ rec = calloc(1, sizeof *rec);
+ if (!rec) {
+ YLMSG_E("Unable to allocate features information record (%s).\n", strerror(errno));
+ return -1;
+ }
+ if (ly_set_add(fset, rec, 1, NULL)) {
+ YLMSG_E("Unable to store features information (%s).\n", strerror(errno));
+ free(rec);
+ return -1;
+ }
+
+ /* fill the record */
+ p = strchr(fstring, ':');
+ if (!p) {
+ YLMSG_E("Invalid format of the features specification (%s).\n", fstring);
+ return -1;
+ }
+ rec->mod_name = strndup(fstring, p - fstring);
+
+ count = 0;
+ while (p) {
+ size_t len = 0;
+ char *token = p + 1;
+
+ p = strchr(token, ',');
+ if (!p) {
+ /* the last item, if any */
+ len = strlen(token);
+ } else {
+ len = p - token;
+ }
+
+ if (len) {
+ fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+ if (!fp) {
+ YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
+ return -1;
+ }
+ rec->features = fp;
+ fp = &rec->features[count++]; /* array item to set */
+ (*fp) = strndup(token, len);
+ }
+ }
+
+ /* terminating NULL */
+ fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+ if (!fp) {
+ YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
+ return -1;
+ }
+ rec->features = fp;
+ rec->features[count++] = NULL;
+
+ return 0;
+}
+
+struct cmdline_file *
+fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
+{
+ struct cmdline_file *rec;
+
+ rec = malloc(sizeof *rec);
+ if (!rec) {
+ YLMSG_E("Allocating memory for data file information failed.\n");
+ return NULL;
+ }
+ rec->in = in;
+ rec->path = path;
+ rec->format = format;
+
+ if (set && ly_set_add(set, rec, 1, NULL)) {
+ free(rec);
+ YLMSG_E("Storing data file information failed.\n");
+ return NULL;
+ }
+
+ return rec;
+}
+
+int
+collect_features(const struct lys_module *mod, struct ly_set *set)
+{
+ struct lysp_feature *f = NULL;
+ uint32_t idx = 0;
+
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ if (ly_set_add(set, (void *)f->name, 1, NULL)) {
+ YLMSG_E("Memory allocation failed.\n");
+ ly_set_erase(set, NULL);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set)
+{
+ size_t max_len;
+ uint32_t j;
+ const char *name;
+
+ /* header */
+ ly_print(out, "%s:\n", mod->name);
+
+ /* no features */
+ if (!set->count) {
+ ly_print(out, "\t(none)\n\n");
+ return;
+ }
+
+ /* get max len, so the statuses of all the features will be aligned */
+ max_len = 0;
+ for (j = 0; j < set->count; ++j) {
+ name = set->objs[j];
+ if (strlen(name) > max_len) {
+ max_len = strlen(name);
+ }
+ }
+
+ /* print features */
+ for (j = 0; j < set->count; ++j) {
+ name = set->objs[j];
+ ly_print(out, "\t%-*s (%s)\n", (int)max_len, name, lys_feature_value(mod, name) ? "off" : "on");
+ }
+
+ ly_print(out, "\n");
+}
+
+int
+generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param)
+{
+ uint32_t j;
+ /*
+ * features_len - length of all the features in the current module
+ * added_len - length of a string to be added, = features_len + extra necessary length
+ * param_len - length of the parameter before appending new string
+ */
+ size_t features_len, added_len, param_len;
+ char *tmp;
+
+ features_len = 0;
+ for (j = 0; j < set->count; j++) {
+ features_len += strlen(set->objs[j]);
+ }
+
+ if (j == 0) {
+ /* no features */
+ added_len = strlen("-F ") + strlen(mod->name) + strlen(":");
+ } else {
+ /* j = comma count, -1 because of trailing comma */
+ added_len = strlen("-F ") + strlen(mod->name) + strlen(":") + features_len + j - 1;
+ }
+
+ /* to avoid strlen(NULL) if this is the first call */
+ param_len = 0;
+ if (*features_param) {
+ param_len = strlen(*features_param);
+ }
+
+ /* +1 because of white space at the beginning */
+ tmp = realloc(*features_param, param_len + added_len + 1 + 1);
+ if (!tmp) {
+ goto error;
+ } else {
+ *features_param = tmp;
+ }
+ sprintf(*features_param + param_len, " -F %s:", mod->name);
+
+ for (j = 0; j < set->count; j++) {
+ strcat(*features_param, set->objs[j]);
+ /* no trailing comma */
+ if (j != (set->count - 1)) {
+ strcat(*features_param, ",");
+ }
+ }
+
+ return 0;
+
+error:
+ YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
+ return 1;
+}
+
+int
+print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param)
+{
+ int ret = 0;
+ uint32_t i = 0;
+ struct lys_module *mod;
+ struct ly_set set = {0};
+
+ while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
+ /* only care about implemented modules */
+ if (!mod->implemented) {
+ continue;
+ }
+
+ /* always erase the set, so the previous module's features don't carry over to the next module's features */
+ ly_set_erase(&set, NULL);
+
+ if (collect_features(mod, &set)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (generate_features && generate_features_output(mod, &set, features_param)) {
+ ret = 1;
+ goto cleanup;
+ }
+ print_features(out, mod, &set);
+ }
+
+cleanup:
+ ly_set_erase(&set, NULL);
+ return ret;
+}
+
+void
+free_cmdline_file(void *cmdline_file)
+{
+ struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
+
+ if (rec) {
+ ly_in_free(rec->in, 1);
+ free(rec);
+ }
+}
+
+void
+free_cmdline(char *argv[])
+{
+ if (argv) {
+ free(argv[0]);
+ free(argv);
+ }
+}
+
+int
+parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
+{
+ int count;
+ char **vector;
+ char *ptr;
+ char qmark = 0;
+
+ assert(cmdline);
+ assert(argc_p);
+ assert(argv_p);
+
+ /* init */
+ optind = 0; /* reinitialize getopt() */
+ count = 1;
+ vector = malloc((count + 1) * sizeof *vector);
+ vector[0] = strdup(cmdline);
+
+ /* command name */
+ strtok(vector[0], " ");
+
+ /* arguments */
+ while ((ptr = strtok(NULL, " "))) {
+ size_t len;
+ void *r;
+
+ len = strlen(ptr);
+
+ if (qmark) {
+ /* still in quotated text */
+ /* remove NULL termination of the previous token since it is not a token,
+ * but a part of the quotation string */
+ ptr[-1] = ' ';
+
+ if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+ /* end of quotation */
+ qmark = 0;
+ /* shorten the argument by the terminating quotation mark */
+ ptr[len - 1] = '\0';
+ }
+ continue;
+ }
+
+ /* another token in cmdline */
+ ++count;
+ r = realloc(vector, (count + 1) * sizeof *vector);
+ if (!r) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
+ free(vector);
+ return -1;
+ }
+ vector = r;
+ vector[count - 1] = ptr;
+
+ if ((ptr[0] == '"') || (ptr[0] == '\'')) {
+ /* remember the quotation mark to identify end of quotation */
+ qmark = ptr[0];
+
+ /* move the remembered argument after the quotation mark */
+ ++vector[count - 1];
+
+ /* check if the quotation is terminated within this token */
+ if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+ /* end of quotation */
+ qmark = 0;
+ /* shorten the argument by the terminating quotation mark */
+ ptr[len - 1] = '\0';
+ }
+ }
+ }
+ vector[count] = NULL;
+
+ *argc_p = count;
+ *argv_p = vector;
+
+ return 0;
+}
+
+int
+get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
+{
+ char *ptr;
+ LYS_INFORMAT informat_s;
+ LYD_FORMAT informat_d;
+
+ /* get the file format */
+ if ((ptr = strrchr(filename, '.')) != NULL) {
+ ++ptr;
+ if (!strcmp(ptr, "yang")) {
+ informat_s = LYS_IN_YANG;
+ informat_d = 0;
+ } else if (!strcmp(ptr, "yin")) {
+ informat_s = LYS_IN_YIN;
+ informat_d = 0;
+ } else if (!strcmp(ptr, "xml")) {
+ informat_s = 0;
+ informat_d = LYD_XML;
+ } else if (!strcmp(ptr, "json")) {
+ informat_s = 0;
+ informat_d = LYD_JSON;
+ } else if (!strcmp(ptr, "lyb")) {
+ informat_s = 0;
+ informat_d = LYD_LYB;
+ } else {
+ YLMSG_E("Input file \"%s\" in an unknown format \"%s\".\n", filename, ptr);
+ return 0;
+ }
+ } else {
+ YLMSG_E("Input file \"%s\" without file extension - unknown format.\n", filename);
+ return 1;
+ }
+
+ if (informat_d) {
+ if (!data) {
+ YLMSG_E("Input file \"%s\" not expected to contain data instances (unexpected format).\n", filename);
+ return 2;
+ }
+ (*data) = informat_d;
+ } else if (informat_s) {
+ if (!schema) {
+ YLMSG_E("Input file \"%s\" not expected to contain schema definition (unexpected format).\n", filename);
+ return 3;
+ }
+ (*schema) = informat_s;
+ }
+
+ return 0;
+}
+
+int
+print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
+{
+ struct lyd_node *ylib;
+ uint32_t idx = 0, has_modules = 0;
+ const struct lys_module *mod;
+
+ if (outformat != LYD_UNKNOWN) {
+ if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
+ YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, use an option to add it internally.\n");
+ return 1;
+ }
+
+ lyd_print_all(out, ylib, outformat, 0);
+ lyd_free_all(ylib);
+ return 0;
+ }
+
+ /* iterate schemas in context and provide just the basic info */
+ ly_print(out, "List of the loaded models:\n");
+ while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
+ has_modules++;
+
+ /* conformance print */
+ if (mod->implemented) {
+ ly_print(out, " I");
+ } else {
+ ly_print(out, " i");
+ }
+
+ /* module print */
+ ly_print(out, " %s", mod->name);
+ if (mod->revision) {
+ ly_print(out, "@%s", mod->revision);
+ }
+
+ /* submodules print */
+ if (mod->parsed && mod->parsed->includes) {
+ uint64_t u = 0;
+
+ ly_print(out, " (");
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ ly_print(out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
+ if (mod->parsed->includes[u].rev[0]) {
+ ly_print(out, "@%s", mod->parsed->includes[u].rev);
+ }
+ }
+ ly_print(out, ")");
+ }
+
+ /* finish the line */
+ ly_print(out, "\n");
+ }
+
+ if (!has_modules) {
+ ly_print(out, "\t(none)\n");
+ }
+
+ ly_print_flush(out);
+ return 0;
+}
+
+int
+evaluate_xpath(const struct lyd_node *tree, const char *xpath)
+{
+ struct ly_set *set = NULL;
+
+ if (lyd_find_xpath(tree, xpath, &set)) {
+ return -1;
+ }
+
+ /* print result */
+ printf("XPath \"%s\" evaluation result:\n", xpath);
+ if (!set->count) {
+ printf("\tEmpty\n");
+ } else {
+ for (uint32_t u = 0; u < set->count; ++u) {
+ struct lyd_node *node = (struct lyd_node *)set->objs[u];
+
+ printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
+ if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ printf(" (value: \"%s\")\n", lyd_get_value(node));
+ } else if (node->schema->nodetype == LYS_LIST) {
+ printf(" (");
+ for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
+ printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
+ key->schema->name, lyd_get_value(key));
+ }
+ printf(")\n");
+ }
+ }
+ }
+
+ ly_set_free(set, NULL);
+ return 0;
+}
+
+LY_ERR
+process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
+ uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
+ struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
+ char *path = NULL;
+ struct ly_set *set = NULL;
+
+ /* additional operational datastore */
+ if (operational_f && operational_f->in) {
+ ret = lyd_parse_data(ctx, NULL, operational_f->in, operational_f->format, LYD_PARSE_ONLY, 0, &oper_tree);
+ if (ret) {
+ YLMSG_E("Failed to parse operational datastore file \"%s\".\n", operational_f->path);
+ goto cleanup;
+ }
+ }
+
+ for (uint32_t u = 0; u < inputs->count; ++u) {
+ struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
+
+ switch (data_type) {
+ case LYD_TYPE_DATA_YANG:
+ ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, options_parse, options_validate, &tree);
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_NOTIF_YANG:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &tree, &op);
+ break;
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &envp, &op);
+
+ /* adjust pointers */
+ for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ /* parse source RPC operation */
+ assert(rpc_f && rpc_f->in);
+ ret = lyd_parse_op(ctx, NULL, rpc_f->in, rpc_f->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
+ if (ret) {
+ YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".\n", rpc_f->path);
+ goto cleanup;
+ }
+
+ /* adjust pointers */
+ for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
+
+ /* free input */
+ lyd_free_siblings(lyd_child(op));
+
+ /* we do not care */
+ lyd_free_all(envp);
+ envp = NULL;
+
+ ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, data_type, &envp, NULL);
+ break;
+ default:
+ YLMSG_E("Internal error (%s:%d).\n", __FILE__, __LINE__);
+ goto cleanup;
+ }
+
+ if (ret) {
+ YLMSG_E("Failed to parse input data file \"%s\".\n", input_f->path);
+ goto cleanup;
+ }
+
+ if (merge) {
+ /* merge the data so far parsed for later validation and print */
+ if (!merged_tree) {
+ merged_tree = tree;
+ } else {
+ ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
+ if (ret) {
+ YLMSG_E("Merging %s with previous data failed.\n", input_f->path);
+ goto cleanup;
+ }
+ }
+ tree = NULL;
+ } else if (format) {
+ /* print */
+ switch (data_type) {
+ case LYD_TYPE_DATA_YANG:
+ lyd_print_all(out, tree, format, options_print);
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_NOTIF_YANG:
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ lyd_print_tree(out, tree, format, options_print);
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ /* just the output */
+ lyd_print_tree(out, lyd_child(tree), format, options_print);
+ break;
+ default:
+ assert(0);
+ }
+ } else {
+ /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
+ switch (data_type) {
+ case LYD_TYPE_DATA_YANG:
+ /* already validated */
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_RPC_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_REPLY_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ case LYD_TYPE_NOTIF_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
+ break;
+ default:
+ assert(0);
+ }
+ if (ret) {
+ if (operational_f->path) {
+ YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".\n",
+ input_f->path, operational_f->path);
+ } else {
+ YLMSG_E("Failed to validate input data file \"%s\".\n", input_f->path);
+ }
+ goto cleanup;
+ }
+
+ if (op && oper_tree && lyd_parent(op)) {
+ /* check operation parent existence */
+ path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
+ if (!path) {
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
+ goto cleanup;
+ }
+ if (!set->count) {
+ YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* next iter */
+ lyd_free_all(tree);
+ tree = NULL;
+ lyd_free_all(envp);
+ envp = NULL;
+ }
+
+ if (merge) {
+ /* validate the merged result */
+ ret = lyd_validate_all(&merged_tree, ctx, LYD_VALIDATE_PRESENT, NULL);
+ if (ret) {
+ YLMSG_E("Merged data are not valid.\n");
+ goto cleanup;
+ }
+
+ if (format) {
+ /* and print it */
+ lyd_print_all(out, merged_tree, format, options_print);
+ }
+
+ for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
+ if (evaluate_xpath(merged_tree, (const char *)xpaths->objs[u])) {
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+ lyd_free_all(merged_tree);
+ lyd_free_all(oper_tree);
+ free(path);
+ ly_set_free(set, NULL);
+ return ret;
+}
+
+const struct lysc_node *
+find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
+{
+ const char *end, *module_name_end;
+ char *module_name = NULL;
+ const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
+ const struct lys_module *module;
+ size_t node_name_len;
+ ly_bool found_exact_match = 0;
+
+ /* iterate over each '/' in the path */
+ while (schema_path) {
+ /* example: schema_path = /listen/endpoint
+ * end == NULL for endpoint, end exists for listen */
+ end = strchr(schema_path + 1, '/');
+ if (end) {
+ node_name_len = end - schema_path - 1;
+ } else {
+ node_name_len = strlen(schema_path + 1);
+ }
+
+ /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
+ module_name_end = strchr(schema_path, ':');
+ if (module_name_end && (!end || (module_name_end < end))) {
+ /* only get module's name, if it is in the current scope */
+ free(module_name);
+ /* - 1 because module_name_end points to ':' */
+ module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
+ if (!module_name) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ parent_node = NULL;
+ goto cleanup;
+ }
+ /* move the pointer to the beginning of the node's name - 1 */
+ schema_path = module_name_end;
+
+ /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
+ if (module_name_end < end) {
+ node_name_len = end - module_name_end - 1;
+ } else if (!end) {
+ node_name_len = strlen(module_name_end + 1);
+ }
+ }
+
+ module = ly_ctx_get_module_implemented(ctx, module_name);
+ if (!module) {
+ /* unknown module name */
+ parent_node = NULL;
+ goto cleanup;
+ }
+
+ /* iterate over the node's siblings / module's top level containers */
+ while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
+ if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
+ /* check if the whole node's name matches and it's not just a common prefix */
+ parent_node = node;
+ break;
+ } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
+ /* do the same here, however if there is no exact match, use the last node with the same prefix */
+ if (strlen(node->name) == node_name_len) {
+ parent_node = node;
+ found_exact_match = 1;
+ break;
+ } else {
+ parent_node_tmp = node;
+ }
+ }
+ }
+
+ if (!end && !found_exact_match) {
+ /* no exact match */
+ parent_node = parent_node_tmp;
+ }
+ found_exact_match = 0;
+
+ /* next iter */
+ schema_path = strchr(schema_path + 1, '/');
+ }
+
+cleanup:
+ free(module_name);
+ return parent_node;
+}
diff --git a/tools/lint/common.h b/tools/lint/common.h
new file mode 100644
index 0000000..7c6a8ad
--- /dev/null
+++ b/tools/lint/common.h
@@ -0,0 +1,257 @@
+/**
+ * @file common.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool - common functions and definitions for both interactive and non-interactive mode.
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef COMMON_H_
+#define COMMON_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#define PROMPT "> "
+
+/**
+ * @brief Default context creation options.
+ */
+#define YL_DEFAULT_CTX_OPTIONS LY_CTX_NO_YANGLIBRARY
+
+/**
+ * @brief Default data parsing flags.
+ */
+#define YL_DEFAULT_DATA_PARSE_OPTIONS LYD_PARSE_STRICT
+
+/**
+ * @brief log error message
+ */
+#define YLMSG_E(...) \
+ fprintf(stderr, "YANGLINT[E]: " __VA_ARGS__)
+
+/**
+ * @brief log warning message
+ */
+#define YLMSG_W(...) \
+ fprintf(stderr, "YANGLINT[W]: " __VA_ARGS__)
+
+#ifndef _WIN32
+# define PATH_SEPARATOR ":"
+#else
+# define PATH_SEPARATOR ";"
+#endif
+
+/**
+ * @brief Storage for the list of the features (their names) in a specific YANG module.
+ */
+struct schema_features {
+ char *mod_name;
+ char **features;
+ ly_bool applied;
+};
+
+/**
+ * @brief Data connected with a file provided on a command line as a file path.
+ */
+struct cmdline_file {
+ struct ly_in *in;
+ const char *path;
+ LYD_FORMAT format;
+};
+
+/**
+ * @brief Free the schema features list (struct schema_features *)
+ * @param[in,out] flist The (struct schema_features *) to free.
+ */
+void free_features(void *flist);
+
+/**
+ * @brief Get the list of features connected with the specific YANG module.
+ *
+ * @param[in] fset The set of features information (struct schema_features *).
+ * @param[in] module Name of the YANG module which features should be found.
+ * @param[out] features Pointer to the list of features being returned.
+ */
+void get_features(struct ly_set *fset, const char *module, const char ***features);
+
+/**
+ * @brief Parse features being specified for the specific YANG module.
+ *
+ * Format of the input @p fstring is as follows: <module_name>:[<feature>,]*
+ *
+ * @param[in] fstring Input string to be parsed.
+ * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
+ */
+int parse_features(const char *fstring, struct ly_set *fset);
+
+/**
+ * @brief Collect all features of a module.
+ *
+ * @param[in] mod Module to be searched for features.
+ * @param[out] set Set in which the features will be stored.
+ * @return 0 on success.
+ * @return 1 on error.
+ */
+int collect_features(const struct lys_module *mod, struct ly_set *set);
+
+/**
+ * @brief Print all features of a single module.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] mod Module which contains the features.
+ * @param[in] set Set which holds the features.
+ */
+void print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set);
+
+/**
+ * @brief Generate a string, which will contain features paramater.
+ *
+ * @param[in] mod Module, for which the string will be generated.
+ * @param[in] set Set containing the features.
+ * @param[out] features_param String which will contain the output.
+ * @return 0 on success.
+ * @return 1 on error.
+ */
+int generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param);
+
+/**
+ * @brief Print all features of all implemented modules.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] ctx Libyang context.
+ * @param[in] generate_features Flag expressing whether to generate features parameter.
+ * @param[out] features_param String, which will contain the output if the above flag is set.
+ * @return 0 on success.
+ * @return 1 on error.
+ */
+int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param);
+
+/**
+ * @brief Parse path of a schema module file into the directory and module name.
+ *
+ * @param[in] path Schema module file path to be parsed.
+ * @param[out] dir Pointer to the directory path where the file resides. Caller is expected to free the returned string.
+ * @param[out] module Pointer to the name of the module (without file suffixes or revision information) specified by the
+ * @path. Caller is expected to free the returned string.
+ * @return 0 on success
+ * @return -1 on error
+ */
+int parse_schema_path(const char *path, char **dir, char **module);
+
+/**
+ * @brief Get input handler for the specified path.
+ *
+ * Using the @p format_schema and @p format_data the type of the file can be limited (by providing NULL) or it can be
+ * got known if both types are possible.
+ *
+ * @param[in] filepath Path of the file to open.
+ * @param[out] format_schema Format of the schema detected from the file name. If NULL specified, the schema formats are
+ * prohibited and such files are refused.
+ * @param[out] format_data Format of the data detected from the file name. If NULL specified, the data formats are
+ * prohibited and such files are refused.
+ * @param[out] in Created input handler referring the file behind the @p filepath.
+ * @return 0 on success.
+ * @return -1 on failure.
+ */
+int get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in);
+
+/**
+ * @brief Free the command line file data (struct cmdline_file *)
+ * @param[in,out] cmdline_file The (struct cmdline_file *) to free.
+ */
+void free_cmdline_file(void *cmdline_file);
+
+/**
+ * @brief Create and fill the command line file data (struct cmdline_file *).
+ * @param[in] set Optional parameter in case the record is supposed to be added into a set.
+ * @param[in] in Input file handler.
+ * @param[in] path Filepath of the file.
+ * @param[in] format Format of the data file.
+ * @return The created command line file structure.
+ * @return NULL on failure
+ */
+struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
+
+/**
+ * @brief Helper function to prepare argc, argv pair from a command line string.
+ *
+ * @param[in] cmdline Complete command line string.
+ * @param[out] argc_p Pointer to store argc value.
+ * @param[out] argv_p Pointer to store argv vector.
+ * @return 0 on success, non-zero on failure.
+ */
+int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
+
+/**
+ * @brief Destructor for the argument vector prepared by ::parse_cmdline().
+ *
+ * @param[in,out] argv Argument vector to destroy.
+ */
+void free_cmdline(char *argv[]);
+
+/**
+ * @brief Get expected format of the @p filename's content according to the @p filename's suffix.
+ * @param[in] filename Name of the file to examine.
+ * @param[out] schema Pointer to a variable to store the expected input schema format. Do not provide the pointer in case a
+ * schema format is not expected.
+ * @param[out] data Pointer to a variable to store the expected input data format. Do not provide the pointer in case a data
+ * format is not expected.
+ * @return zero in case a format was successfully detected.
+ * @return nonzero in case it is not possible to get valid format from the @p filename.
+ */
+int get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data);
+
+/**
+ * @brief Print list of schemas in the context.
+ *
+ * @param[in] out Output handler where to print.
+ * @param[in] ctx Context to print.
+ * @param[in] outformat Optional output format. If not specified (:LYD_UNKNOWN), a simple list with single module per line
+ * is printed. Otherwise, the ietf-yang-library data are printed in the specified format.
+ * @return zero in case the data successfully printed.
+ * @return nonzero in case of error.
+ */
+int print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
+
+/**
+ * @brief Process the input data files - parse, validate and print according to provided options.
+ *
+ * @param[in] ctx libyang context with schema.
+ * @param[in] data_type The type of data in the input files.
+ * @param[in] merge Flag if the data should be merged before validation.
+ * @param[in] format Data format for printing.
+ * @param[in] out The output handler for printing.
+ * @param[in] options_parse Parser options.
+ * @param[in] options_validate Validation options.
+ * @param[in] options_print Printer options.
+ * @param[in] operational_f Optional operational datastore file information for the case of an extended validation of
+ * operation(s).
+ * @param[in] rpc_f Source RPC operation file information for parsing NETCONF rpc-reply.
+ * @param[in] inputs Set of file informations of input data files.
+ * @param[in] xpath The set of XPaths to be evaluated on the processed data tree, basic information about the resulting set
+ * is printed. Alternative to data printing.
+ * @return LY_ERR value.
+ */
+LY_ERR process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
+ uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
+ struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths);
+
+/**
+ * @brief Get the node specified by the path.
+ *
+ * @param[in] ctx libyang context with schema.
+ * @param[in] schema_path Path to the wanted node.
+ * @return Pointer to the schema node specified by the path on success, NULL otherwise.
+ */
+const struct lysc_node * find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
+
+#endif /* COMMON_H_ */
diff --git a/tools/lint/completion.c b/tools/lint/completion.c
new file mode 100644
index 0000000..9843816
--- /dev/null
+++ b/tools/lint/completion.c
@@ -0,0 +1,379 @@
+/**
+ * @file completion.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool auto completion
+ *
+ * Copyright (c) 2015 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "cmd.h"
+#include "common.h"
+#include "compat.h"
+#include "linenoise/linenoise.h"
+
+/* from the main.c */
+extern struct ly_ctx *ctx;
+
+/**
+ * @brief Add a match to the completions array.
+ *
+ * @param[in] match Match to be added.
+ * @param[in,out] matches Matches provided to the user as a completion hint.
+ * @param[in,out] match_count Number of matches.
+ */
+static void
+cmd_completion_add_match(const char *match, char ***matches, unsigned int *match_count)
+{
+ void *p;
+
+ ++(*match_count);
+ p = realloc(*matches, *match_count * sizeof **matches);
+ if (!p) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ return;
+ }
+ *matches = p;
+ (*matches)[*match_count - 1] = strdup(match);
+}
+
+/**
+ * @brief Provides completion for command names.
+ *
+ * @param[in] hint User input.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
+{
+ int i;
+
+ *match_count = 0;
+ *matches = NULL;
+
+ for (i = 0; commands[i].name; i++) {
+ if (!strncmp(hint, commands[i].name, strlen(hint))) {
+ cmd_completion_add_match(commands[i].name, matches, match_count);
+ }
+ }
+}
+
+/**
+ * @brief Provides completion for module names.
+ *
+ * @param[in] hint User input.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_model_completion(const char *hint, char ***matches, unsigned int *match_count)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t idx = 0;
+ const struct lys_module *module;
+
+ *match_count = 0;
+ *matches = NULL;
+
+ while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
+ if (!strncmp(hint, module->name, strlen(hint))) {
+ cmd_completion_add_match(module->name, matches, match_count);
+ }
+
+ LY_ARRAY_FOR(module->parsed->includes, u) {
+ if (!strncmp(hint, module->parsed->includes[u].submodule->name, strlen(hint))) {
+ cmd_completion_add_match(module->parsed->includes[u].submodule->name, matches, match_count);
+ }
+ }
+ }
+}
+
+/**
+ * @brief Add all child nodes of a single node to the completion hint.
+ *
+ * @param[in] last_node Node of which children will be added to the hint.
+ * @param matches[out] Matches provided to the user as a completion hint.
+ * @param match_count[out] Number of matches.
+ */
+static void
+single_hint_add_children(const struct lysc_node *last_node, char ***matches, unsigned int *match_count)
+{
+ const struct lysc_node *node = NULL;
+ char *match;
+
+ if (!last_node) {
+ return;
+ }
+
+ while ((node = lys_getnext(node, last_node, NULL, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
+ match = lysc_path(node, LYSC_PATH_LOG, NULL, 0);
+ cmd_completion_add_match(match, matches, match_count);
+ free(match);
+ }
+}
+
+/**
+ * @brief Add module and/or node's children names to the hint.
+ *
+ * @param[in] module Compiled schema module.
+ * @param[in] parent Parent node of which children are potential matches.
+ * @param[in] hint_node_name Node name contained within the hint specified by user.
+ * @param[in,out] matches Matches provided to the user as a completion hint.
+ * @param[in,out] match_count Number of matches.
+ * @param[out] last_node Last processed node.
+ */
+static void
+add_all_children_nodes(const struct lysc_module *module, const struct lysc_node *parent,
+ const char *hint_node_name, char ***matches, unsigned int *match_count, const struct lysc_node **last_node)
+{
+ const struct lysc_node *node;
+ char *match, *node_name = NULL;
+
+ *last_node = NULL;
+
+ if (!parent && !module) {
+ return;
+ }
+
+ node = NULL;
+ while ((node = lys_getnext(node, parent, module, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
+ if (parent && (node->module != parent->module)) {
+ /* augmented node */
+ if (asprintf(&node_name, "%s:%s", node->module->name, node->name) == -1) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ break;
+ }
+ } else {
+ node_name = strdup(node->name);
+ if (!node_name) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ break;
+ }
+ }
+ if (!hint_node_name || !strncmp(hint_node_name, node_name, strlen(hint_node_name))) {
+ /* adding just module names + their top level node(s) to the hint */
+ *last_node = node;
+ match = lysc_path(node, LYSC_PATH_LOG, NULL, 0);
+ cmd_completion_add_match(match, matches, match_count);
+ free(match);
+ }
+ free(node_name);
+ }
+}
+
+/**
+ * @brief Provides completion for schemas.
+ *
+ * @param[in] hint User input.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_schema_completion(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const struct lys_module *module;
+ uint32_t idx;
+ const char *start;
+ char *end, *module_name = NULL, *path = NULL;
+ const struct lysc_node *parent, *last_node;
+ int rc = 0;
+ size_t len;
+
+ *match_count = 0;
+ *matches = NULL;
+
+ if (strlen(hint)) {
+ if (hint[0] != '/') {
+ return;
+ }
+ start = hint + 1;
+ } else {
+ start = hint;
+ }
+
+ end = strchr(start, ':');
+ if (!end) {
+ /* no module name */
+ len = strlen(start);
+
+ /* go through all the modules */
+ idx = 0;
+ while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
+ if (!module->implemented) {
+ continue;
+ }
+
+ if (!len || !strncmp(start, module->name, len)) {
+ /* add all their (matching) top level nodes */
+ add_all_children_nodes(module->compiled, NULL, NULL, matches, match_count, &last_node);
+ }
+ }
+ } else {
+ /* module name known */
+ module_name = strndup(start, end - start);
+ if (!module_name) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ rc = 1;
+ goto cleanup;
+ }
+
+ module = ly_ctx_get_module_implemented(ctx, module_name);
+ if (!module) {
+ goto cleanup;
+ }
+
+ /* find the last '/', if it is at the beginning of the hint, only path up to the top level node is known,
+ * else the name of the last node starts after the found '/' */
+ start = strrchr(hint, '/');
+ if (!start) {
+ goto cleanup;
+ }
+
+ if (start == hint) {
+ /* only the (incomplete) top level node path, add all (matching) top level nodes */
+ add_all_children_nodes(module->compiled, NULL, end + 1, matches, match_count, &last_node);
+ goto cleanup;
+ }
+
+ /* get rid of stuff after the last '/' to obtain the parent node */
+ path = strndup(hint, start - hint);
+ if (!path) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ rc = 1;
+ goto cleanup;
+ }
+
+ /* get the last parent in the hint (it may not exist) */
+ parent = find_schema_path(ctx, path);
+
+ /* add all (matching) child nodes of the parent */
+ add_all_children_nodes(NULL, parent, start + 1, matches, match_count, &last_node);
+ }
+
+cleanup:
+ if (!rc && (*match_count == 1)) {
+ /* to avoid a single hint (space at the end), add all children as hints */
+ single_hint_add_children(last_node, matches, match_count);
+ }
+ free(path);
+ free(module_name);
+}
+
+/**
+ * @brief Get the string before the hint, which autocompletion is for.
+ *
+ * @param[in] buf Complete user input.
+ * @param[in] hint Hint part of the user input.
+ * @return Pointer to the last string.
+ */
+static const char *
+get_last_str(const char *buf, const char *hint)
+{
+ const char *ptr;
+
+ if (buf == hint) {
+ return buf;
+ }
+
+ ptr = hint - 1;
+ while (ptr[0] == ' ') {
+ --ptr;
+ if (buf == ptr) {
+ return buf;
+ }
+ }
+
+ while (ptr[-1] != ' ') {
+ --ptr;
+ if (buf == ptr) {
+ return buf;
+ }
+ }
+
+ return ptr;
+}
+
+/* callback */
+void
+complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
+{
+ struct autocomplete {
+ const char *cmd; /**< command */
+ const char *opt; /**< optional option */
+ int last_opt; /**< whether to autocomplete even if an option is last in the hint */
+
+ void (*ln_cb)(const char *, const char *, linenoiseCompletions *); /**< linenoise callback to call */
+ void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
+ } ac[] = {
+ {"add", NULL, 1, linenoisePathCompletion, NULL},
+ {"searchpath", NULL, 0, linenoisePathCompletion, NULL},
+ {"data", NULL, 0, linenoisePathCompletion, NULL},
+ {"print", NULL, 0, NULL, get_model_completion},
+ {"feature", NULL, 0, NULL, get_model_completion},
+ {"print", "-P", 1, NULL, get_schema_completion},
+ };
+ size_t cmd_len;
+ const char *last;
+ char **matches = NULL;
+ unsigned int match_count = 0, i;
+
+ if (buf == hint) {
+ /* command autocomplete */
+ get_cmd_completion(hint, &matches, &match_count);
+
+ } else {
+ for (i = 0; i < (sizeof ac / sizeof *ac); ++i) {
+ cmd_len = strlen(ac[i].cmd);
+ if (strncmp(buf, ac[i].cmd, cmd_len) || (buf[cmd_len] != ' ')) {
+ /* not this command */
+ continue;
+ }
+
+ last = get_last_str(buf, hint);
+ if (ac[i].opt && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
+ /* autocompletion for (another) option */
+ continue;
+ }
+ if (!ac[i].last_opt && (last[0] == '-')) {
+ /* autocompletion for the command, not an option */
+ continue;
+ }
+ if ((last != buf) && (last[0] != '-')) {
+ /* autocompleted */
+ return;
+ }
+
+ /* callback */
+ if (ac[i].ln_cb) {
+ ac[i].ln_cb(buf, hint, lc);
+ } else {
+ ac[i].yl_cb(hint, &matches, &match_count);
+ }
+ break;
+ }
+ }
+
+ /* transform matches into autocompletion, if needed */
+ for (i = 0; i < match_count; ++i) {
+ linenoiseAddCompletion(lc, matches[i]);
+ free(matches[i]);
+ }
+ free(matches);
+}
diff --git a/tools/lint/completion.h b/tools/lint/completion.h
new file mode 100644
index 0000000..20b8d17
--- /dev/null
+++ b/tools/lint/completion.h
@@ -0,0 +1,25 @@
+/**
+ * @file completion.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool auto completion header
+ *
+ * Copyright (c) 2015 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef COMPLETION_H_
+#define COMPLETION_H_
+
+#include "./linenoise/linenoise.h"
+
+/**
+ * @brief Command line completion callback.
+ */
+void complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc);
+
+#endif /* COMPLETION_H_ */
diff --git a/tools/lint/configuration.c b/tools/lint/configuration.c
new file mode 100644
index 0000000..86179fa
--- /dev/null
+++ b/tools/lint/configuration.c
@@ -0,0 +1,125 @@
+/**
+ * @file configuration.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief yanglint configuration
+ *
+ * Copyright (c) 2017 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "configuration.h"
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "linenoise/linenoise.h"
+
+#include "common.h"
+
+/* Yanglint home (appended to ~/) */
+#define YL_DIR ".yanglint"
+
+char *
+get_yanglint_dir(void)
+{
+ int ret;
+ struct passwd *pw;
+ char *user_home, *yl_dir;
+
+ if (!(pw = getpwuid(getuid()))) {
+ YLMSG_E("Determining home directory failed (%s).\n", strerror(errno));
+ return NULL;
+ }
+ user_home = pw->pw_dir;
+
+ yl_dir = malloc(strlen(user_home) + 1 + strlen(YL_DIR) + 1);
+ if (!yl_dir) {
+ YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ return NULL;
+ }
+ sprintf(yl_dir, "%s/%s", user_home, YL_DIR);
+
+ ret = access(yl_dir, R_OK | X_OK);
+ if (ret == -1) {
+ if (errno == ENOENT) {
+ /* directory does not exist */
+ YLMSG_W("Configuration directory \"%s\" does not exist, creating it.\n", yl_dir);
+ if (mkdir(yl_dir, 00700)) {
+ if (errno != EEXIST) {
+ /* parallel execution, yay */
+ YLMSG_E("Configuration directory \"%s\" cannot be created (%s).\n", yl_dir, strerror(errno));
+ free(yl_dir);
+ return NULL;
+ }
+ }
+ } else {
+ YLMSG_E("Configuration directory \"%s\" exists but cannot be accessed (%s).\n", yl_dir, strerror(errno));
+ free(yl_dir);
+ return NULL;
+ }
+ }
+
+ return yl_dir;
+}
+
+void
+load_config(void)
+{
+ char *yl_dir, *history_file;
+
+ if ((yl_dir = get_yanglint_dir()) == NULL) {
+ return;
+ }
+
+ history_file = malloc(strlen(yl_dir) + 9);
+ if (!history_file) {
+ YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ free(yl_dir);
+ return;
+ }
+
+ sprintf(history_file, "%s/history", yl_dir);
+ if (access(history_file, F_OK) && (errno == ENOENT)) {
+ YLMSG_W("No saved history.\n");
+ } else if (linenoiseHistoryLoad(history_file)) {
+ YLMSG_E("Failed to load history.\n");
+ }
+
+ free(history_file);
+ free(yl_dir);
+}
+
+void
+store_config(void)
+{
+ char *yl_dir, *history_file;
+
+ if ((yl_dir = get_yanglint_dir()) == NULL) {
+ return;
+ }
+
+ history_file = malloc(strlen(yl_dir) + 9);
+ if (!history_file) {
+ YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ free(yl_dir);
+ return;
+ }
+
+ sprintf(history_file, "%s/history", yl_dir);
+ if (linenoiseHistorySave(history_file)) {
+ YLMSG_E("Failed to save history.\n");
+ }
+
+ free(history_file);
+ free(yl_dir);
+}
diff --git a/tools/lint/configuration.h b/tools/lint/configuration.h
new file mode 100644
index 0000000..d677876
--- /dev/null
+++ b/tools/lint/configuration.h
@@ -0,0 +1,36 @@
+/**
+ * @file configuration.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief yanglint configuration header
+ *
+ * Copyright (c) 2017 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef CONFIGURATION_H_
+#define CONFIGURATION_H_
+
+/**
+ * @brief Finds the current user's yanglint dir
+ * @return NULL on failure, dynamically allocated yanglint dir path
+ * otherwise
+ */
+char *get_yanglint_dir(void);
+
+/**
+ * @brief Checks all the relevant files and directories creating any
+ * that are missing, sets the saved configuration (currently only history)
+ */
+void load_config(void);
+
+/**
+ * @brief Saves the current configuration (currently only history)
+ */
+void store_config(void);
+
+#endif /* CONFIGURATION_H_ */
diff --git a/tools/lint/examples/README.md b/tools/lint/examples/README.md
new file mode 100644
index 0000000..604591c
--- /dev/null
+++ b/tools/lint/examples/README.md
@@ -0,0 +1,471 @@
+# YANGLINT - Interactive Mode Examples
+
+This text provides several use-case of the `yanglint(1)` interactive
+mode. For basic information about the `yanglint(1)` usage, please see
+the man page.
+
+The examples are supposed to be went through one by one. Some of the examples
+suppose the specific schemas loaded in some of the previous example is still
+loaded. If an addition work is need, the *preparation* part in the example
+provides information what to do.
+
+To show all available command of the `yanglint(1)`, use the `help` command:
+```
+> help
+Available commands:
+ help Display commands description
+ add Add a new module from a specific file
+ load Load a new schema from the searchdirs
+ print Print a module
+ data Load, validate and optionally print instance data
+ list List all the loaded modules
+ feature Print all features of module(s) with their state
+ searchpath Print/set the search path(s) for schemas
+ clear Clear the context - remove all the loaded modules
+ verb Change verbosity
+ debug Display specific debug message groups
+ quit Quit the program
+ ? Display commands description
+ exit Quit the program
+```
+To show the information about the specific command, use the `help` command in
+combination with the command name you are interested in:
+```
+> help searchpath
+Usage: searchpath [--clear] [<modules-dir-path> ...]
+ Set paths of directories where to search for imports and
+ includes of the schema modules. The current working directory
+ and the path of the module being added is used implicitly.
+ The 'load' command uses these paths to search even for the
+ schema modules to be loaded.
+```
+
+The input files referred in this document are available together with this
+document.
+
+## Duplicit Data Model
+
+Let's have two data models [module1.yang](./module1.yang)
+and [module1b.yang](./module1b.yang).
+They differ in the module name but their namespaces are the same.
+
+Preparation:
+
+```
+> clear
+> add module1.yang
+> list
+```
+
+Output:
+
+```
+List of the loaded models:
+ i ietf-yang-metadata@2016-08-05
+ I yang@2022-06-16
+ i ietf-inet-types@2013-07-15
+ i ietf-yang-types@2013-07-15
+ I ietf-yang-schema-mount@2019-01-14
+ I module1
+```
+
+Command and its output:
+
+```
+> add module1b.yang
+libyang[0]: Two different modules ("module1" and "module1b") have the same namespace "urn:yanglint:module".
+libyang[0]: Parsing module "module1b" failed.
+```
+
+## Yang Data Model Validation
+
+**YANG/YIN syntax**
+
+`module2.yin` contains a syntax error.
+There is a bad syntax of the `type` statement in YIN file.
+
+```
+<type value="string"/>
+```
+
+instead of
+
+```
+<type name="string"/>
+```
+
+Preparation:
+
+```
+> clear
+```
+
+Command and its output:
+
+```
+> add module2.yin
+libyang[0]: Unexpected attribute "value" of "type" element. (path: Line number 8.)
+libyang[0]: Parsing module "module2" failed.
+```
+
+Similarly, there is a typo in `module2.yang`.
+
+**XPath errors**
+
+`libyang` and `yanglint(1)` is able to detect also errors in XPath expressions.
+In `module3.yang` the `must` expression refers to the node which does not exists.
+
+Preparation:
+
+```
+> clear
+```
+
+Command and its output:
+
+```
+> add module3.yang
+libyang[1]: Schema node "a" for parent "/module3:c" not found; in expr "../c/a" with context node "/module3:m".
+```
+
+Note that libyang prints only a warning in this case because it is not
+specified that XPath expressions must refer to existing nodes.
+
+## Data Validation
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+```
+
+**Unknown data**
+
+By default, yanglint ignores unknown data and no error is printed (you can
+compare real content of the `datastore.xml` file and what yanglint prints
+in the following command if you add `-f xml` option).
+
+Command and its output:
+
+```
+> data -t config datastore.xml
+```
+
+We use option `-t` to specify type of the data in `datastore.xml`. By the
+`config` value we declare that the input file contains all the configuration
+data (with at least all the mandatory nodes as required by the loaded schemas),
+but without the status data. More examples of different data types will follow.
+
+Command and its output:
+
+```
+> data -t config datastore.xml
+libyang[0]: No module with namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces" in the context. (path: Line number 20.)
+YANGLINT[E]: Failed to parse input data file "datastore.xml".
+```
+
+Note that in case of working with complete datastore including the status data
+(no `-t` option is specified), `yanglint(1)` has to add status data from its
+internal `ietf-yang-library` module.
+
+**RPC and RPC-reply**
+
+It is possible to validate RPCs and their replies as well.
+
+Peparation:
+
+```
+> clear
+> add module4.yang
+```
+
+Command and its output:
+
+```
+> data -t rpc rpc.xml
+```
+
+Reply to this RPC can be validated too, but it must be nested in the original
+RPC element.
+
+Command and its output:
+
+```
+> data -t reply ../tools/lint/examples/rpc-reply.xml
+```
+
+**action and action-reply**
+
+Actions are validated the same way as RPCs except you need to be careful
+about the input file structure. No NETCONF-specific envelopes are expected.
+
+Preparation
+
+```
+> clear
+> add module4.yang
+```
+
+Command and its output:
+
+```
+> data -t rpc action.xml
+```
+
+Command and its output:
+
+```
+> data -t rpc action-reply.xml action.xml
+```
+
+**notification**
+
+Both top-level and nested notification can be validated.
+
+Preparation
+
+```
+> clear
+> add module4.yang
+```
+
+Command and its output:
+
+```
+> data -t notif notification.xml
+```
+
+Command and its output:
+
+```
+> data -t notif nested-notification.xml
+```
+
+
+**Multiple top-level elements in a single document**
+
+As a feature and in conflict with the XML definition, `yanglint(1)` (and libyang)
+is able to read XML files with multiple top-level elements. Such documents
+are not well-formed according to the XML spec, but it fits to how the YANG
+interconnects data trees (defined as top-level elements of a single schema
+or by multiple schemas).
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+> add ietf-interfaces.yang
+> add ietf-ip.yang
+> add iana-if-type.yang
+```
+
+Command and its output:
+
+```
+> data -t config datastore.xml
+```
+
+**Different data content types**
+
+Since NETCONF requires the data described by YANG to be used in different
+situations (e.g. as <edit-config data>, result of the <get> with status data
+included or as a result of the <get-config> without the status data and
+possibly filtered, so without specified subtrees), it must be possible to
+specify which kind of data is going to be parsed. In `yanglint(1)`, this is done
+via `-t` option. The list of supported modes can be displayed by the `-h`
+option given to the `data` command. In general, the `auto` value lets the
+`yanglint(1)` to recognize the data type automatically by the additional top-level
+elements added to the parsed data. This is the same way as `pyang(1)` uses. Note,
+that the automatic data type recognition is available only for the XML input.
+
+**Malformed XML data**
+
+Command and its output:
+
+```
+> data -t edit config-missing-key.xml
+libyang[0]: Node "nam" not found as a child of "group" node. (path: Schema location "/ietf-netconf-acm:nacm/groups/group", data location "/ietf-netconf-acm:group", line number 19.)
+YANGLINT[E]: Failed to parse input data file "config-missing-key.xml".
+```
+
+**State information in edit-config XML**
+
+Command and its output:
+
+```
+> data -t edit config-unknown-element.xml
+libyang[0]: Unexpected data state node "denied-operations" found. (path: Schema location "/ietf-netconf-acm:nacm/denied-operations", data location "/ietf-netconf-acm:nacm", line number 24.)
+YANGLINT[E]: Failed to parse input data file "config-unknown-element.xml".
+```
+
+**Missing required element in NETCONF data**
+
+Command and its output:
+
+```
+> data data-missing-key.xml
+libyang[0]: List instance is missing its key "name". (path: Schema location "/ietf-netconf-acm:nacm/rule-list/rule", data location "/ietf-netconf-acm:rule", line number 10.)
+YANGLINT[E]: Failed to parse input data file "data-missing-key.xml".
+```
+
+**Malformed XML**
+
+Command and its output:
+
+```
+> data data-malformed-xml.xml
+libyang[0]: Node "nam" not found as a child of "rule" node. (path: Schema location "/ietf-netconf-acm:nacm/rule-list/rule", data location "/ietf-netconf-acm:rule", line number 8.)
+YANGLINT[E]: Failed to parse input data file "data-malformed-xml.xml".
+```
+
+Command and its output:
+
+```
+> data data-malformed-xml2.xml
+libyang[0]: Child element "module-name" inside a terminal node "name" found. (path: Schema location "/ietf-netconf-acm:nacm/rule-list/rule/name", data location "/ietf-netconf-acm:name", line number 7.)
+YANGLINT[E]: Failed to parse input data file "data-malformed-xml2.xml".
+```
+
+**Bad value**
+
+Command and its output:
+
+```
+> data data-out-of-range-value.xml
+libyang[0]: Value "-1" is out of type uint32 min/max bounds. (path: Schema location "/ietf-netconf-acm:nacm/denied-operations", data location "/ietf-netconf-acm:nacm", line number 24.)
+YANGLINT[E]: Failed to parse input data file "data-out-of-range-value.xml".
+```
+
+## Validation of "when" Statement in Data
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm-when.yang
+```
+
+**`When` condition is not satisfied since `denied-operation = 0`**
+
+Command and its output:
+
+```
+> data data-acm.xml
+libyang[0]: When condition "../denied-operations > 0" not satisfied. (path: Schema location "/ietf-netconf-acm-when:nacm/denied-data-writes", data location "/ietf-netconf-acm-when:nacm/denied-data-writes".)
+YANGLINT[E]: Failed to parse input data file "data-acm.xml".
+```
+
+## Printing a Data Model
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+```
+
+**Print a `pyang`-style tree**
+
+Command and its output:
+
+```
+> print ietf-netconf-acm
+module: ietf-netconf-acm
+ +--rw nacm
+ +--rw enable-nacm? boolean
+ +--rw read-default? action-type
+ +--rw write-default? action-type
+ +--rw exec-default? action-type
+ +--rw enable-external-groups? boolean
+ +--ro denied-operations yang:zero-based-counter32
+ +--ro denied-data-writes yang:zero-based-counter32
+ +--ro denied-notifications yang:zero-based-counter32
+ +--rw groups
+ | +--rw group* [name]
+ | +--rw name group-name-type
+ | +--rw user-name* user-name-type
+ +--rw rule-list* [name]
+ +--rw name string
+ +--rw group* union
+ +--rw rule* [name]
+ +--rw name string
+ +--rw module-name? union
+ +--rw (rule-type)?
+ | +--:(protocol-operation)
+ | | +--rw rpc-name? union
+ | +--:(notification)
+ | | +--rw notification-name? union
+ | +--:(data-node)
+ | +--rw path node-instance-identifier
+ +--rw access-operations? union
+ +--rw action action-type
+ +--rw comment? string
+```
+
+**Print information about specific model part**
+
+Command and its output:
+
+```
+> print -f info -P /ietf-netconf-acm:nacm/ietf-netconf-acm:enable-nacm ietf-netconf-acm
+leaf enable-nacm {
+ ietf-netconf-acm:default-deny-all;
+ type boolean;
+ default "true";
+ config true;
+ status current;
+ description
+ "Enables or disables all NETCONF access control
+ enforcement. If 'true', then enforcement
+ is enabled. If 'false', then enforcement
+ is disabled.";
+}
+```
+
+## Usage of `feature` in Yang
+
+Preparation:
+
+```
+> clear
+> add ietf-interfaces.yang
+> add ietf-ip.yang -F ietf-ip:*
+> add iana-if-type.yang
+```
+
+Note: This example also shows `JSON` output of the command.
+
+Command and its output:
+```
+> feature ietf-ip
+ietf-ip features:
+ ipv4-non-contiguous-netmasks (on)
+ ipv6-privacy-autoconf (on)
+> data -f json -t config data-ip.xml
+{
+ "ietf-interfaces:interfaces": {
+ "interface": [
+ {
+ "name": "eth0",
+ "description": "Wire Connection",
+ "type": "iana-if-type:ethernetCsmacd",
+ "enabled": true,
+ "ietf-ip:ipv4": {
+ "address": [
+ {
+ "ip": "192.168.1.15",
+ "netmask": "255.255.255.0"
+ },
+ {
+ "ip": "192.168.1.10",
+ "netmask": "255.255.255.0"
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+```
diff --git a/tools/lint/examples/action-reply.xml b/tools/lint/examples/action-reply.xml
new file mode 100644
index 0000000..e6fc284
--- /dev/null
+++ b/tools/lint/examples/action-reply.xml
@@ -0,0 +1,8 @@
+<cont1 xmlns="urn:module4">
+ <list>
+ <leaf1>key_val</leaf1>
+ <act>
+ <leaf3>some_output</leaf3>
+ </act>
+ </list>
+</cont1>
diff --git a/tools/lint/examples/action.xml b/tools/lint/examples/action.xml
new file mode 100644
index 0000000..661fecf
--- /dev/null
+++ b/tools/lint/examples/action.xml
@@ -0,0 +1,8 @@
+<cont1 xmlns="urn:module4">
+ <list>
+ <leaf1>key_val</leaf1>
+ <act>
+ <leaf2>some_input</leaf2>
+ </act>
+ </list>
+</cont1>
diff --git a/tools/lint/examples/config-acm.xml b/tools/lint/examples/config-acm.xml
new file mode 100644
index 0000000..8c99419
--- /dev/null
+++ b/tools/lint/examples/config-acm.xml
@@ -0,0 +1,24 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group nc:operation="create">test</group>
+ <rule>
+ <name>almighty</name>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+</nacm>
diff --git a/tools/lint/examples/config-missing-key.xml b/tools/lint/examples/config-missing-key.xml
new file mode 100644
index 0000000..c30c2b0
--- /dev/null
+++ b/tools/lint/examples/config-missing-key.xml
@@ -0,0 +1,24 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <name>almighty</name>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <nam>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+</nacm>
diff --git a/tools/lint/examples/config-unknown-element.xml b/tools/lint/examples/config-unknown-element.xml
new file mode 100644
index 0000000..66ae880
--- /dev/null
+++ b/tools/lint/examples/config-unknown-element.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <name>almighty</name>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+ <denied-operations>0</denied-operations>
+ <denied-data-writes>0</denied-data-writes>
+ <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-acm.xml b/tools/lint/examples/data-acm.xml
new file mode 100644
index 0000000..66ae880
--- /dev/null
+++ b/tools/lint/examples/data-acm.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <name>almighty</name>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+ <denied-operations>0</denied-operations>
+ <denied-data-writes>0</denied-data-writes>
+ <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-ip.xml b/tools/lint/examples/data-ip.xml
new file mode 100644
index 0000000..1894f6d
--- /dev/null
+++ b/tools/lint/examples/data-ip.xml
@@ -0,0 +1,12 @@
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+ <interface>
+ <name>eth0</name>
+ <description>Wire Connection</description>
+ <type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
+ <enabled>true</enabled>
+ <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
+ <address><ip>192.168.1.15</ip><netmask>255.255.255.0</netmask></address>
+ <address><ip>192.168.1.10</ip><netmask>255.255.255.0</netmask></address>
+ </ipv4>
+ </interface>
+</interfaces>
diff --git a/tools/lint/examples/data-malformed-xml.xml b/tools/lint/examples/data-malformed-xml.xml
new file mode 100644
index 0000000..908d79b
--- /dev/null
+++ b/tools/lint/examples/data-malformed-xml.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <nam>almighty
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+ <denied-operations>0</denied-operations>
+ <denied-data-writes>0</denied-data-writes>
+ <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-malformed-xml2.xml b/tools/lint/examples/data-malformed-xml2.xml
new file mode 100644
index 0000000..8d0e5f4
--- /dev/null
+++ b/tools/lint/examples/data-malformed-xml2.xml
@@ -0,0 +1,26 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <name>almighty<module-name></name> *</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+ <denied-operations>0</denied-operations>
+ <denied-data-writes>0</denied-data-writes>
+ <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-missing-key.xml b/tools/lint/examples/data-missing-key.xml
new file mode 100644
index 0000000..2e9684d
--- /dev/null
+++ b/tools/lint/examples/data-missing-key.xml
@@ -0,0 +1,26 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+ <denied-operations>0</denied-operations>
+ <denied-data-writes>0</denied-data-writes>
+ <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-out-of-range-value.xml b/tools/lint/examples/data-out-of-range-value.xml
new file mode 100644
index 0000000..2af5ba9
--- /dev/null
+++ b/tools/lint/examples/data-out-of-range-value.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <group>test</group>
+ <rule>
+ <name>almighty</name>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>test</name>
+ <user-name>smith</user-name>
+ </group>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ <user-name>doe</user-name>
+ </group>
+ </groups>
+ <denied-operations>-1</denied-operations>
+ <denied-data-writes>0</denied-data-writes>
+ <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/datastore.xml b/tools/lint/examples/datastore.xml
new file mode 100644
index 0000000..c6a6fc9
--- /dev/null
+++ b/tools/lint/examples/datastore.xml
@@ -0,0 +1,29 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+ <rule-list>
+ <name>almighty</name>
+ <group>almighty</group>
+ <rule>
+ <name>almighty</name>
+ <module-name>*</module-name>
+ <access-operations>*</access-operations>
+ <action>permit</action>
+ </rule>
+ </rule-list>
+ <groups>
+ <group>
+ <name>almighty</name>
+ <user-name>smith</user-name>
+ </group>
+ </groups>
+</nacm>
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+ <interface>
+ <name>eth0</name>
+ <description>Wire Connection</description>
+ <type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
+ <enabled>true</enabled>
+ <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
+ <address><ip>192.168.1.15</ip><prefix-length>24</prefix-length></address>
+ </ipv4>
+ </interface>
+</interfaces>
diff --git a/tools/lint/examples/iana-if-type.yang b/tools/lint/examples/iana-if-type.yang
new file mode 100644
index 0000000..5dd8219
--- /dev/null
+++ b/tools/lint/examples/iana-if-type.yang
@@ -0,0 +1,1547 @@
+module iana-if-type {
+ namespace "urn:ietf:params:xml:ns:yang:iana-if-type";
+ prefix ianaift;
+
+ import ietf-interfaces {
+ prefix if;
+ }
+
+ organization "IANA";
+ contact
+ " Internet Assigned Numbers Authority
+
+ Postal: ICANN
+ 4676 Admiralty Way, Suite 330
+ Marina del Rey, CA 90292
+
+ Tel: +1 310 823 9358
+ <mailto:iana@iana.org>";
+ description
+ "This YANG module defines YANG identities for IANA-registered
+ interface types.
+
+ This YANG module is maintained by IANA and reflects the
+ 'ifType definitions' registry.
+
+ The latest revision of this YANG module can be obtained from
+ the IANA web site.
+
+ Requests for new values should be made to IANA via
+ email (iana@iana.org).
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ The initial version of this YANG module is part of RFC 7224;
+ see the RFC itself for full legal notices.";
+ reference
+ "IANA 'ifType definitions' registry.
+ <http://www.iana.org/assignments/smi-numbers>";
+
+ revision 2014-05-08 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7224: IANA Interface Type YANG Module";
+ }
+
+ identity iana-interface-type {
+ base if:interface-type;
+ description
+ "This identity is used as a base for all interface types
+ defined in the 'ifType definitions' registry.";
+ }
+
+
+
+
+
+
+ identity other {
+ base iana-interface-type;
+ }
+ identity regular1822 {
+ base iana-interface-type;
+ }
+ identity hdh1822 {
+ base iana-interface-type;
+ }
+ identity ddnX25 {
+ base iana-interface-type;
+ }
+ identity rfc877x25 {
+ base iana-interface-type;
+ reference
+ "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer";
+ }
+ identity ethernetCsmacd {
+ base iana-interface-type;
+ description
+ "For all Ethernet-like interfaces, regardless of speed,
+ as per RFC 3635.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity iso88023Csmacd {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Deprecated via RFC 3635.
+ Use ethernetCsmacd(6) instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity iso88024TokenBus {
+ base iana-interface-type;
+ }
+ identity iso88025TokenRing {
+ base iana-interface-type;
+ }
+ identity iso88026Man {
+ base iana-interface-type;
+ }
+ identity starLan {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Deprecated via RFC 3635.
+ Use ethernetCsmacd(6) instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity proteon10Mbit {
+ base iana-interface-type;
+ }
+ identity proteon80Mbit {
+ base iana-interface-type;
+ }
+ identity hyperchannel {
+ base iana-interface-type;
+ }
+ identity fddi {
+ base iana-interface-type;
+ reference
+ "RFC 1512 - FDDI Management Information Base";
+ }
+ identity lapb {
+ base iana-interface-type;
+ reference
+ "RFC 1381 - SNMP MIB Extension for X.25 LAPB";
+ }
+ identity sdlc {
+ base iana-interface-type;
+ }
+ identity ds1 {
+ base iana-interface-type;
+ description
+ "DS1-MIB.";
+ reference
+ "RFC 4805 - Definitions of Managed Objects for the
+ DS1, J1, E1, DS2, and E2 Interface Types";
+ }
+ identity e1 {
+ base iana-interface-type;
+ status obsolete;
+ description
+ "Obsolete; see DS1-MIB.";
+ reference
+ "RFC 4805 - Definitions of Managed Objects for the
+ DS1, J1, E1, DS2, and E2 Interface Types";
+ }
+
+
+ identity basicISDN {
+ base iana-interface-type;
+ description
+ "No longer used. See also RFC 2127.";
+ }
+ identity primaryISDN {
+ base iana-interface-type;
+ description
+ "No longer used. See also RFC 2127.";
+ }
+ identity propPointToPointSerial {
+ base iana-interface-type;
+ description
+ "Proprietary serial.";
+ }
+ identity ppp {
+ base iana-interface-type;
+ }
+ identity softwareLoopback {
+ base iana-interface-type;
+ }
+ identity eon {
+ base iana-interface-type;
+ description
+ "CLNP over IP.";
+ }
+ identity ethernet3Mbit {
+ base iana-interface-type;
+ }
+ identity nsip {
+ base iana-interface-type;
+ description
+ "XNS over IP.";
+ }
+ identity slip {
+ base iana-interface-type;
+ description
+ "Generic SLIP.";
+ }
+ identity ultra {
+ base iana-interface-type;
+ description
+ "Ultra Technologies.";
+ }
+ identity ds3 {
+ base iana-interface-type;
+ description
+ "DS3-MIB.";
+ reference
+ "RFC 3896 - Definitions of Managed Objects for the
+ DS3/E3 Interface Type";
+ }
+ identity sip {
+ base iana-interface-type;
+ description
+ "SMDS, coffee.";
+ reference
+ "RFC 1694 - Definitions of Managed Objects for SMDS
+ Interfaces using SMIv2";
+ }
+ identity frameRelay {
+ base iana-interface-type;
+ description
+ "DTE only.";
+ reference
+ "RFC 2115 - Management Information Base for Frame Relay
+ DTEs Using SMIv2";
+ }
+ identity rs232 {
+ base iana-interface-type;
+ reference
+ "RFC 1659 - Definitions of Managed Objects for RS-232-like
+ Hardware Devices using SMIv2";
+ }
+ identity para {
+ base iana-interface-type;
+ description
+ "Parallel-port.";
+ reference
+ "RFC 1660 - Definitions of Managed Objects for
+ Parallel-printer-like Hardware Devices using
+ SMIv2";
+ }
+ identity arcnet {
+ base iana-interface-type;
+ description
+ "ARCnet.";
+ }
+ identity arcnetPlus {
+ base iana-interface-type;
+ description
+ "ARCnet Plus.";
+ }
+
+
+
+ identity atm {
+ base iana-interface-type;
+ description
+ "ATM cells.";
+ }
+ identity miox25 {
+ base iana-interface-type;
+ reference
+ "RFC 1461 - SNMP MIB extension for Multiprotocol
+ Interconnect over X.25";
+ }
+ identity sonet {
+ base iana-interface-type;
+ description
+ "SONET or SDH.";
+ }
+ identity x25ple {
+ base iana-interface-type;
+ reference
+ "RFC 2127 - ISDN Management Information Base using SMIv2";
+ }
+ identity iso88022llc {
+ base iana-interface-type;
+ }
+ identity localTalk {
+ base iana-interface-type;
+ }
+ identity smdsDxi {
+ base iana-interface-type;
+ }
+ identity frameRelayService {
+ base iana-interface-type;
+ description
+ "FRNETSERV-MIB.";
+ reference
+ "RFC 2954 - Definitions of Managed Objects for Frame
+ Relay Service";
+ }
+ identity v35 {
+ base iana-interface-type;
+ }
+ identity hssi {
+ base iana-interface-type;
+ }
+ identity hippi {
+ base iana-interface-type;
+ }
+
+ identity modem {
+ base iana-interface-type;
+ description
+ "Generic modem.";
+ }
+ identity aal5 {
+ base iana-interface-type;
+ description
+ "AAL5 over ATM.";
+ }
+ identity sonetPath {
+ base iana-interface-type;
+ }
+ identity sonetVT {
+ base iana-interface-type;
+ }
+ identity smdsIcip {
+ base iana-interface-type;
+ description
+ "SMDS InterCarrier Interface.";
+ }
+ identity propVirtual {
+ base iana-interface-type;
+ description
+ "Proprietary virtual/internal.";
+ reference
+ "RFC 2863 - The Interfaces Group MIB";
+ }
+ identity propMultiplexor {
+ base iana-interface-type;
+ description
+ "Proprietary multiplexing.";
+ reference
+ "RFC 2863 - The Interfaces Group MIB";
+ }
+ identity ieee80212 {
+ base iana-interface-type;
+ description
+ "100BaseVG.";
+ }
+ identity fibreChannel {
+ base iana-interface-type;
+ description
+ "Fibre Channel.";
+ }
+
+
+
+ identity hippiInterface {
+ base iana-interface-type;
+ description
+ "HIPPI interfaces.";
+ }
+ identity frameRelayInterconnect {
+ base iana-interface-type;
+ status obsolete;
+ description
+ "Obsolete; use either
+ frameRelay(32) or frameRelayService(44).";
+ }
+ identity aflane8023 {
+ base iana-interface-type;
+ description
+ "ATM Emulated LAN for 802.3.";
+ }
+ identity aflane8025 {
+ base iana-interface-type;
+ description
+ "ATM Emulated LAN for 802.5.";
+ }
+ identity cctEmul {
+ base iana-interface-type;
+ description
+ "ATM Emulated circuit.";
+ }
+ identity fastEther {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Obsoleted via RFC 3635.
+ ethernetCsmacd(6) should be used instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity isdn {
+ base iana-interface-type;
+ description
+ "ISDN and X.25.";
+ reference
+ "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN
+ in the Packet Mode";
+ }
+
+
+
+ identity v11 {
+ base iana-interface-type;
+ description
+ "CCITT V.11/X.21.";
+ }
+ identity v36 {
+ base iana-interface-type;
+ description
+ "CCITT V.36.";
+ }
+ identity g703at64k {
+ base iana-interface-type;
+ description
+ "CCITT G703 at 64Kbps.";
+ }
+ identity g703at2mb {
+ base iana-interface-type;
+ status obsolete;
+ description
+ "Obsolete; see DS1-MIB.";
+ }
+ identity qllc {
+ base iana-interface-type;
+ description
+ "SNA QLLC.";
+ }
+ identity fastEtherFX {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Obsoleted via RFC 3635.
+ ethernetCsmacd(6) should be used instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity channel {
+ base iana-interface-type;
+ description
+ "Channel.";
+ }
+ identity ieee80211 {
+ base iana-interface-type;
+ description
+ "Radio spread spectrum.";
+ }
+ identity ibm370parChan {
+ base iana-interface-type;
+ description
+ "IBM System 360/370 OEMI Channel.";
+ }
+ identity escon {
+ base iana-interface-type;
+ description
+ "IBM Enterprise Systems Connection.";
+ }
+ identity dlsw {
+ base iana-interface-type;
+ description
+ "Data Link Switching.";
+ }
+ identity isdns {
+ base iana-interface-type;
+ description
+ "ISDN S/T interface.";
+ }
+ identity isdnu {
+ base iana-interface-type;
+ description
+ "ISDN U interface.";
+ }
+ identity lapd {
+ base iana-interface-type;
+ description
+ "Link Access Protocol D.";
+ }
+ identity ipSwitch {
+ base iana-interface-type;
+ description
+ "IP Switching Objects.";
+ }
+ identity rsrb {
+ base iana-interface-type;
+ description
+ "Remote Source Route Bridging.";
+ }
+ identity atmLogical {
+ base iana-interface-type;
+ description
+ "ATM Logical Port.";
+ reference
+ "RFC 3606 - Definitions of Supplemental Managed Objects
+ for ATM Interface";
+ }
+ identity ds0 {
+ base iana-interface-type;
+ description
+ "Digital Signal Level 0.";
+ reference
+ "RFC 2494 - Definitions of Managed Objects for the DS0
+ and DS0 Bundle Interface Type";
+ }
+ identity ds0Bundle {
+ base iana-interface-type;
+ description
+ "Group of ds0s on the same ds1.";
+ reference
+ "RFC 2494 - Definitions of Managed Objects for the DS0
+ and DS0 Bundle Interface Type";
+ }
+ identity bsc {
+ base iana-interface-type;
+ description
+ "Bisynchronous Protocol.";
+ }
+ identity async {
+ base iana-interface-type;
+ description
+ "Asynchronous Protocol.";
+ }
+ identity cnr {
+ base iana-interface-type;
+ description
+ "Combat Net Radio.";
+ }
+ identity iso88025Dtr {
+ base iana-interface-type;
+ description
+ "ISO 802.5r DTR.";
+ }
+ identity eplrs {
+ base iana-interface-type;
+ description
+ "Ext Pos Loc Report Sys.";
+ }
+ identity arap {
+ base iana-interface-type;
+ description
+ "Appletalk Remote Access Protocol.";
+ }
+ identity propCnls {
+ base iana-interface-type;
+ description
+ "Proprietary Connectionless Protocol.";
+ }
+ identity hostPad {
+ base iana-interface-type;
+ description
+ "CCITT-ITU X.29 PAD Protocol.";
+ }
+ identity termPad {
+ base iana-interface-type;
+ description
+ "CCITT-ITU X.3 PAD Facility.";
+ }
+ identity frameRelayMPI {
+ base iana-interface-type;
+ description
+ "Multiproto Interconnect over FR.";
+ }
+ identity x213 {
+ base iana-interface-type;
+ description
+ "CCITT-ITU X213.";
+ }
+ identity adsl {
+ base iana-interface-type;
+ description
+ "Asymmetric Digital Subscriber Loop.";
+ }
+ identity radsl {
+ base iana-interface-type;
+ description
+ "Rate-Adapt. Digital Subscriber Loop.";
+ }
+ identity sdsl {
+ base iana-interface-type;
+ description
+ "Symmetric Digital Subscriber Loop.";
+ }
+ identity vdsl {
+ base iana-interface-type;
+ description
+ "Very H-Speed Digital Subscrib. Loop.";
+ }
+ identity iso88025CRFPInt {
+ base iana-interface-type;
+ description
+ "ISO 802.5 CRFP.";
+ }
+ identity myrinet {
+ base iana-interface-type;
+ description
+ "Myricom Myrinet.";
+ }
+ identity voiceEM {
+ base iana-interface-type;
+ description
+ "Voice recEive and transMit.";
+ }
+ identity voiceFXO {
+ base iana-interface-type;
+ description
+ "Voice Foreign Exchange Office.";
+ }
+ identity voiceFXS {
+ base iana-interface-type;
+ description
+ "Voice Foreign Exchange Station.";
+ }
+ identity voiceEncap {
+ base iana-interface-type;
+ description
+ "Voice encapsulation.";
+ }
+ identity voiceOverIp {
+ base iana-interface-type;
+ description
+ "Voice over IP encapsulation.";
+ }
+ identity atmDxi {
+ base iana-interface-type;
+ description
+ "ATM DXI.";
+ }
+ identity atmFuni {
+ base iana-interface-type;
+ description
+ "ATM FUNI.";
+ }
+ identity atmIma {
+ base iana-interface-type;
+ description
+ "ATM IMA.";
+ }
+ identity pppMultilinkBundle {
+ base iana-interface-type;
+ description
+ "PPP Multilink Bundle.";
+ }
+ identity ipOverCdlc {
+ base iana-interface-type;
+ description
+ "IBM ipOverCdlc.";
+ }
+ identity ipOverClaw {
+ base iana-interface-type;
+ description
+ "IBM Common Link Access to Workstn.";
+ }
+ identity stackToStack {
+ base iana-interface-type;
+ description
+ "IBM stackToStack.";
+ }
+ identity virtualIpAddress {
+ base iana-interface-type;
+ description
+ "IBM VIPA.";
+ }
+ identity mpc {
+ base iana-interface-type;
+ description
+ "IBM multi-protocol channel support.";
+ }
+ identity ipOverAtm {
+ base iana-interface-type;
+ description
+ "IBM ipOverAtm.";
+ reference
+ "RFC 2320 - Definitions of Managed Objects for Classical IP
+ and ARP Over ATM Using SMIv2 (IPOA-MIB)";
+ }
+ identity iso88025Fiber {
+ base iana-interface-type;
+ description
+ "ISO 802.5j Fiber Token Ring.";
+ }
+ identity tdlc {
+ base iana-interface-type;
+ description
+ "IBM twinaxial data link control.";
+ }
+ identity gigabitEthernet {
+ base iana-interface-type;
+ status deprecated;
+
+
+ description
+ "Obsoleted via RFC 3635.
+ ethernetCsmacd(6) should be used instead.";
+ reference
+ "RFC 3635 - Definitions of Managed Objects for the
+ Ethernet-like Interface Types";
+ }
+ identity hdlc {
+ base iana-interface-type;
+ description
+ "HDLC.";
+ }
+ identity lapf {
+ base iana-interface-type;
+ description
+ "LAP F.";
+ }
+ identity v37 {
+ base iana-interface-type;
+ description
+ "V.37.";
+ }
+ identity x25mlp {
+ base iana-interface-type;
+ description
+ "Multi-Link Protocol.";
+ }
+ identity x25huntGroup {
+ base iana-interface-type;
+ description
+ "X25 Hunt Group.";
+ }
+ identity transpHdlc {
+ base iana-interface-type;
+ description
+ "Transp HDLC.";
+ }
+ identity interleave {
+ base iana-interface-type;
+ description
+ "Interleave channel.";
+ }
+ identity fast {
+ base iana-interface-type;
+ description
+ "Fast channel.";
+ }
+
+ identity ip {
+ base iana-interface-type;
+ description
+ "IP (for APPN HPR in IP networks).";
+ }
+ identity docsCableMaclayer {
+ base iana-interface-type;
+ description
+ "CATV Mac Layer.";
+ }
+ identity docsCableDownstream {
+ base iana-interface-type;
+ description
+ "CATV Downstream interface.";
+ }
+ identity docsCableUpstream {
+ base iana-interface-type;
+ description
+ "CATV Upstream interface.";
+ }
+ identity a12MppSwitch {
+ base iana-interface-type;
+ description
+ "Avalon Parallel Processor.";
+ }
+ identity tunnel {
+ base iana-interface-type;
+ description
+ "Encapsulation interface.";
+ }
+ identity coffee {
+ base iana-interface-type;
+ description
+ "Coffee pot.";
+ reference
+ "RFC 2325 - Coffee MIB";
+ }
+ identity ces {
+ base iana-interface-type;
+ description
+ "Circuit Emulation Service.";
+ }
+ identity atmSubInterface {
+ base iana-interface-type;
+ description
+ "ATM Sub Interface.";
+ }
+
+ identity l2vlan {
+ base iana-interface-type;
+ description
+ "Layer 2 Virtual LAN using 802.1Q.";
+ }
+ identity l3ipvlan {
+ base iana-interface-type;
+ description
+ "Layer 3 Virtual LAN using IP.";
+ }
+ identity l3ipxvlan {
+ base iana-interface-type;
+ description
+ "Layer 3 Virtual LAN using IPX.";
+ }
+ identity digitalPowerline {
+ base iana-interface-type;
+ description
+ "IP over Power Lines.";
+ }
+ identity mediaMailOverIp {
+ base iana-interface-type;
+ description
+ "Multimedia Mail over IP.";
+ }
+ identity dtm {
+ base iana-interface-type;
+ description
+ "Dynamic synchronous Transfer Mode.";
+ }
+ identity dcn {
+ base iana-interface-type;
+ description
+ "Data Communications Network.";
+ }
+ identity ipForward {
+ base iana-interface-type;
+ description
+ "IP Forwarding Interface.";
+ }
+ identity msdsl {
+ base iana-interface-type;
+ description
+ "Multi-rate Symmetric DSL.";
+ }
+ identity ieee1394 {
+ base iana-interface-type;
+
+ description
+ "IEEE1394 High Performance Serial Bus.";
+ }
+ identity if-gsn {
+ base iana-interface-type;
+ description
+ "HIPPI-6400.";
+ }
+ identity dvbRccMacLayer {
+ base iana-interface-type;
+ description
+ "DVB-RCC MAC Layer.";
+ }
+ identity dvbRccDownstream {
+ base iana-interface-type;
+ description
+ "DVB-RCC Downstream Channel.";
+ }
+ identity dvbRccUpstream {
+ base iana-interface-type;
+ description
+ "DVB-RCC Upstream Channel.";
+ }
+ identity atmVirtual {
+ base iana-interface-type;
+ description
+ "ATM Virtual Interface.";
+ }
+ identity mplsTunnel {
+ base iana-interface-type;
+ description
+ "MPLS Tunnel Virtual Interface.";
+ }
+ identity srp {
+ base iana-interface-type;
+ description
+ "Spatial Reuse Protocol.";
+ }
+ identity voiceOverAtm {
+ base iana-interface-type;
+ description
+ "Voice over ATM.";
+ }
+ identity voiceOverFrameRelay {
+ base iana-interface-type;
+ description
+ "Voice Over Frame Relay.";
+ }
+ identity idsl {
+ base iana-interface-type;
+ description
+ "Digital Subscriber Loop over ISDN.";
+ }
+ identity compositeLink {
+ base iana-interface-type;
+ description
+ "Avici Composite Link Interface.";
+ }
+ identity ss7SigLink {
+ base iana-interface-type;
+ description
+ "SS7 Signaling Link.";
+ }
+ identity propWirelessP2P {
+ base iana-interface-type;
+ description
+ "Prop. P2P wireless interface.";
+ }
+ identity frForward {
+ base iana-interface-type;
+ description
+ "Frame Forward Interface.";
+ }
+ identity rfc1483 {
+ base iana-interface-type;
+ description
+ "Multiprotocol over ATM AAL5.";
+ reference
+ "RFC 1483 - Multiprotocol Encapsulation over ATM
+ Adaptation Layer 5";
+ }
+ identity usb {
+ base iana-interface-type;
+ description
+ "USB Interface.";
+ }
+ identity ieee8023adLag {
+ base iana-interface-type;
+ description
+ "IEEE 802.3ad Link Aggregate.";
+ }
+ identity bgppolicyaccounting {
+ base iana-interface-type;
+ description
+ "BGP Policy Accounting.";
+ }
+ identity frf16MfrBundle {
+ base iana-interface-type;
+ description
+ "FRF.16 Multilink Frame Relay.";
+ }
+ identity h323Gatekeeper {
+ base iana-interface-type;
+ description
+ "H323 Gatekeeper.";
+ }
+ identity h323Proxy {
+ base iana-interface-type;
+ description
+ "H323 Voice and Video Proxy.";
+ }
+ identity mpls {
+ base iana-interface-type;
+ description
+ "MPLS.";
+ }
+ identity mfSigLink {
+ base iana-interface-type;
+ description
+ "Multi-frequency signaling link.";
+ }
+ identity hdsl2 {
+ base iana-interface-type;
+ description
+ "High Bit-Rate DSL - 2nd generation.";
+ }
+ identity shdsl {
+ base iana-interface-type;
+ description
+ "Multirate HDSL2.";
+ }
+ identity ds1FDL {
+ base iana-interface-type;
+ description
+ "Facility Data Link (4Kbps) on a DS1.";
+ }
+ identity pos {
+ base iana-interface-type;
+ description
+ "Packet over SONET/SDH Interface.";
+ }
+
+
+
+ identity dvbAsiIn {
+ base iana-interface-type;
+ description
+ "DVB-ASI Input.";
+ }
+ identity dvbAsiOut {
+ base iana-interface-type;
+ description
+ "DVB-ASI Output.";
+ }
+ identity plc {
+ base iana-interface-type;
+ description
+ "Power Line Communications.";
+ }
+ identity nfas {
+ base iana-interface-type;
+ description
+ "Non-Facility Associated Signaling.";
+ }
+ identity tr008 {
+ base iana-interface-type;
+ description
+ "TR008.";
+ }
+ identity gr303RDT {
+ base iana-interface-type;
+ description
+ "Remote Digital Terminal.";
+ }
+ identity gr303IDT {
+ base iana-interface-type;
+ description
+ "Integrated Digital Terminal.";
+ }
+ identity isup {
+ base iana-interface-type;
+ description
+ "ISUP.";
+ }
+ identity propDocsWirelessMaclayer {
+ base iana-interface-type;
+ description
+ "Cisco proprietary Maclayer.";
+ }
+
+
+
+ identity propDocsWirelessDownstream {
+ base iana-interface-type;
+ description
+ "Cisco proprietary Downstream.";
+ }
+ identity propDocsWirelessUpstream {
+ base iana-interface-type;
+ description
+ "Cisco proprietary Upstream.";
+ }
+ identity hiperlan2 {
+ base iana-interface-type;
+ description
+ "HIPERLAN Type 2 Radio Interface.";
+ }
+ identity propBWAp2Mp {
+ base iana-interface-type;
+ description
+ "PropBroadbandWirelessAccesspt2Multipt (use of this value
+ for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f
+ is deprecated, and ieee80216WMAN(237) should be used
+ instead).";
+ }
+ identity sonetOverheadChannel {
+ base iana-interface-type;
+ description
+ "SONET Overhead Channel.";
+ }
+ identity digitalWrapperOverheadChannel {
+ base iana-interface-type;
+ description
+ "Digital Wrapper.";
+ }
+ identity aal2 {
+ base iana-interface-type;
+ description
+ "ATM adaptation layer 2.";
+ }
+ identity radioMAC {
+ base iana-interface-type;
+ description
+ "MAC layer over radio links.";
+ }
+ identity atmRadio {
+ base iana-interface-type;
+ description
+ "ATM over radio links.";
+ }
+ identity imt {
+ base iana-interface-type;
+ description
+ "Inter-Machine Trunks.";
+ }
+ identity mvl {
+ base iana-interface-type;
+ description
+ "Multiple Virtual Lines DSL.";
+ }
+ identity reachDSL {
+ base iana-interface-type;
+ description
+ "Long Reach DSL.";
+ }
+ identity frDlciEndPt {
+ base iana-interface-type;
+ description
+ "Frame Relay DLCI End Point.";
+ }
+ identity atmVciEndPt {
+ base iana-interface-type;
+ description
+ "ATM VCI End Point.";
+ }
+ identity opticalChannel {
+ base iana-interface-type;
+ description
+ "Optical Channel.";
+ }
+ identity opticalTransport {
+ base iana-interface-type;
+ description
+ "Optical Transport.";
+ }
+ identity propAtm {
+ base iana-interface-type;
+ description
+ "Proprietary ATM.";
+ }
+ identity voiceOverCable {
+ base iana-interface-type;
+ description
+ "Voice Over Cable Interface.";
+ }
+
+
+
+ identity infiniband {
+ base iana-interface-type;
+ description
+ "Infiniband.";
+ }
+ identity teLink {
+ base iana-interface-type;
+ description
+ "TE Link.";
+ }
+ identity q2931 {
+ base iana-interface-type;
+ description
+ "Q.2931.";
+ }
+ identity virtualTg {
+ base iana-interface-type;
+ description
+ "Virtual Trunk Group.";
+ }
+ identity sipTg {
+ base iana-interface-type;
+ description
+ "SIP Trunk Group.";
+ }
+ identity sipSig {
+ base iana-interface-type;
+ description
+ "SIP Signaling.";
+ }
+ identity docsCableUpstreamChannel {
+ base iana-interface-type;
+ description
+ "CATV Upstream Channel.";
+ }
+ identity econet {
+ base iana-interface-type;
+ description
+ "Acorn Econet.";
+ }
+ identity pon155 {
+ base iana-interface-type;
+ description
+ "FSAN 155Mb Symetrical PON interface.";
+ }
+
+
+
+ identity pon622 {
+ base iana-interface-type;
+ description
+ "FSAN 622Mb Symetrical PON interface.";
+ }
+ identity bridge {
+ base iana-interface-type;
+ description
+ "Transparent bridge interface.";
+ }
+ identity linegroup {
+ base iana-interface-type;
+ description
+ "Interface common to multiple lines.";
+ }
+ identity voiceEMFGD {
+ base iana-interface-type;
+ description
+ "Voice E&M Feature Group D.";
+ }
+ identity voiceFGDEANA {
+ base iana-interface-type;
+ description
+ "Voice FGD Exchange Access North American.";
+ }
+ identity voiceDID {
+ base iana-interface-type;
+ description
+ "Voice Direct Inward Dialing.";
+ }
+ identity mpegTransport {
+ base iana-interface-type;
+ description
+ "MPEG transport interface.";
+ }
+ identity sixToFour {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "6to4 interface (DEPRECATED).";
+ reference
+ "RFC 4087 - IP Tunnel MIB";
+ }
+ identity gtp {
+ base iana-interface-type;
+ description
+ "GTP (GPRS Tunneling Protocol).";
+ }
+ identity pdnEtherLoop1 {
+ base iana-interface-type;
+ description
+ "Paradyne EtherLoop 1.";
+ }
+ identity pdnEtherLoop2 {
+ base iana-interface-type;
+ description
+ "Paradyne EtherLoop 2.";
+ }
+ identity opticalChannelGroup {
+ base iana-interface-type;
+ description
+ "Optical Channel Group.";
+ }
+ identity homepna {
+ base iana-interface-type;
+ description
+ "HomePNA ITU-T G.989.";
+ }
+ identity gfp {
+ base iana-interface-type;
+ description
+ "Generic Framing Procedure (GFP).";
+ }
+ identity ciscoISLvlan {
+ base iana-interface-type;
+ description
+ "Layer 2 Virtual LAN using Cisco ISL.";
+ }
+ identity actelisMetaLOOP {
+ base iana-interface-type;
+ description
+ "Acteleis proprietary MetaLOOP High Speed Link.";
+ }
+ identity fcipLink {
+ base iana-interface-type;
+ description
+ "FCIP Link.";
+ }
+ identity rpr {
+ base iana-interface-type;
+ description
+ "Resilient Packet Ring Interface Type.";
+ }
+
+
+
+ identity qam {
+ base iana-interface-type;
+ description
+ "RF Qam Interface.";
+ }
+ identity lmp {
+ base iana-interface-type;
+ description
+ "Link Management Protocol.";
+ reference
+ "RFC 4327 - Link Management Protocol (LMP) Management
+ Information Base (MIB)";
+ }
+ identity cblVectaStar {
+ base iana-interface-type;
+ description
+ "Cambridge Broadband Networks Limited VectaStar.";
+ }
+ identity docsCableMCmtsDownstream {
+ base iana-interface-type;
+ description
+ "CATV Modular CMTS Downstream Interface.";
+ }
+ identity adsl2 {
+ base iana-interface-type;
+ status deprecated;
+ description
+ "Asymmetric Digital Subscriber Loop Version 2
+ (DEPRECATED/OBSOLETED - please use adsl2plus(238)
+ instead).";
+ reference
+ "RFC 4706 - Definitions of Managed Objects for Asymmetric
+ Digital Subscriber Line 2 (ADSL2)";
+ }
+ identity macSecControlledIF {
+ base iana-interface-type;
+ description
+ "MACSecControlled.";
+ }
+ identity macSecUncontrolledIF {
+ base iana-interface-type;
+ description
+ "MACSecUncontrolled.";
+ }
+ identity aviciOpticalEther {
+ base iana-interface-type;
+ description
+ "Avici Optical Ethernet Aggregate.";
+ }
+ identity atmbond {
+ base iana-interface-type;
+ description
+ "atmbond.";
+ }
+ identity voiceFGDOS {
+ base iana-interface-type;
+ description
+ "Voice FGD Operator Services.";
+ }
+ identity mocaVersion1 {
+ base iana-interface-type;
+ description
+ "MultiMedia over Coax Alliance (MoCA) Interface
+ as documented in information provided privately to IANA.";
+ }
+ identity ieee80216WMAN {
+ base iana-interface-type;
+ description
+ "IEEE 802.16 WMAN interface.";
+ }
+ identity adsl2plus {
+ base iana-interface-type;
+ description
+ "Asymmetric Digital Subscriber Loop Version 2 -
+ Version 2 Plus and all variants.";
+ }
+ identity dvbRcsMacLayer {
+ base iana-interface-type;
+ description
+ "DVB-RCS MAC Layer.";
+ reference
+ "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+ }
+ identity dvbTdm {
+ base iana-interface-type;
+ description
+ "DVB Satellite TDM.";
+ reference
+ "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+ }
+ identity dvbRcsTdma {
+ base iana-interface-type;
+ description
+ "DVB-RCS TDMA.";
+ reference
+ "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+ }
+ identity x86Laps {
+ base iana-interface-type;
+ description
+ "LAPS based on ITU-T X.86/Y.1323.";
+ }
+ identity wwanPP {
+ base iana-interface-type;
+ description
+ "3GPP WWAN.";
+ }
+ identity wwanPP2 {
+ base iana-interface-type;
+ description
+ "3GPP2 WWAN.";
+ }
+ identity voiceEBS {
+ base iana-interface-type;
+ description
+ "Voice P-phone EBS physical interface.";
+ }
+ identity ifPwType {
+ base iana-interface-type;
+ description
+ "Pseudowire interface type.";
+ reference
+ "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)";
+ }
+ identity ilan {
+ base iana-interface-type;
+ description
+ "Internal LAN on a bridge per IEEE 802.1ap.";
+ }
+ identity pip {
+ base iana-interface-type;
+ description
+ "Provider Instance Port on a bridge per IEEE 802.1ah PBB.";
+ }
+ identity aluELP {
+ base iana-interface-type;
+ description
+ "Alcatel-Lucent Ethernet Link Protection.";
+ }
+ identity gpon {
+ base iana-interface-type;
+ description
+ "Gigabit-capable passive optical networks (G-PON) as per
+ ITU-T G.948.";
+ }
+ identity vdsl2 {
+ base iana-interface-type;
+ description
+ "Very high speed digital subscriber line Version 2
+ (as per ITU-T Recommendation G.993.2).";
+ reference
+ "RFC 5650 - Definitions of Managed Objects for Very High
+ Speed Digital Subscriber Line 2 (VDSL2)";
+ }
+ identity capwapDot11Profile {
+ base iana-interface-type;
+ description
+ "WLAN Profile Interface.";
+ reference
+ "RFC 5834 - Control and Provisioning of Wireless Access
+ Points (CAPWAP) Protocol Binding MIB for
+ IEEE 802.11";
+ }
+ identity capwapDot11Bss {
+ base iana-interface-type;
+ description
+ "WLAN BSS Interface.";
+ reference
+ "RFC 5834 - Control and Provisioning of Wireless Access
+ Points (CAPWAP) Protocol Binding MIB for
+ IEEE 802.11";
+ }
+ identity capwapWtpVirtualRadio {
+ base iana-interface-type;
+ description
+ "WTP Virtual Radio Interface.";
+ reference
+ "RFC 5833 - Control and Provisioning of Wireless Access
+ Points (CAPWAP) Protocol Base MIB";
+ }
+ identity bits {
+ base iana-interface-type;
+ description
+ "bitsport.";
+ }
+ identity docsCableUpstreamRfPort {
+ base iana-interface-type;
+ description
+ "DOCSIS CATV Upstream RF Port.";
+ }
+
+
+ identity cableDownstreamRfPort {
+ base iana-interface-type;
+ description
+ "CATV downstream RF Port.";
+ }
+ identity vmwareVirtualNic {
+ base iana-interface-type;
+ description
+ "VMware Virtual Network Interface.";
+ }
+ identity ieee802154 {
+ base iana-interface-type;
+ description
+ "IEEE 802.15.4 WPAN interface.";
+ reference
+ "IEEE 802.15.4-2006";
+ }
+ identity otnOdu {
+ base iana-interface-type;
+ description
+ "OTN Optical Data Unit.";
+ }
+ identity otnOtu {
+ base iana-interface-type;
+ description
+ "OTN Optical channel Transport Unit.";
+ }
+ identity ifVfiType {
+ base iana-interface-type;
+ description
+ "VPLS Forwarding Instance Interface Type.";
+ }
+ identity g9981 {
+ base iana-interface-type;
+ description
+ "G.998.1 bonded interface.";
+ }
+ identity g9982 {
+ base iana-interface-type;
+ description
+ "G.998.2 bonded interface.";
+ }
+ identity g9983 {
+ base iana-interface-type;
+ description
+ "G.998.3 bonded interface.";
+ }
+
+ identity aluEpon {
+ base iana-interface-type;
+ description
+ "Ethernet Passive Optical Networks (E-PON).";
+ }
+ identity aluEponOnu {
+ base iana-interface-type;
+ description
+ "EPON Optical Network Unit.";
+ }
+ identity aluEponPhysicalUni {
+ base iana-interface-type;
+ description
+ "EPON physical User to Network interface.";
+ }
+ identity aluEponLogicalLink {
+ base iana-interface-type;
+ description
+ "The emulation of a point-to-point link over the EPON
+ layer.";
+ }
+ identity aluGponOnu {
+ base iana-interface-type;
+ description
+ "GPON Optical Network Unit.";
+ reference
+ "ITU-T G.984.2";
+ }
+ identity aluGponPhysicalUni {
+ base iana-interface-type;
+ description
+ "GPON physical User to Network interface.";
+ reference
+ "ITU-T G.984.2";
+ }
+ identity vmwareNicTeam {
+ base iana-interface-type;
+ description
+ "VMware NIC Team.";
+ }
+}
diff --git a/tools/lint/examples/ietf-interfaces.yang b/tools/lint/examples/ietf-interfaces.yang
new file mode 100644
index 0000000..ad64425
--- /dev/null
+++ b/tools/lint/examples/ietf-interfaces.yang
@@ -0,0 +1,725 @@
+module ietf-interfaces {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+ prefix if;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "This module contains a collection of YANG definitions for
+ managing network interfaces.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7223; see
+ the RFC itself for full legal notices.";
+
+ revision 2014-05-08 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7223: A YANG Data Model for Interface Management";
+ }
+
+ /*
+ * Typedefs
+ */
+
+ typedef interface-ref {
+ type leafref {
+ path "/if:interfaces/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ configured interfaces.";
+ }
+
+ typedef interface-state-ref {
+ type leafref {
+ path "/if:interfaces-state/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ the operationally present interfaces.";
+ }
+
+ /*
+ * Identities
+ */
+
+ identity interface-type {
+ description
+ "Base identity from which specific interface types are
+ derived.";
+ }
+
+ /*
+ * Features
+ */
+
+ feature arbitrary-names {
+ description
+ "This feature indicates that the device allows user-controlled
+ interfaces to be named arbitrarily.";
+ }
+ feature pre-provisioning {
+ description
+ "This feature indicates that the device supports
+ pre-provisioning of interface configuration, i.e., it is
+ possible to configure an interface whose physical interface
+ hardware is not present on the device.";
+ }
+
+ feature if-mib {
+ description
+ "This feature indicates that the device implements
+ the IF-MIB.";
+ reference
+ "RFC 2863: The Interfaces Group MIB";
+ }
+
+ /*
+ * Configuration data nodes
+ */
+
+ container interfaces {
+ description
+ "Interface configuration parameters.";
+
+ list interface {
+ key "name";
+
+ description
+ "The list of configured interfaces on the device.
+
+ The operational state of an interface is available in the
+ /interfaces-state/interface list. If the configuration of a
+ system-controlled interface cannot be used by the system
+ (e.g., the interface hardware present does not match the
+ interface type), then the configuration is not applied to
+ the system-controlled interface shown in the
+ /interfaces-state/interface list. If the configuration
+ of a user-controlled interface cannot be used by the system,
+ the configured interface is not instantiated in the
+ /interfaces-state/interface list.";
+
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A device MAY restrict the allowed values for this leaf,
+ possibly depending on the type of the interface.
+ For system-controlled interfaces, this leaf is the
+ device-specific name of the interface. The 'config false'
+ list /interfaces-state/interface contains the currently
+ existing interfaces on the device.
+
+ If a client tries to create configuration for a
+ system-controlled interface that is not present in the
+ /interfaces-state/interface list, the server MAY reject
+ the request if the implementation does not support
+ pre-provisioning of interfaces or if the name refers to
+ an interface that can never exist in the system. A
+ NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.
+
+ If the device supports pre-provisioning of interface
+ configuration, the 'pre-provisioning' feature is
+ advertised.
+
+ If the device allows arbitrarily named user-controlled
+ interfaces, the 'arbitrary-names' feature is advertised.
+
+ When a configured user-controlled interface is created by
+ the system, it is instantiated with the same name in the
+ /interface-state/interface list.";
+ }
+
+ leaf description {
+ type string;
+ description
+ "A textual description of the interface.
+
+ A server implementation MAY map this leaf to the ifAlias
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifAlias. The definition of
+ such a mechanism is outside the scope of this document.
+
+ Since ifAlias is defined to be stored in non-volatile
+ storage, the MIB implementation MUST map ifAlias to the
+ value of 'description' in the persistently stored
+ datastore.
+
+ Specifically, if the device supports ':startup', when
+ ifAlias is read the device MUST return the value of
+ 'description' in the 'startup' datastore, and when it is
+ written, it MUST be written to the 'running' and 'startup'
+ datastores. Note that it is up to the implementation to
+
+ decide whether to modify this single leaf in 'startup' or
+ perform an implicit copy-config from 'running' to
+ 'startup'.
+
+ If the device does not support ':startup', ifAlias MUST
+ be mapped to the 'description' leaf in the 'running'
+ datastore.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAlias";
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.
+
+ When an interface entry is created, a server MAY
+ initialize the type leaf with a valid value, e.g., if it
+ is possible to derive the type from the name of the
+ interface.
+
+ If a client tries to set the type of an interface to a
+ value that can never be used by the system, e.g., if the
+ type is not supported or if the type does not match the
+ name of the interface, the server MUST reject the request.
+ A NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+ }
+
+ leaf enabled {
+ type boolean;
+ default "true";
+ description
+ "This leaf contains the configured, desired state of the
+ interface.
+
+ Systems that implement the IF-MIB use the value of this
+ leaf in the 'running' datastore to set
+ IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+ has been initialized, as described in RFC 2863.
+
+
+
+ Changes in this leaf in the 'running' datastore are
+ reflected in ifAdminStatus, but if ifAdminStatus is
+ changed over SNMP, this leaf is not affected.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+ }
+
+ leaf link-up-down-trap-enable {
+ if-feature if-mib;
+ type enumeration {
+ enum enabled {
+ value 1;
+ }
+ enum disabled {
+ value 2;
+ }
+ }
+ description
+ "Controls whether linkUp/linkDown SNMP notifications
+ should be generated for this interface.
+
+ If this node is not configured, the value 'enabled' is
+ operationally used by the server for interfaces that do
+ not operate on top of any other interface (i.e., there are
+ no 'lower-layer-if' entries), and 'disabled' otherwise.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifLinkUpDownTrapEnable";
+ }
+ }
+ }
+
+ /*
+ * Operational state data nodes
+ */
+
+ container interfaces-state {
+ config false;
+ description
+ "Data nodes for the operational state of interfaces.";
+
+ list interface {
+ key "name";
+
+
+
+
+
+ description
+ "The list of interfaces on the device.
+
+ System-controlled interfaces created by the system are
+ always present in this list, whether they are configured or
+ not.";
+
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A server implementation MAY map this leaf to the ifName
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifName. The definition of
+ such a mechanism is outside the scope of this document.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifName";
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+ }
+
+ leaf admin-status {
+ if-feature if-mib;
+ type enumeration {
+ enum up {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum down {
+ value 2;
+ description
+ "Not ready to pass packets and not in some test mode.";
+ }
+
+
+
+ enum testing {
+ value 3;
+ description
+ "In some test mode.";
+ }
+ }
+ mandatory true;
+ description
+ "The desired state of the interface.
+
+ This leaf has the same read semantics as ifAdminStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+ }
+
+ leaf oper-status {
+ type enumeration {
+ enum up {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum down {
+ value 2;
+ description
+ "The interface does not pass any packets.";
+ }
+ enum testing {
+ value 3;
+ description
+ "In some test mode. No operational packets can
+ be passed.";
+ }
+ enum unknown {
+ value 4;
+ description
+ "Status cannot be determined for some reason.";
+ }
+ enum dormant {
+ value 5;
+ description
+ "Waiting for some external event.";
+ }
+ enum not-present {
+ value 6;
+ description
+ "Some component (typically hardware) is missing.";
+ }
+ enum lower-layer-down {
+ value 7;
+ description
+ "Down due to state of lower-layer interface(s).";
+ }
+ }
+ mandatory true;
+ description
+ "The current operational state of the interface.
+
+ This leaf has the same semantics as ifOperStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+ }
+
+ leaf last-change {
+ type yang:date-and-time;
+ description
+ "The time the interface entered its current operational
+ state. If the current state was entered prior to the
+ last re-initialization of the local network management
+ subsystem, then this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifLastChange";
+ }
+
+ leaf if-index {
+ if-feature if-mib;
+ type int32 {
+ range "1..2147483647";
+ }
+ mandatory true;
+ description
+ "The ifIndex value for the ifEntry represented by this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifIndex";
+ }
+
+ leaf phys-address {
+ type yang:phys-address;
+ description
+ "The interface's address at its protocol sub-layer. For
+ example, for an 802.x interface, this object normally
+ contains a Media Access Control (MAC) address. The
+ interface's media-specific modules must define the bit
+
+
+ and byte ordering and the format of the value of this
+ object. For interfaces that do not have such an address
+ (e.g., a serial line), this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+ }
+
+ leaf-list higher-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered on top of this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+ }
+
+ leaf-list lower-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered underneath this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+ }
+
+ leaf speed {
+ type yang:gauge64;
+ units "bits/second";
+ description
+ "An estimate of the interface's current bandwidth in bits
+ per second. For interfaces that do not vary in
+ bandwidth or for those where no accurate estimation can
+ be made, this node should contain the nominal bandwidth.
+ For interfaces that have no concept of bandwidth, this
+ node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifSpeed, ifHighSpeed";
+ }
+
+
+
+
+
+
+
+
+
+ container statistics {
+ description
+ "A collection of interface-related statistics objects.";
+
+ leaf discontinuity-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The time on the most recent occasion at which any one or
+ more of this interface's counters suffered a
+ discontinuity. If no such discontinuities have occurred
+ since the last re-initialization of the local management
+ subsystem, then this node contains the time the local
+ management subsystem re-initialized itself.";
+ }
+
+ leaf in-octets {
+ type yang:counter64;
+ description
+ "The total number of octets received on the interface,
+ including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+ }
+
+ leaf in-unicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were not addressed to a
+ multicast or broadcast address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+ }
+
+
+
+
+ leaf in-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a broadcast
+ address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInBroadcastPkts";
+ }
+
+ leaf in-multicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a multicast
+ address at this sub-layer. For a MAC-layer protocol,
+ this includes both Group and Functional addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInMulticastPkts";
+ }
+
+ leaf in-discards {
+ type yang:counter32;
+ description
+ "The number of inbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being deliverable to a higher-layer
+ protocol. One possible reason for discarding such a
+ packet could be to free up buffer space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+
+
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+ }
+
+ leaf in-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of inbound
+ packets that contained errors preventing them from being
+ deliverable to a higher-layer protocol. For character-
+ oriented or fixed-length interfaces, the number of
+ inbound transmission units that contained errors
+ preventing them from being deliverable to a higher-layer
+ protocol.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInErrors";
+ }
+
+ leaf in-unknown-protos {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of packets
+ received via the interface that were discarded because
+ of an unknown or unsupported protocol. For
+ character-oriented or fixed-length interfaces that
+ support protocol multiplexing, the number of
+ transmission units received via the interface that were
+ discarded because of an unknown or unsupported protocol.
+ For any interface that does not support protocol
+ multiplexing, this counter is not present.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+ }
+
+
+
+
+
+ leaf out-octets {
+ type yang:counter64;
+ description
+ "The total number of octets transmitted out of the
+ interface, including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+ }
+
+ leaf out-unicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were not addressed
+ to a multicast or broadcast address at this sub-layer,
+ including those that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+ }
+
+ leaf out-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ broadcast address at this sub-layer, including those
+ that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutBroadcastPkts";
+ }
+
+
+ leaf out-multicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ multicast address at this sub-layer, including those
+ that were discarded or not sent. For a MAC-layer
+ protocol, this includes both Group and Functional
+ addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutMulticastPkts";
+ }
+
+ leaf out-discards {
+ type yang:counter32;
+ description
+ "The number of outbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being transmitted. One possible reason
+ for discarding such a packet could be to free up buffer
+ space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+ }
+
+ leaf out-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of outbound
+ packets that could not be transmitted because of errors.
+ For character-oriented or fixed-length interfaces, the
+ number of outbound transmission units that could not be
+ transmitted because of errors.
+
+
+
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+ }
+ }
+ }
+ }
+}
diff --git a/tools/lint/examples/ietf-ip.yang b/tools/lint/examples/ietf-ip.yang
new file mode 100644
index 0000000..1499120
--- /dev/null
+++ b/tools/lint/examples/ietf-ip.yang
@@ -0,0 +1,758 @@
+module ietf-ip {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
+ prefix ip;
+
+ import ietf-interfaces {
+ prefix if;
+ }
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+
+
+
+
+
+
+
+
+
+ description
+ "This module contains a collection of YANG definitions for
+ configuring IP implementations.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7277; see
+ the RFC itself for full legal notices.";
+
+ revision 2014-06-16 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7277: A YANG Data Model for IP Management";
+ }
+
+ /*
+
+ * Features
+ */
+
+ feature ipv4-non-contiguous-netmasks {
+ description
+ "Indicates support for configuring non-contiguous
+ subnet masks.";
+ }
+
+ feature ipv6-privacy-autoconf {
+ description
+ "Indicates support for Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+ /*
+ * Typedefs
+ */
+
+ typedef ip-address-origin {
+ type enumeration {
+ enum other {
+ description
+ "None of the following.";
+ }
+ enum static {
+ description
+ "Indicates that the address has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum dhcp {
+ description
+ "Indicates an address that has been assigned to this
+ system by a DHCP server.";
+ }
+ enum link-layer {
+ description
+ "Indicates an address created by IPv6 stateless
+ autoconfiguration that embeds a link-layer address in its
+ interface identifier.";
+ }
+ enum random {
+ description
+ "Indicates an address chosen by the system at
+
+ random, e.g., an IPv4 address within 169.254/16, an
+ RFC 4941 temporary address, or an RFC 7217 semantically
+ opaque address.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ RFC 7217: A Method for Generating Semantically Opaque
+ Interface Identifiers with IPv6 Stateless
+ Address Autoconfiguration (SLAAC)";
+ }
+ }
+ description
+ "The origin of an address.";
+ }
+
+
+
+ typedef neighbor-origin {
+ type enumeration {
+ enum other {
+ description
+ "None of the following.";
+ }
+ enum static {
+ description
+ "Indicates that the mapping has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum dynamic {
+ description
+ "Indicates that the mapping has been dynamically resolved
+ using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery
+ protocol.";
+ }
+ }
+ description
+ "The origin of a neighbor entry.";
+ }
+
+ /*
+ * Configuration data nodes
+ */
+
+ augment "/if:interfaces/if:interface" {
+ description
+ "Parameters for configuring IP on interfaces.
+
+ If an interface is not capable of running IP, the server
+ must not allow the client to configure these parameters.";
+
+ container ipv4 {
+ presence
+ "Enables IPv4 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv4 address family.";
+
+
+
+
+
+
+
+
+ leaf enabled {
+ type boolean;
+ default true;
+ description
+ "Controls whether IPv4 is enabled or disabled on this
+ interface. When IPv4 is enabled, this interface is
+ connected to an IPv4 stack, and the interface can send
+ and receive IPv4 packets.";
+ }
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Controls IPv4 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv4 routers
+ forward datagrams. IPv4 hosts do not (except those
+ source-routed via the host).";
+ }
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 791: Internet Protocol";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv4 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+
+
+
+ choice subnet {
+ mandatory true;
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ }
+
+ }
+
+
+ container ipv6 {
+ presence
+ "Enables IPv6 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv6 address family.";
+
+ leaf enabled {
+ type boolean;
+ default true;
+ description
+ "Controls whether IPv6 is enabled or disabled on this
+ interface. When IPv6 is enabled, this interface is
+ connected to an IPv6 stack, and the interface can send
+ and receive IPv6 packets.";
+ }
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Controls IPv6 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv6 routers
+ forward datagrams. IPv6 hosts do not (except those
+ source-routed via the host).";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+ }
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+ }
+
+
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv6 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ }
+
+
+
+
+
+
+ leaf dup-addr-detect-transmits {
+ type uint32;
+ default 1;
+ description
+ "The number of consecutive Neighbor Solicitation messages
+ sent while performing Duplicate Address Detection on a
+ tentative address. A value of zero indicates that
+ Duplicate Address Detection is not performed on
+ tentative addresses. A value of one indicates a single
+ transmission with no follow-up retransmissions.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+ }
+ container autoconf {
+ description
+ "Parameters to control the autoconfiguration of IPv6
+ addresses, as described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+ leaf create-global-addresses {
+ type boolean;
+ default true;
+ description
+ "If enabled, the host creates global addresses as
+ described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration
+ Section 5.5";
+ }
+ leaf create-temporary-addresses {
+ if-feature ipv6-privacy-autoconf;
+ type boolean;
+ default false;
+ description
+ "If enabled, the host creates temporary addresses as
+ described in RFC 4941.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+
+
+ leaf temporary-valid-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default 604800;
+ description
+ "The time period during which the temporary address
+ is valid.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_VALID_LIFETIME";
+ }
+ leaf temporary-preferred-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default 86400;
+ description
+ "The time period during which the temporary address is
+ preferred.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_PREFERRED_LIFETIME";
+ }
+ }
+ }
+ }
+
+ /*
+ * Operational state data nodes
+ */
+
+ augment "/if:interfaces-state/if:interface" {
+ description
+ "Data nodes for the operational state of IP on interfaces.";
+
+ container ipv4 {
+ presence "Present if IPv4 is enabled on this interface";
+ config false;
+ description
+ "Interface-specific parameters for the IPv4 address family.";
+
+
+
+
+
+ leaf forwarding {
+ type boolean;
+ description
+ "Indicates whether IPv4 packet forwarding is enabled or
+ disabled on this interface.";
+ }
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 791: Internet Protocol";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of IPv4 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+ choice subnet {
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ }
+
+
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ This list represents the ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ }
+
+ }
+
+ container ipv6 {
+ presence "Present if IPv6 is enabled on this interface";
+ config false;
+ description
+ "Parameters for the IPv6 address family.";
+
+
+
+
+
+
+
+
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Indicates whether IPv6 packet forwarding is enabled or
+ disabled on this interface.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+ }
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of IPv6 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+
+
+
+ leaf status {
+ type enumeration {
+ enum preferred {
+ description
+ "This is a valid address that can appear as the
+ destination or source address of a packet.";
+ }
+ enum deprecated {
+ description
+ "This is a valid but deprecated address that should
+ no longer be used as a source address in new
+ communications, but packets addressed to such an
+ address are processed as expected.";
+ }
+ enum invalid {
+ description
+ "This isn't a valid address, and it shouldn't appear
+ as the destination or source address of a packet.";
+ }
+ enum inaccessible {
+ description
+ "The address is not accessible because the interface
+ to which this address is assigned is not
+ operational.";
+ }
+ enum unknown {
+ description
+ "The status cannot be determined for some reason.";
+ }
+ enum tentative {
+ description
+ "The uniqueness of the address on the link is being
+ verified. Addresses in this state should not be
+ used for general communication and should only be
+ used to determine the uniqueness of the address.";
+ }
+ enum duplicate {
+ description
+ "The address has been determined to be non-unique on
+ the link and so must not be used.";
+ }
+
+
+
+
+
+
+
+ enum optimistic {
+ description
+ "The address is available for use, subject to
+ restrictions, while its uniqueness on a link is
+ being verified.";
+ }
+ }
+ description
+ "The status of an address. Most of the states correspond
+ to states from the IPv6 Stateless Address
+ Autoconfiguration protocol.";
+ reference
+ "RFC 4293: Management Information Base for the
+ Internet Protocol (IP)
+ - IpAddressStatusTC
+ RFC 4862: IPv6 Stateless Address Autoconfiguration";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ This list represents the Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ leaf is-router {
+ type empty;
+ description
+ "Indicates that the neighbor node acts as a router.";
+ }
+ leaf state {
+ type enumeration {
+ enum incomplete {
+ description
+ "Address resolution is in progress, and the link-layer
+ address of the neighbor has not yet been
+ determined.";
+ }
+ enum reachable {
+ description
+ "Roughly speaking, the neighbor is known to have been
+ reachable recently (within tens of seconds ago).";
+ }
+ enum stale {
+ description
+ "The neighbor is no longer known to be reachable, but
+ until traffic is sent to the neighbor no attempt
+ should be made to verify its reachability.";
+ }
+ enum delay {
+ description
+ "The neighbor is no longer known to be reachable, and
+ traffic has recently been sent to the neighbor.
+ Rather than probe the neighbor immediately, however,
+ delay sending probes for a short while in order to
+ give upper-layer protocols a chance to provide
+ reachability confirmation.";
+ }
+ enum probe {
+ description
+ "The neighbor is no longer known to be reachable, and
+ unicast Neighbor Solicitation probes are being sent
+ to verify reachability.";
+ }
+ }
+ description
+ "The Neighbor Unreachability Detection state of this
+ entry.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 7.3.2";
+ }
+ }
+ }
+ }
+}
diff --git a/tools/lint/examples/ietf-netconf-acm-when.yang b/tools/lint/examples/ietf-netconf-acm-when.yang
new file mode 100644
index 0000000..902fcbf
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm-when.yang
@@ -0,0 +1,412 @@
+module ietf-netconf-acm-when {
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+ prefix nacm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ WG Chair: Mehmet Ersue
+ <mailto:mehmet.ersue@nsn.com>
+
+ WG Chair: Bert Wijnen
+ <mailto:bertietf@bwijnen.net>
+
+ Editor: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+ description
+ "NETCONF Access Control Model.
+
+ Copyright (c) 2012 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6536; see
+ the RFC itself for full legal notices.";
+
+ revision 2012-02-22 {
+ description
+ "Initial version";
+ reference
+ "RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model";
+ }
+
+ extension default-deny-write {
+ description
+ "Used to indicate that the data model node
+ represents a sensitive security system parameter.
+
+ If present, and the NACM module is enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), the NETCONF server
+ will only allow the designated 'recovery session' to have
+ write access to the node. An explicit access control rule is
+ required for all other users.
+
+ The 'default-deny-write' extension MAY appear within a data
+ definition statement. It is ignored otherwise.";
+ }
+
+ extension default-deny-all {
+ description
+ "Used to indicate that the data model node
+ controls a very sensitive security system parameter.
+
+ If present, and the NACM module is enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), the NETCONF server
+ will only allow the designated 'recovery session' to have
+ read, write, or execute access to the node. An explicit
+ access control rule is required for all other users.
+
+ The 'default-deny-all' extension MAY appear within a data
+ definition statement, 'rpc' statement, or 'notification'
+ statement. It is ignored otherwise.";
+ }
+
+ typedef user-name-type {
+ type string {
+ length "1..max";
+ }
+ description
+ "General Purpose Username string.";
+ }
+
+ typedef matchall-string-type {
+ type string {
+ pattern "\\*";
+ }
+ description
+ "The string containing a single asterisk '*' is used
+ to conceptually represent all possible values
+ for the particular leaf using this data type.";
+ }
+
+ typedef access-operations-type {
+ type bits {
+ bit create {
+ description
+ "Any protocol operation that creates a
+ new data node.";
+ }
+ bit read {
+ description
+ "Any protocol operation or notification that
+ returns the value of a data node.";
+ }
+ bit update {
+ description
+ "Any protocol operation that alters an existing
+ data node.";
+ }
+ bit delete {
+ description
+ "Any protocol operation that removes a data node.";
+ }
+ bit exec {
+ description
+ "Execution access to the specified protocol operation.";
+ }
+ }
+ description
+ "NETCONF Access Operation.";
+ }
+
+ typedef group-name-type {
+ type string {
+ length "1..max";
+ pattern "[^\\*].*";
+ }
+ description
+ "Name of administrative group to which
+ users can be assigned.";
+ }
+
+ typedef action-type {
+ type enumeration {
+ enum "permit" {
+ description
+ "Requested action is permitted.";
+ }
+ enum "deny" {
+ description
+ "Requested action is denied.";
+ }
+ }
+ description
+ "Action taken by the server when a particular
+ rule matches.";
+ }
+
+ typedef node-instance-identifier {
+ type yang:xpath1.0;
+ description
+ "Path expression used to represent a special
+ data node instance identifier string.
+
+ A node-instance-identifier value is an
+ unrestricted YANG instance-identifier expression.
+ All the same rules as an instance-identifier apply
+ except predicates for keys are optional. If a key
+ predicate is missing, then the node-instance-identifier
+ represents all possible server instances for that key.
+
+ This XPath expression is evaluated in the following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the current
+ session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.";
+ }
+
+ container nacm {
+ nacm:default-deny-all;
+ description
+ "Parameters for NETCONF Access Control Model.";
+ leaf enable-nacm {
+ type boolean;
+ default "true";
+ description
+ "Enables or disables all NETCONF access control
+ enforcement. If 'true', then enforcement
+ is enabled. If 'false', then enforcement
+ is disabled.";
+ }
+ leaf read-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether read access is granted if
+ no appropriate rule is found for a
+ particular read request.";
+ }
+ leaf write-default {
+ type action-type;
+ default "deny";
+ description
+ "Controls whether create, update, or delete access
+ is granted if no appropriate rule is found for a
+ particular write request.";
+ }
+ leaf exec-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether exec access is granted if no appropriate
+ rule is found for a particular protocol operation request.";
+ }
+ leaf enable-external-groups {
+ type boolean;
+ default "true";
+ description
+ "Controls whether the server uses the groups reported by the
+ NETCONF transport layer when it assigns the user to a set of
+ NACM groups. If this leaf has the value 'false', any group
+ names reported by the transport layer are ignored by the
+ server.";
+ }
+ leaf denied-operations {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request was denied.";
+ }
+ leaf denied-data-writes {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ when "../denied-operations > 0";
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request to alter
+ a configuration datastore was denied.";
+ }
+ leaf denied-notifications {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that
+ a notification was dropped for a subscription because
+ access to the event type was denied.";
+ }
+ container groups {
+ description
+ "NETCONF Access Control Groups.";
+ list group {
+ key "name";
+ description
+ "One NACM Group Entry. This list will only contain
+ configured entries, not any entries learned from
+ any transport protocols.";
+ leaf name {
+ type group-name-type;
+ description
+ "Group name associated with this entry.";
+ }
+ leaf-list user-name {
+ type user-name-type;
+ description
+ "Each entry identifies the username of
+ a member of the group associated with
+ this entry.";
+ }
+ }
+ }
+ list rule-list {
+ key "name";
+ ordered-by user;
+ description
+ "An ordered collection of access control rules.";
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule-list.";
+ }
+ leaf-list group {
+ type union {
+ type matchall-string-type;
+ type group-name-type;
+ }
+ description
+ "List of administrative groups that will be
+ assigned the associated access rights
+ defined by the 'rule' list.
+
+ The string '*' indicates that all groups apply to the
+ entry.";
+ }
+ list rule {
+ key "name";
+ ordered-by user;
+ description
+ "One access control rule.
+
+ Rules are processed in user-defined order until a match is
+ found. A rule matches if 'module-name', 'rule-type', and
+ 'access-operations' match the request. If a rule
+ matches, the 'action' leaf determines if access is granted
+ or not.";
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule.";
+ }
+ leaf module-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ default "*";
+ description
+ "Name of the module associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ object being accessed is defined in the module with the
+ specified module name.";
+ }
+ choice rule-type {
+ description
+ "This choice matches if all leafs present in the rule
+ match the request. If no leafs are present, the
+ choice matches all requests.";
+ case protocol-operation {
+ leaf rpc-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if
+ its value equals the requested protocol operation
+ name.";
+ }
+ }
+ case notification {
+ leaf notification-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if its
+ value equals the requested notification name.";
+ }
+ }
+ case data-node {
+ leaf path {
+ type node-instance-identifier;
+ mandatory true;
+ description
+ "Data Node Instance Identifier associated with the
+ data node controlled by this rule.
+
+ Configuration data or state data instance
+ identifiers start with a top-level data node. A
+ complete instance identifier is required for this
+ type of path value.
+
+ The special value '/' refers to all possible
+ datastore contents.";
+ }
+ }
+ }
+ leaf access-operations {
+ type union {
+ type matchall-string-type;
+ type access-operations-type;
+ }
+ default "*";
+ description
+ "Access operations associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ bit corresponding to the requested operation is set.";
+ }
+ leaf action {
+ type action-type;
+ mandatory true;
+ description
+ "The access control action associated with the
+ rule. If a rule is determined to match a
+ particular request, then this object is used
+ to determine whether to permit or deny the
+ request.";
+ }
+ leaf comment {
+ type string;
+ description
+ "A textual description of the access rule.";
+ }
+ }
+ }
+ }
+}
diff --git a/tools/lint/examples/ietf-netconf-acm-when.yin b/tools/lint/examples/ietf-netconf-acm-when.yin
new file mode 100644
index 0000000..cbff758
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm-when.yin
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types" name="ietf-netconf-acm-when">
+ <namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
+ <prefix value="nacm"/>
+ <import module="ietf-yang-types">
+ <prefix value="yang"/>
+ </import>
+ <organization>
+ <text>IETF NETCONF (Network Configuration) Working Group</text>
+ </organization>
+ <contact>
+ <text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
+WG List: &lt;mailto:netconf@ietf.org&gt;
+
+WG Chair: Mehmet Ersue
+ &lt;mailto:mehmet.ersue@nsn.com&gt;
+
+WG Chair: Bert Wijnen
+ &lt;mailto:bertietf@bwijnen.net&gt;
+
+Editor: Andy Bierman
+ &lt;mailto:andy@yumaworks.com&gt;
+
+Editor: Martin Bjorklund
+ &lt;mailto:mbj@tail-f.com&gt;</text>
+ </contact>
+ <description>
+ <text>NETCONF Access Control Model.
+
+Copyright (c) 2012 IETF Trust and the persons identified as
+authors of the code. All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, is permitted pursuant to, and subject
+to the license terms contained in, the Simplified BSD
+License set forth in Section 4.c of the IETF Trust's
+Legal Provisions Relating to IETF Documents
+(http://trustee.ietf.org/license-info).
+
+This version of this YANG module is part of RFC 6536; see
+the RFC itself for full legal notices.</text>
+ </description>
+ <revision date="2012-02-22">
+ <description>
+ <text>Initial version</text>
+ </description>
+ <reference>
+ <text>RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model</text>
+ </reference>
+ </revision>
+ <extension name="default-deny-write">
+ <description>
+ <text>Used to indicate that the data model node
+represents a sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+write access to the node. An explicit access control rule is
+required for all other users.
+
+The 'default-deny-write' extension MAY appear within a data
+definition statement. It is ignored otherwise.</text>
+ </description>
+ </extension>
+ <extension name="default-deny-all">
+ <description>
+ <text>Used to indicate that the data model node
+controls a very sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+read, write, or execute access to the node. An explicit
+access control rule is required for all other users.
+
+The 'default-deny-all' extension MAY appear within a data
+definition statement, 'rpc' statement, or 'notification'
+statement. It is ignored otherwise.</text>
+ </description>
+ </extension>
+ <typedef name="user-name-type">
+ <type name="string">
+ <length value="1..max"/>
+ </type>
+ <description>
+ <text>General Purpose Username string.</text>
+ </description>
+ </typedef>
+ <typedef name="matchall-string-type">
+ <type name="string">
+ <pattern value="\*"/>
+ </type>
+ <description>
+ <text>The string containing a single asterisk '*' is used
+to conceptually represent all possible values
+for the particular leaf using this data type.</text>
+ </description>
+ </typedef>
+ <typedef name="access-operations-type">
+ <type name="bits">
+ <bit name="create">
+ <description>
+ <text>Any protocol operation that creates a
+new data node.</text>
+ </description>
+ </bit>
+ <bit name="read">
+ <description>
+ <text>Any protocol operation or notification that
+returns the value of a data node.</text>
+ </description>
+ </bit>
+ <bit name="update">
+ <description>
+ <text>Any protocol operation that alters an existing
+data node.</text>
+ </description>
+ </bit>
+ <bit name="delete">
+ <description>
+ <text>Any protocol operation that removes a data node.</text>
+ </description>
+ </bit>
+ <bit name="exec">
+ <description>
+ <text>Execution access to the specified protocol operation.</text>
+ </description>
+ </bit>
+ </type>
+ <description>
+ <text>NETCONF Access Operation.</text>
+ </description>
+ </typedef>
+ <typedef name="group-name-type">
+ <type name="string">
+ <length value="1..max"/>
+ <pattern value="[^\*].*"/>
+ </type>
+ <description>
+ <text>Name of administrative group to which
+users can be assigned.</text>
+ </description>
+ </typedef>
+ <typedef name="action-type">
+ <type name="enumeration">
+ <enum name="permit">
+ <description>
+ <text>Requested action is permitted.</text>
+ </description>
+ </enum>
+ <enum name="deny">
+ <description>
+ <text>Requested action is denied.</text>
+ </description>
+ </enum>
+ </type>
+ <description>
+ <text>Action taken by the server when a particular
+rule matches.</text>
+ </description>
+ </typedef>
+ <typedef name="node-instance-identifier">
+ <type name="yang:xpath1.0"/>
+ <description>
+ <text>Path expression used to represent a special
+data node instance identifier string.
+
+A node-instance-identifier value is an
+unrestricted YANG instance-identifier expression.
+All the same rules as an instance-identifier apply
+except predicates for keys are optional. If a key
+predicate is missing, then the node-instance-identifier
+represents all possible server instances for that key.
+
+This XPath expression is evaluated in the following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the current
+ session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.</text>
+ </description>
+ </typedef>
+ <container name="nacm">
+ <nacm:default-deny-all/>
+ <description>
+ <text>Parameters for NETCONF Access Control Model.</text>
+ </description>
+ <leaf name="enable-nacm">
+ <type name="boolean"/>
+ <default value="true"/>
+ <description>
+ <text>Enables or disables all NETCONF access control
+enforcement. If 'true', then enforcement
+is enabled. If 'false', then enforcement
+is disabled.</text>
+ </description>
+ </leaf>
+ <leaf name="read-default">
+ <type name="action-type"/>
+ <default value="permit"/>
+ <description>
+ <text>Controls whether read access is granted if
+no appropriate rule is found for a
+particular read request.</text>
+ </description>
+ </leaf>
+ <leaf name="write-default">
+ <type name="action-type"/>
+ <default value="deny"/>
+ <description>
+ <text>Controls whether create, update, or delete access
+is granted if no appropriate rule is found for a
+particular write request.</text>
+ </description>
+ </leaf>
+ <leaf name="exec-default">
+ <type name="action-type"/>
+ <default value="permit"/>
+ <description>
+ <text>Controls whether exec access is granted if no appropriate
+rule is found for a particular protocol operation request.</text>
+ </description>
+ </leaf>
+ <leaf name="enable-external-groups">
+ <type name="boolean"/>
+ <default value="true"/>
+ <description>
+ <text>Controls whether the server uses the groups reported by the
+NETCONF transport layer when it assigns the user to a set of
+NACM groups. If this leaf has the value 'false', any group
+names reported by the transport layer are ignored by the
+server.</text>
+ </description>
+ </leaf>
+ <leaf name="denied-operations">
+ <type name="yang:zero-based-counter32"/>
+ <config value="false"/>
+ <mandatory value="true"/>
+ <description>
+ <text>Number of times since the server last restarted that a
+protocol operation request was denied.</text>
+ </description>
+ </leaf>
+ <leaf name="denied-data-writes">
+ <type name="yang:zero-based-counter32"/>
+ <config value="false"/>
+ <mandatory value="true"/>
+ <when value="../denied-operations > 0"/>
+ <description>
+ <text>Number of times since the server last restarted that a
+protocol operation request to alter
+a configuration datastore was denied.</text>
+ </description>
+ </leaf>
+ <leaf name="denied-notifications">
+ <type name="yang:zero-based-counter32"/>
+ <config value="false"/>
+ <mandatory value="true"/>
+ <description>
+ <text>Number of times since the server last restarted that
+a notification was dropped for a subscription because
+access to the event type was denied.</text>
+ </description>
+ </leaf>
+ <container name="groups">
+ <description>
+ <text>NETCONF Access Control Groups.</text>
+ </description>
+ <list name="group">
+ <key value="name"/>
+ <description>
+ <text>One NACM Group Entry. This list will only contain
+configured entries, not any entries learned from
+any transport protocols.</text>
+ </description>
+ <leaf name="name">
+ <type name="group-name-type"/>
+ <description>
+ <text>Group name associated with this entry.</text>
+ </description>
+ </leaf>
+ <leaf-list name="user-name">
+ <type name="user-name-type"/>
+ <description>
+ <text>Each entry identifies the username of
+a member of the group associated with
+this entry.</text>
+ </description>
+ </leaf-list>
+ </list>
+ </container>
+ <list name="rule-list">
+ <key value="name"/>
+ <ordered-by value="user"/>
+ <description>
+ <text>An ordered collection of access control rules.</text>
+ </description>
+ <leaf name="name">
+ <type name="string">
+ <length value="1..max"/>
+ </type>
+ <description>
+ <text>Arbitrary name assigned to the rule-list.</text>
+ </description>
+ </leaf>
+ <leaf-list name="group">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="group-name-type"/>
+ </type>
+ <description>
+ <text>List of administrative groups that will be
+assigned the associated access rights
+defined by the 'rule' list.
+
+The string '*' indicates that all groups apply to the
+entry.</text>
+ </description>
+ </leaf-list>
+ <list name="rule">
+ <key value="name"/>
+ <ordered-by value="user"/>
+ <description>
+ <text>One access control rule.
+
+Rules are processed in user-defined order until a match is
+found. A rule matches if 'module-name', 'rule-type', and
+'access-operations' match the request. If a rule
+matches, the 'action' leaf determines if access is granted
+or not.</text>
+ </description>
+ <leaf name="name">
+ <type name="string">
+ <length value="1..max"/>
+ </type>
+ <description>
+ <text>Arbitrary name assigned to the rule.</text>
+ </description>
+ </leaf>
+ <leaf name="module-name">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="string"/>
+ </type>
+ <default value="*"/>
+ <description>
+ <text>Name of the module associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+object being accessed is defined in the module with the
+specified module name.</text>
+ </description>
+ </leaf>
+ <choice name="rule-type">
+ <description>
+ <text>This choice matches if all leafs present in the rule
+match the request. If no leafs are present, the
+choice matches all requests.</text>
+ </description>
+ <case name="protocol-operation">
+ <leaf name="rpc-name">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="string"/>
+ </type>
+ <description>
+ <text>This leaf matches if it has the value '*' or if
+its value equals the requested protocol operation
+name.</text>
+ </description>
+ </leaf>
+ </case>
+ <case name="notification">
+ <leaf name="notification-name">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="string"/>
+ </type>
+ <description>
+ <text>This leaf matches if it has the value '*' or if its
+value equals the requested notification name.</text>
+ </description>
+ </leaf>
+ </case>
+ <case name="data-node">
+ <leaf name="path">
+ <type name="node-instance-identifier"/>
+ <mandatory value="true"/>
+ <description>
+ <text>Data Node Instance Identifier associated with the
+data node controlled by this rule.
+
+Configuration data or state data instance
+identifiers start with a top-level data node. A
+complete instance identifier is required for this
+type of path value.
+
+The special value '/' refers to all possible
+datastore contents.</text>
+ </description>
+ </leaf>
+ </case>
+ </choice>
+ <leaf name="access-operations">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="access-operations-type"/>
+ </type>
+ <default value="*"/>
+ <description>
+ <text>Access operations associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+bit corresponding to the requested operation is set.</text>
+ </description>
+ </leaf>
+ <leaf name="action">
+ <type name="action-type"/>
+ <mandatory value="true"/>
+ <description>
+ <text>The access control action associated with the
+rule. If a rule is determined to match a
+particular request, then this object is used
+to determine whether to permit or deny the
+request.</text>
+ </description>
+ </leaf>
+ <leaf name="comment">
+ <type name="string"/>
+ <description>
+ <text>A textual description of the access rule.</text>
+ </description>
+ </leaf>
+ </list>
+ </list>
+ </container>
+</module>
diff --git a/tools/lint/examples/ietf-netconf-acm-when2.yin b/tools/lint/examples/ietf-netconf-acm-when2.yin
new file mode 100644
index 0000000..f8f25a0
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm-when2.yin
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types" name="ietf-netconf-acm-when2">
+ <namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
+ <prefix value="nacm"/>
+ <import module="ietf-yang-types">
+ <prefix value="yang"/>
+ </import>
+ <organization>
+ <text>IETF NETCONF (Network Configuration) Working Group</text>
+ </organization>
+ <contact>
+ <text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
+WG List: &lt;mailto:netconf@ietf.org&gt;
+
+WG Chair: Mehmet Ersue
+ &lt;mailto:mehmet.ersue@nsn.com&gt;
+
+WG Chair: Bert Wijnen
+ &lt;mailto:bertietf@bwijnen.net&gt;
+
+Editor: Andy Bierman
+ &lt;mailto:andy@yumaworks.com&gt;
+
+Editor: Martin Bjorklund
+ &lt;mailto:mbj@tail-f.com&gt;</text>
+ </contact>
+ <description>
+ <text>NETCONF Access Control Model.
+
+Copyright (c) 2012 IETF Trust and the persons identified as
+authors of the code. All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, is permitted pursuant to, and subject
+to the license terms contained in, the Simplified BSD
+License set forth in Section 4.c of the IETF Trust's
+Legal Provisions Relating to IETF Documents
+(http://trustee.ietf.org/license-info).
+
+This version of this YANG module is part of RFC 6536; see
+the RFC itself for full legal notices.</text>
+ </description>
+ <revision date="2012-02-22">
+ <description>
+ <text>Initial version</text>
+ </description>
+ <reference>
+ <text>RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model</text>
+ </reference>
+ </revision>
+ <extension name="default-deny-write">
+ <description>
+ <text>Used to indicate that the data model node
+represents a sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+write access to the node. An explicit access control rule is
+required for all other users.
+
+The 'default-deny-write' extension MAY appear within a data
+definition statement. It is ignored otherwise.</text>
+ </description>
+ </extension>
+ <extension name="default-deny-all">
+ <description>
+ <text>Used to indicate that the data model node
+controls a very sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+read, write, or execute access to the node. An explicit
+access control rule is required for all other users.
+
+The 'default-deny-all' extension MAY appear within a data
+definition statement, 'rpc' statement, or 'notification'
+statement. It is ignored otherwise.</text>
+ </description>
+ </extension>
+ <typedef name="user-name-type">
+ <type name="string">
+ <length value="1..max"/>
+ </type>
+ <description>
+ <text>General Purpose Username string.</text>
+ </description>
+ </typedef>
+ <typedef name="matchall-string-type">
+ <type name="string">
+ <pattern value="\*"/>
+ </type>
+ <description>
+ <text>The string containing a single asterisk '*' is used
+to conceptually represent all possible values
+for the particular leaf using this data type.</text>
+ </description>
+ </typedef>
+ <typedef name="access-operations-type">
+ <type name="bits">
+ <bit name="create">
+ <description>
+ <text>Any protocol operation that creates a
+new data node.</text>
+ </description>
+ </bit>
+ <bit name="read">
+ <description>
+ <text>Any protocol operation or notification that
+returns the value of a data node.</text>
+ </description>
+ </bit>
+ <bit name="update">
+ <description>
+ <text>Any protocol operation that alters an existing
+data node.</text>
+ </description>
+ </bit>
+ <bit name="delete">
+ <description>
+ <text>Any protocol operation that removes a data node.</text>
+ </description>
+ </bit>
+ <bit name="exec">
+ <description>
+ <text>Execution access to the specified protocol operation.</text>
+ </description>
+ </bit>
+ </type>
+ <description>
+ <text>NETCONF Access Operation.</text>
+ </description>
+ </typedef>
+ <typedef name="group-name-type">
+ <type name="string">
+ <length value="1..max"/>
+ <pattern value="[^\*].*"/>
+ </type>
+ <description>
+ <text>Name of administrative group to which
+users can be assigned.</text>
+ </description>
+ </typedef>
+ <typedef name="action-type">
+ <type name="enumeration">
+ <enum name="permit">
+ <description>
+ <text>Requested action is permitted.</text>
+ </description>
+ </enum>
+ <enum name="deny">
+ <description>
+ <text>Requested action is denied.</text>
+ </description>
+ </enum>
+ </type>
+ <description>
+ <text>Action taken by the server when a particular
+rule matches.</text>
+ </description>
+ </typedef>
+ <typedef name="node-instance-identifier">
+ <type name="yang:xpath1.0"/>
+ <description>
+ <text>Path expression used to represent a special
+data node instance identifier string.
+
+A node-instance-identifier value is an
+unrestricted YANG instance-identifier expression.
+All the same rules as an instance-identifier apply
+except predicates for keys are optional. If a key
+predicate is missing, then the node-instance-identifier
+represents all possible server instances for that key.
+
+This XPath expression is evaluated in the following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the current
+ session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.</text>
+ </description>
+ </typedef>
+ <container name="nacm">
+ <nacm:default-deny-all/>
+ <description>
+ <text>Parameters for NETCONF Access Control Model.</text>
+ </description>
+ <leaf name="enable-nacm">
+ <type name="boolean"/>
+ <default value="true"/>
+ <description>
+ <text>Enables or disables all NETCONF access control
+enforcement. If 'true', then enforcement
+is enabled. If 'false', then enforcement
+is disabled.</text>
+ </description>
+ </leaf>
+ <leaf name="read-default">
+ <type name="action-type"/>
+ <default value="permit"/>
+ <description>
+ <text>Controls whether read access is granted if
+no appropriate rule is found for a
+particular read request.</text>
+ </description>
+ </leaf>
+ <leaf name="write-default">
+ <type name="action-type"/>
+ <default value="deny"/>
+ <description>
+ <text>Controls whether create, update, or delete access
+is granted if no appropriate rule is found for a
+particular write request.</text>
+ </description>
+ </leaf>
+ <leaf name="exec-default">
+ <type name="action-type"/>
+ <default value="permit"/>
+ <description>
+ <text>Controls whether exec access is granted if no appropriate
+rule is found for a particular protocol operation request.</text>
+ </description>
+ </leaf>
+ <leaf name="enable-external-groups">
+ <type name="boolean"/>
+ <default value="true"/>
+ <description>
+ <text>Controls whether the server uses the groups reported by the
+NETCONF transport layer when it assigns the user to a set of
+NACM groups. If this leaf has the value 'false', any group
+names reported by the transport layer are ignored by the
+server.</text>
+ </description>
+ </leaf>
+ <leaf name="denied-operations">
+ <type name="yang:zero-based-counter32"/>
+ <config value="false"/>
+ <mandatory value="true"/>
+ <description>
+ <text>Number of times since the server last restarted that a
+protocol operation request was denied.</text>
+ </description>
+ </leaf>
+ <leaf name="denied-data-writes">
+ <type name="yang:zero-based-counter32"/>
+ <config value="false"/>
+ <mandatory value="true"/>
+ <when condition="../denied-operations > 0"/>
+ <description>
+ <text>Number of times since the server last restarted that a
+protocol operation request to alter
+a configuration datastore was denied.</text>
+ </description>
+ </leaf>
+ <leaf name="denied-notifications">
+ <type name="yang:zero-based-counter32"/>
+ <config value="false"/>
+ <mandatory value="true"/>
+ <description>
+ <text>Number of times since the server last restarted that
+a notification was dropped for a subscription because
+access to the event type was denied.</text>
+ </description>
+ </leaf>
+ <container name="groups">
+ <description>
+ <text>NETCONF Access Control Groups.</text>
+ </description>
+ <list name="group">
+ <key value="name"/>
+ <description>
+ <text>One NACM Group Entry. This list will only contain
+configured entries, not any entries learned from
+any transport protocols.</text>
+ </description>
+ <leaf name="name">
+ <type name="group-name-type"/>
+ <description>
+ <text>Group name associated with this entry.</text>
+ </description>
+ </leaf>
+ <leaf-list name="user-name">
+ <type name="user-name-type"/>
+ <description>
+ <text>Each entry identifies the username of
+a member of the group associated with
+this entry.</text>
+ </description>
+ </leaf-list>
+ </list>
+ </container>
+ <list name="rule-list">
+ <key value="name"/>
+ <ordered-by value="user"/>
+ <description>
+ <text>An ordered collection of access control rules.</text>
+ </description>
+ <leaf name="name">
+ <type name="string">
+ <length value="1..max"/>
+ </type>
+ <description>
+ <text>Arbitrary name assigned to the rule-list.</text>
+ </description>
+ </leaf>
+ <leaf-list name="group">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="group-name-type"/>
+ </type>
+ <description>
+ <text>List of administrative groups that will be
+assigned the associated access rights
+defined by the 'rule' list.
+
+The string '*' indicates that all groups apply to the
+entry.</text>
+ </description>
+ </leaf-list>
+ <list name="rule">
+ <key value="name"/>
+ <ordered-by value="user"/>
+ <description>
+ <text>One access control rule.
+
+Rules are processed in user-defined order until a match is
+found. A rule matches if 'module-name', 'rule-type', and
+'access-operations' match the request. If a rule
+matches, the 'action' leaf determines if access is granted
+or not.</text>
+ </description>
+ <leaf name="name">
+ <type name="string">
+ <length value="1..max"/>
+ </type>
+ <description>
+ <text>Arbitrary name assigned to the rule.</text>
+ </description>
+ </leaf>
+ <leaf name="module-name">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="string"/>
+ </type>
+ <default value="*"/>
+ <description>
+ <text>Name of the module associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+object being accessed is defined in the module with the
+specified module name.</text>
+ </description>
+ </leaf>
+ <choice name="rule-type">
+ <description>
+ <text>This choice matches if all leafs present in the rule
+match the request. If no leafs are present, the
+choice matches all requests.</text>
+ </description>
+ <case name="protocol-operation">
+ <leaf name="rpc-name">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="string"/>
+ </type>
+ <description>
+ <text>This leaf matches if it has the value '*' or if
+its value equals the requested protocol operation
+name.</text>
+ </description>
+ </leaf>
+ </case>
+ <case name="notification">
+ <leaf name="notification-name">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="string"/>
+ </type>
+ <description>
+ <text>This leaf matches if it has the value '*' or if its
+value equals the requested notification name.</text>
+ </description>
+ </leaf>
+ </case>
+ <case name="data-node">
+ <leaf name="path">
+ <type name="node-instance-identifier"/>
+ <mandatory value="true"/>
+ <description>
+ <text>Data Node Instance Identifier associated with the
+data node controlled by this rule.
+
+Configuration data or state data instance
+identifiers start with a top-level data node. A
+complete instance identifier is required for this
+type of path value.
+
+The special value '/' refers to all possible
+datastore contents.</text>
+ </description>
+ </leaf>
+ </case>
+ </choice>
+ <leaf name="access-operations">
+ <type name="union">
+ <type name="matchall-string-type"/>
+ <type name="access-operations-type"/>
+ </type>
+ <default value="*"/>
+ <description>
+ <text>Access operations associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+bit corresponding to the requested operation is set.</text>
+ </description>
+ </leaf>
+ <leaf name="action">
+ <type name="action-type"/>
+ <mandatory value="true"/>
+ <description>
+ <text>The access control action associated with the
+rule. If a rule is determined to match a
+particular request, then this object is used
+to determine whether to permit or deny the
+request.</text>
+ </description>
+ </leaf>
+ <leaf name="comment">
+ <type name="string"/>
+ <description>
+ <text>A textual description of the access rule.</text>
+ </description>
+ </leaf>
+ </list>
+ </list>
+ </container>
+</module>
diff --git a/tools/lint/examples/ietf-netconf-acm.yang b/tools/lint/examples/ietf-netconf-acm.yang
new file mode 100644
index 0000000..dc3655e
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm.yang
@@ -0,0 +1,411 @@
+module ietf-netconf-acm {
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+ prefix nacm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ WG Chair: Mehmet Ersue
+ <mailto:mehmet.ersue@nsn.com>
+
+ WG Chair: Bert Wijnen
+ <mailto:bertietf@bwijnen.net>
+
+ Editor: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+ description
+ "NETCONF Access Control Model.
+
+ Copyright (c) 2012 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6536; see
+ the RFC itself for full legal notices.";
+
+ revision 2012-02-22 {
+ description
+ "Initial version";
+ reference
+ "RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model";
+ }
+
+ extension default-deny-write {
+ description
+ "Used to indicate that the data model node
+ represents a sensitive security system parameter.
+
+ If present, and the NACM module is enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), the NETCONF server
+ will only allow the designated 'recovery session' to have
+ write access to the node. An explicit access control rule is
+ required for all other users.
+
+ The 'default-deny-write' extension MAY appear within a data
+ definition statement. It is ignored otherwise.";
+ }
+
+ extension default-deny-all {
+ description
+ "Used to indicate that the data model node
+ controls a very sensitive security system parameter.
+
+ If present, and the NACM module is enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), the NETCONF server
+ will only allow the designated 'recovery session' to have
+ read, write, or execute access to the node. An explicit
+ access control rule is required for all other users.
+
+ The 'default-deny-all' extension MAY appear within a data
+ definition statement, 'rpc' statement, or 'notification'
+ statement. It is ignored otherwise.";
+ }
+
+ typedef user-name-type {
+ type string {
+ length "1..max";
+ }
+ description
+ "General Purpose Username string.";
+ }
+
+ typedef matchall-string-type {
+ type string {
+ pattern "\\*";
+ }
+ description
+ "The string containing a single asterisk '*' is used
+ to conceptually represent all possible values
+ for the particular leaf using this data type.";
+ }
+
+ typedef access-operations-type {
+ type bits {
+ bit create {
+ description
+ "Any protocol operation that creates a
+ new data node.";
+ }
+ bit read {
+ description
+ "Any protocol operation or notification that
+ returns the value of a data node.";
+ }
+ bit update {
+ description
+ "Any protocol operation that alters an existing
+ data node.";
+ }
+ bit delete {
+ description
+ "Any protocol operation that removes a data node.";
+ }
+ bit exec {
+ description
+ "Execution access to the specified protocol operation.";
+ }
+ }
+ description
+ "NETCONF Access Operation.";
+ }
+
+ typedef group-name-type {
+ type string {
+ length "1..max";
+ pattern "[^\\*].*";
+ }
+ description
+ "Name of administrative group to which
+ users can be assigned.";
+ }
+
+ typedef action-type {
+ type enumeration {
+ enum "permit" {
+ description
+ "Requested action is permitted.";
+ }
+ enum "deny" {
+ description
+ "Requested action is denied.";
+ }
+ }
+ description
+ "Action taken by the server when a particular
+ rule matches.";
+ }
+
+ typedef node-instance-identifier {
+ type yang:xpath1.0;
+ description
+ "Path expression used to represent a special
+ data node instance identifier string.
+
+ A node-instance-identifier value is an
+ unrestricted YANG instance-identifier expression.
+ All the same rules as an instance-identifier apply
+ except predicates for keys are optional. If a key
+ predicate is missing, then the node-instance-identifier
+ represents all possible server instances for that key.
+
+ This XPath expression is evaluated in the following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the current
+ session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.";
+ }
+
+ container nacm {
+ nacm:default-deny-all;
+ description
+ "Parameters for NETCONF Access Control Model.";
+ leaf enable-nacm {
+ type boolean;
+ default "true";
+ description
+ "Enables or disables all NETCONF access control
+ enforcement. If 'true', then enforcement
+ is enabled. If 'false', then enforcement
+ is disabled.";
+ }
+ leaf read-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether read access is granted if
+ no appropriate rule is found for a
+ particular read request.";
+ }
+ leaf write-default {
+ type action-type;
+ default "deny";
+ description
+ "Controls whether create, update, or delete access
+ is granted if no appropriate rule is found for a
+ particular write request.";
+ }
+ leaf exec-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether exec access is granted if no appropriate
+ rule is found for a particular protocol operation request.";
+ }
+ leaf enable-external-groups {
+ type boolean;
+ default "true";
+ description
+ "Controls whether the server uses the groups reported by the
+ NETCONF transport layer when it assigns the user to a set of
+ NACM groups. If this leaf has the value 'false', any group
+ names reported by the transport layer are ignored by the
+ server.";
+ }
+ leaf denied-operations {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request was denied.";
+ }
+ leaf denied-data-writes {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request to alter
+ a configuration datastore was denied.";
+ }
+ leaf denied-notifications {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that
+ a notification was dropped for a subscription because
+ access to the event type was denied.";
+ }
+ container groups {
+ description
+ "NETCONF Access Control Groups.";
+ list group {
+ key "name";
+ description
+ "One NACM Group Entry. This list will only contain
+ configured entries, not any entries learned from
+ any transport protocols.";
+ leaf name {
+ type group-name-type;
+ description
+ "Group name associated with this entry.";
+ }
+ leaf-list user-name {
+ type user-name-type;
+ description
+ "Each entry identifies the username of
+ a member of the group associated with
+ this entry.";
+ }
+ }
+ }
+ list rule-list {
+ key "name";
+ ordered-by user;
+ description
+ "An ordered collection of access control rules.";
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule-list.";
+ }
+ leaf-list group {
+ type union {
+ type matchall-string-type;
+ type group-name-type;
+ }
+ description
+ "List of administrative groups that will be
+ assigned the associated access rights
+ defined by the 'rule' list.
+
+ The string '*' indicates that all groups apply to the
+ entry.";
+ }
+ list rule {
+ key "name";
+ ordered-by user;
+ description
+ "One access control rule.
+
+ Rules are processed in user-defined order until a match is
+ found. A rule matches if 'module-name', 'rule-type', and
+ 'access-operations' match the request. If a rule
+ matches, the 'action' leaf determines if access is granted
+ or not.";
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule.";
+ }
+ leaf module-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ default "*";
+ description
+ "Name of the module associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ object being accessed is defined in the module with the
+ specified module name.";
+ }
+ choice rule-type {
+ description
+ "This choice matches if all leafs present in the rule
+ match the request. If no leafs are present, the
+ choice matches all requests.";
+ case protocol-operation {
+ leaf rpc-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if
+ its value equals the requested protocol operation
+ name.";
+ }
+ }
+ case notification {
+ leaf notification-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if its
+ value equals the requested notification name.";
+ }
+ }
+ case data-node {
+ leaf path {
+ type node-instance-identifier;
+ mandatory true;
+ description
+ "Data Node Instance Identifier associated with the
+ data node controlled by this rule.
+
+ Configuration data or state data instance
+ identifiers start with a top-level data node. A
+ complete instance identifier is required for this
+ type of path value.
+
+ The special value '/' refers to all possible
+ datastore contents.";
+ }
+ }
+ }
+ leaf access-operations {
+ type union {
+ type matchall-string-type;
+ type access-operations-type;
+ }
+ default "*";
+ description
+ "Access operations associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ bit corresponding to the requested operation is set.";
+ }
+ leaf action {
+ type action-type;
+ mandatory true;
+ description
+ "The access control action associated with the
+ rule. If a rule is determined to match a
+ particular request, then this object is used
+ to determine whether to permit or deny the
+ request.";
+ }
+ leaf comment {
+ type string;
+ description
+ "A textual description of the access rule.";
+ }
+ }
+ }
+ }
+}
diff --git a/tools/lint/examples/module1.yang b/tools/lint/examples/module1.yang
new file mode 100644
index 0000000..1df7bf1
--- /dev/null
+++ b/tools/lint/examples/module1.yang
@@ -0,0 +1,5 @@
+module module1 {
+ namespace "urn:yanglint:module";
+ prefix m;
+ leaf m { type string; }
+}
diff --git a/tools/lint/examples/module1b.yang b/tools/lint/examples/module1b.yang
new file mode 100644
index 0000000..463c936
--- /dev/null
+++ b/tools/lint/examples/module1b.yang
@@ -0,0 +1,5 @@
+module module1b {
+ namespace "urn:yanglint:module";
+ prefix m;
+ leaf mb { type string; }
+}
diff --git a/tools/lint/examples/module2.yang b/tools/lint/examples/module2.yang
new file mode 100644
index 0000000..c87c764
--- /dev/null
+++ b/tools/lint/examples/module2.yang
@@ -0,0 +1,5 @@
+module module2 {
+ namespace "urn:yanglint:module";
+ prefix m;
+ leaf m { ttype string; }
+}
diff --git a/tools/lint/examples/module2.yin b/tools/lint/examples/module2.yin
new file mode 100644
index 0000000..af6cb50
--- /dev/null
+++ b/tools/lint/examples/module2.yin
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module name="module2"
+ xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+ xmlns:m="urn:yanglint:module">
+ <namespace uri="urn:yanglint:module"/>
+ <prefix value="m"/>
+ <leaf name="m">
+ <type value="string"/>
+ </leaf>
+</module>
diff --git a/tools/lint/examples/module3.yang b/tools/lint/examples/module3.yang
new file mode 100644
index 0000000..63754b1
--- /dev/null
+++ b/tools/lint/examples/module3.yang
@@ -0,0 +1,8 @@
+module module3 {
+ namespace "urn:yanglint:module";
+ prefix m;
+ leaf m { type string; must "../c/a"; }
+ container c {
+ leaf b { type string; }
+ }
+}
diff --git a/tools/lint/examples/module4.yang b/tools/lint/examples/module4.yang
new file mode 100644
index 0000000..23ea289
--- /dev/null
+++ b/tools/lint/examples/module4.yang
@@ -0,0 +1,52 @@
+module module4 {
+ yang-version 1.1;
+ namespace "urn:module4";
+ prefix m4;
+
+ container cont1 {
+ list list {
+ key "leaf1";
+ leaf leaf1 {
+ type string;
+ }
+ action act {
+ input {
+ leaf leaf2 {
+ type string;
+ }
+ }
+ output {
+ leaf leaf3 {
+ type string;
+ }
+ }
+ }
+ notification notif1 {
+ leaf leaf4 {
+ type string;
+ }
+ }
+ }
+ }
+
+ rpc rpc {
+ input {
+ leaf leaf5 {
+ type string;
+ }
+ }
+ output {
+ container cont2 {
+ leaf leaf6 {
+ type empty;
+ }
+ }
+ }
+ }
+
+ notification notif2 {
+ leaf leaf7 {
+ type empty;
+ }
+ }
+}
diff --git a/tools/lint/examples/nested-notification.xml b/tools/lint/examples/nested-notification.xml
new file mode 100644
index 0000000..024b65a
--- /dev/null
+++ b/tools/lint/examples/nested-notification.xml
@@ -0,0 +1,8 @@
+<cont1 xmlns="urn:module4">
+ <list>
+ <leaf1>key_val</leaf1>
+ <notif1>
+ <leaf4>some_value</leaf4>
+ </notif1>
+ </list>
+</cont1>
diff --git a/tools/lint/examples/notification.xml b/tools/lint/examples/notification.xml
new file mode 100644
index 0000000..803ddad
--- /dev/null
+++ b/tools/lint/examples/notification.xml
@@ -0,0 +1,3 @@
+<notif2 xmlns="urn:module4">
+ <leaf7/>
+</notif2>
diff --git a/tools/lint/examples/rpc-reply.xml b/tools/lint/examples/rpc-reply.xml
new file mode 100644
index 0000000..54aab3e
--- /dev/null
+++ b/tools/lint/examples/rpc-reply.xml
@@ -0,0 +1,5 @@
+<rpc xmlns="urn:module4">
+ <cont2>
+ <leaf6/>
+ </cont2>
+</rpc>
diff --git a/tools/lint/examples/rpc.xml b/tools/lint/examples/rpc.xml
new file mode 100644
index 0000000..ea8ca90
--- /dev/null
+++ b/tools/lint/examples/rpc.xml
@@ -0,0 +1,3 @@
+<rpc xmlns="urn:module4">
+ <leaf5>some_input</leaf5>
+</rpc>
diff --git a/tools/lint/examples/sm-context-extension.xml b/tools/lint/examples/sm-context-extension.xml
new file mode 100644
index 0000000..747c60f
--- /dev/null
+++ b/tools/lint/examples/sm-context-extension.xml
@@ -0,0 +1,64 @@
+<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"
+ xmlns:ds="urn:ietf:params:xml:ns:yang:ietf-datastores">
+ <module-set>
+ <name>test-set</name>
+ <module>
+ <name>ietf-datastores</name>
+ <revision>2018-02-14</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>
+ </module>
+ <module>
+ <name>ietf-yang-library</name>
+ <revision>2019-01-04</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>
+ </module>
+ <module>
+ <name>sm-extension</name>
+ <namespace>urn:sm-ext</namespace>
+ </module>
+ <module>
+ <name>iana-if-type</name>
+ <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>
+ </module>
+ <import-only-module>
+ <name>ietf-yang-types</name>
+ <revision>2013-07-15</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>
+ </import-only-module>
+ <import-only-module>
+ <name>sm-mod</name>
+ <revision>2017-01-26</revision>
+ <namespace>urn:yanglint:sm-mod</namespace>
+ </import-only-module>
+ </module-set>
+ <schema>
+ <name>test-schema</name>
+ <module-set>test-set</module-set>
+ </schema>
+ <datastore>
+ <name>ds:running</name>
+ <schema>test-schema</schema>
+ </datastore>
+ <datastore>
+ <name>ds:operational</name>
+ <schema>test-schema</schema>
+ </datastore>
+ <content-id>1</content-id>
+ </yang-library>
+ <modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set-id>1</module-set-id>
+ </modules-state>
+ <schema-mounts xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount">
+ <namespace>
+ <prefix>if</prefix>
+ <uri>urn:ietf:params:xml:ns:yang:ietf-interfaces</uri>
+ </namespace>
+ <mount-point>
+ <module>sm-main</module>
+ <label>mnt-root</label>
+ <shared-schema>
+ <parent-reference>/if:interfaces/if:interface/if:name</parent-reference>
+ <parent-reference>/if:interfaces/if:interface/if:type</parent-reference>
+ </shared-schema>
+ </mount-point>
+ </schema-mounts>
diff --git a/tools/lint/examples/sm-context-main.xml b/tools/lint/examples/sm-context-main.xml
new file mode 100644
index 0000000..43558c3
--- /dev/null
+++ b/tools/lint/examples/sm-context-main.xml
@@ -0,0 +1,54 @@
+<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"
+ xmlns:ds="urn:ietf:params:xml:ns:yang:ietf-datastores">
+ <module-set>
+ <name>main-set</name>
+ <module>
+ <name>ietf-datastores</name>
+ <revision>2018-02-14</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>
+ </module>
+ <module>
+ <name>ietf-yang-library</name>
+ <revision>2019-01-04</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>
+ </module>
+ <module>
+ <name>ietf-yang-schema-mount</name>
+ <revision>2019-01-14</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>
+ </module>
+ <module>
+ <name>sm-main</name>
+ <namespace>urn:sm-main</namespace>
+ </module>
+ <module>
+ <name>iana-if-type</name>
+ <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>
+ </module>
+ <module>
+ <name>ietf-interfaces</name>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>
+ </module>
+ <import-only-module>
+ <name>ietf-yang-types</name>
+ <revision>2013-07-15</revision>
+ <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>
+ </import-only-module>
+ </module-set>
+ <schema>
+ <name>main-schema</name>
+ <module-set>main-set</module-set>
+ </schema>
+ <datastore>
+ <name>ds:running</name>
+ <schema>main-schema</schema>
+ </datastore>
+ <datastore>
+ <name>ds:operational</name>
+ <schema>main-schema</schema>
+ </datastore>
+ <content-id>1</content-id>
+ </yang-library>
+ <modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set-id>2</module-set-id>
+ </modules-state>
diff --git a/tools/lint/examples/sm-data.xml b/tools/lint/examples/sm-data.xml
new file mode 100644
index 0000000..478d324
--- /dev/null
+++ b/tools/lint/examples/sm-data.xml
@@ -0,0 +1,19 @@
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+ <interface>
+ <name>eth0</name>
+ <type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
+ </interface>
+ <interface>
+ <name>eth1</name>
+ <type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
+ </interface>
+</interfaces>
+<root3 xmlns="urn:sm-main">
+ <my-list>
+ <name>list item 1</name>
+ <things xmlns="urn:sm-ext">
+ <name>eth0</name>
+ <attribute>1</attribute>
+ </things>
+ </my-list>
+</root3>
diff --git a/tools/lint/examples/sm-extension.yang b/tools/lint/examples/sm-extension.yang
new file mode 100644
index 0000000..2214cf6
--- /dev/null
+++ b/tools/lint/examples/sm-extension.yang
@@ -0,0 +1,39 @@
+module sm-extension {
+ yang-version 1.1;
+ namespace "urn:sm-ext";
+ prefix "sm-ext";
+
+ import ietf-interfaces {
+ prefix if;
+ }
+ import sm-mod {
+ prefix sm-mod;
+ }
+
+ revision 2022-09-15 {
+ description
+ "initial";
+ reference
+ "";
+ }
+
+ list things {
+ key "name";
+ leaf name {
+ type leafref {
+ path "/if:interfaces/if:interface/if:name";
+ }
+ }
+ leaf attribute {
+ type uint32;
+ }
+ }
+
+ augment "/if:interfaces/if:interface" {
+ leaf thing-attribute {
+ type leafref {
+ path "/things/attribute";
+ }
+ }
+ }
+}
diff --git a/tools/lint/examples/sm-main.yang b/tools/lint/examples/sm-main.yang
new file mode 100644
index 0000000..53df6b6
--- /dev/null
+++ b/tools/lint/examples/sm-main.yang
@@ -0,0 +1,32 @@
+module sm-main {
+ yang-version 1.1;
+ namespace "urn:sm-main";
+ prefix "sm-main";
+
+ import ietf-yang-schema-mount {
+ prefix yangmnt;
+ }
+ import ietf-interfaces {
+ prefix if;
+ }
+
+ list root {
+ key "node";
+ leaf node {
+ type string;
+ }
+ yangmnt:mount-point "root";
+ }
+ container root2 {
+ yangmnt:mount-point "root";
+ }
+ container root3 {
+ list my-list {
+ key name;
+ leaf name {
+ type string;
+ }
+ yangmnt:mount-point "mnt-root";
+ }
+ }
+}
diff --git a/tools/lint/examples/sm-mod.yang b/tools/lint/examples/sm-mod.yang
new file mode 100644
index 0000000..79d1a50
--- /dev/null
+++ b/tools/lint/examples/sm-mod.yang
@@ -0,0 +1,21 @@
+module sm-mod {
+ yang-version 1.1;
+ namespace "urn:yanglint:sm-mod";
+ prefix "sm-mod";
+
+ revision 2017-01-26 {
+ description
+ "initial";
+ reference
+ "";
+ }
+
+ container not-compiled {
+ leaf first {
+ type string;
+ }
+ leaf second {
+ type string;
+ }
+ }
+}
diff --git a/tools/lint/linenoise/LICENSE b/tools/lint/linenoise/LICENSE
new file mode 100644
index 0000000..18e8148
--- /dev/null
+++ b/tools/lint/linenoise/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/lint/linenoise/linenoise.c b/tools/lint/linenoise/linenoise.c
new file mode 100644
index 0000000..fed3d26
--- /dev/null
+++ b/tools/lint/linenoise/linenoise.c
@@ -0,0 +1,1218 @@
+/* linenoise.c -- VERSION 1.0
+ *
+ * Guerrilla line editing library against the idea that a line editing lib
+ * needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ *
+ * http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * EL (Erase Line)
+ * Sequence: ESC [ n K
+ * Effect: if n is 0 or missing, clear from cursor to end of line
+ * Effect: if n is 1, clear from beginning of line to cursor
+ * Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ * Sequence: ESC [ n C
+ * Effect: moves cursor forward n chars
+ *
+ * CUB (CUrsor Backward)
+ * Sequence: ESC [ n D
+ * Effect: moves cursor backward n chars
+ *
+ * The following is used to get the terminal width if getting
+ * the width with the TIOCGWINSZ ioctl fails
+ *
+ * DSR (Device Status Report)
+ * Sequence: ESC [ 6 n
+ * Effect: reports the current cusor position as ESC [ n ; m R
+ * where n is the row and m is the column
+ *
+ * When multi line mode is enabled, we also use an additional escape
+ * sequence. However multi line editing is disabled by default.
+ *
+ * CUU (Cursor Up)
+ * Sequence: ESC [ n A
+ * Effect: moves cursor up of n chars.
+ *
+ * CUD (Cursor Down)
+ * Sequence: ESC [ n B
+ * Effect: moves cursor down of n chars.
+ *
+ * When linenoiseClearScreen() is called, two additional escape sequences
+ * are used in order to clear the screen and position the cursor at home
+ * position.
+ *
+ * CUP (Cursor position)
+ * Sequence: ESC [ H
+ * Effect: moves the cursor to upper left corner
+ *
+ * ED (Erase display)
+ * Sequence: ESC [ 2 J
+ * Effect: clear the whole screen
+ *
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include "linenoise.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
+static linenoiseCompletionCallback *completionCallback = NULL;
+
+static struct termios orig_termios; /* In order to restore at exit.*/
+static int mlmode = 0; /* Multi line mode. Default is single line. */
+static int atexit_registered = 0; /* Register atexit just 1 time. */
+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+static int history_len = 0;
+static char **history = NULL;
+
+/* The linenoiseState structure represents the state during line editing.
+ * We pass this state to functions implementing specific editing
+ * functionalities. */
+struct linenoiseState lss;
+
+enum KEY_ACTION{
+ KEY_NULL = 0, /* NULL */
+ CTRL_A = 1, /* Ctrl+a */
+ CTRL_B = 2, /* Ctrl-b */
+ CTRL_C = 3, /* Ctrl-c */
+ CTRL_D = 4, /* Ctrl-d */
+ CTRL_E = 5, /* Ctrl-e */
+ CTRL_F = 6, /* Ctrl-f */
+ CTRL_H = 8, /* Ctrl-h */
+ TAB = 9, /* Tab */
+ CTRL_K = 11, /* Ctrl+k */
+ CTRL_L = 12, /* Ctrl+l */
+ ENTER = 13, /* Enter */
+ CTRL_N = 14, /* Ctrl-n */
+ CTRL_P = 16, /* Ctrl-p */
+ CTRL_T = 20, /* Ctrl-t */
+ CTRL_U = 21, /* Ctrl+u */
+ CTRL_W = 23, /* Ctrl+w */
+ ESC = 27, /* Escape */
+ BACKSPACE = 127 /* Backspace */
+};
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+
+/* Debugging macro. */
+#if 0
+FILE *lndebug_fp = NULL;
+#define lndebug(...) \
+ do { \
+ if (lndebug_fp == NULL) { \
+ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
+ fprintf(lndebug_fp, \
+ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
+ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
+ (int)l->maxrows,old_rows); \
+ } \
+ fprintf(lndebug_fp, ", " __VA_ARGS__); \
+ fflush(lndebug_fp); \
+ } while (0)
+#else
+#define lndebug(...)
+#endif
+
+/* ======================= Low level terminal handling ====================== */
+
+/* Set if to use or not the multi line mode. */
+void linenoiseSetMultiLine(int ml) {
+ mlmode = ml;
+}
+
+/* Return true if the terminal name is in the list of terminals we know are
+ * not able to understand basic escape sequences. */
+static int isUnsupportedTerm(void) {
+ char *term = getenv("TERM");
+ int j;
+
+ if (term == NULL) return 0;
+ for (j = 0; unsupported_term[j]; j++)
+ if (!strcasecmp(term,unsupported_term[j])) return 1;
+ return 0;
+}
+
+/* Raw mode: 1960 magic shit. */
+int linenoiseEnableRawMode(int fd) {
+ struct termios raw;
+
+ if (!isatty(STDIN_FILENO)) goto fatal;
+ if (!atexit_registered) {
+ atexit(linenoiseAtExit);
+ atexit_registered = 1;
+ }
+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+ raw = orig_termios; /* modify the original mode */
+ /* input modes: no break, no CR to NL, no parity check, no strip char,
+ * no start/stop output control. */
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ /* output modes - disable post processing */
+ raw.c_oflag &= ~(OPOST);
+ /* control modes - set 8 bit chars */
+ raw.c_cflag |= (CS8);
+ /* local modes - choing off, canonical off, no extended functions,
+ * no signal chars (^Z,^C) */
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ /* control chars - set return condition: min number of bytes and timer.
+ * We want read to return every single byte, without timeout. */
+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+ /* put terminal in raw mode after flushing */
+ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+ lss.rawmode = 1;
+ return 0;
+
+fatal:
+ errno = ENOTTY;
+ return -1;
+}
+
+void linenoiseDisableRawMode(int fd) {
+ /* Don't even check the return value as it's too late. */
+ if (lss.rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+ lss.rawmode = 0;
+}
+
+/* Use the ESC [6n escape sequence to query the horizontal cursor position
+ * and return it. On error -1 is returned, on success the position of the
+ * cursor. */
+static int getCursorPosition(int ifd, int ofd) {
+ char buf[32];
+ int cols, rows;
+ unsigned int i = 0;
+
+ /* Report cursor location */
+ if (write(ofd, "\x1b[6n", 4) != 4) return -1;
+
+ /* Read the response: ESC [ rows ; cols R */
+ while (i < sizeof(buf)-1) {
+ if (read(ifd,buf+i,1) != 1) break;
+ if (buf[i] == 'R') break;
+ i++;
+ }
+ buf[i] = '\0';
+
+ /* Parse it. */
+ if (buf[0] != ESC || buf[1] != '[') return -1;
+ if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
+ return cols;
+}
+
+/* Try to get the number of columns in the current terminal, or assume 80
+ * if it fails. */
+static int getColumns(int ifd, int ofd) {
+ struct winsize ws;
+
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ /* ioctl() failed. Try to query the terminal itself. */
+ int start, cols;
+
+ /* Get the initial position so we can restore it later. */
+ start = getCursorPosition(ifd,ofd);
+ if (start == -1) goto failed;
+
+ /* Go to right margin and get position. */
+ if (write(ofd,"\x1b[999C",6) != 6) goto failed;
+ cols = getCursorPosition(ifd,ofd);
+ if (cols == -1) goto failed;
+
+ /* Restore position. */
+ if (cols > start) {
+ char seq[32];
+ snprintf(seq,32,"\x1b[%dD",cols-start);
+ if (write(ofd,seq,strlen(seq)) == -1) {
+ /* Can't recover... */
+ }
+ }
+ return cols;
+ } else {
+ return ws.ws_col;
+ }
+
+failed:
+ return 80;
+}
+
+/* Clear the screen. Used to handle ctrl+l */
+void linenoiseClearScreen(void) {
+ if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+ /* nothing to do, just to avoid warning. */
+ }
+}
+
+/* Beep, used for completion when there is nothing to complete or when all
+ * the choices were already shown. */
+static void linenoiseBeep(void) {
+ fprintf(stderr, "\x7");
+ fflush(stderr);
+}
+
+/* ============================== Completion ================================ */
+
+/* Free a list of completion option populated by linenoiseAddCompletion(). */
+static void freeCompletions(linenoiseCompletions *lc) {
+ size_t i;
+ for (i = 0; i < lc->len; i++)
+ free(lc->cvec[i]);
+ if (lc->cvec != NULL)
+ free(lc->cvec);
+}
+
+/* This is an helper function for linenoiseEdit() and is called when the
+ * user types the <tab> key in order to complete the string currently in the
+ * input.
+ *
+ * The state of the editing is encapsulated into the pointed linenoiseState
+ * structure as described in the structure definition. */
+static char completeLine(struct linenoiseState *ls) {
+ linenoiseCompletions lc = {0, 0, NULL};
+ int nread, nwritten, hint_len, hint_line_count, char_count;
+ char c = 0, *common, *hint;
+ struct winsize w;
+
+ /* Hint is only the string after the last space */
+ hint = strrchr(ls->buf, ' ');
+ if (!hint) {
+ hint = ls->buf;
+ } else {
+ ++hint;
+ }
+
+ completionCallback(ls->buf, hint, &lc);
+ if (lc.len == 0) {
+ linenoiseBeep();
+ } else {
+ unsigned int i, j;
+
+ /* Learn the longest common part */
+ common = strdup(lc.cvec[0]);
+ for (i = 1; i < lc.len; ++i) {
+ for (j = 0; j < strlen(lc.cvec[i]); ++j) {
+ if (lc.cvec[i][j] != common[j]) {
+ break;
+ }
+ }
+ common[j] = '\0';
+ }
+
+ /* Path completions have a different hint */
+ if (lc.path && strrchr(hint, '/')) {
+ hint = strrchr(hint, '/');
+ ++hint;
+ }
+
+ /* Show completion */
+ if ((lc.len == 1) && (common[strlen(common) - 1] != '/')) {
+ nwritten = snprintf(hint, ls->buflen - (hint - ls->buf), "%s ", common);
+ } else {
+ nwritten = snprintf(hint, ls->buflen - (hint - ls->buf), "%s", common);
+ }
+ free(common);
+ ls->len = ls->pos = (hint - ls->buf) + nwritten;
+ linenoiseRefreshLine();
+
+ /* A single hint */
+ if (lc.len == 1) {
+ freeCompletions(&lc);
+ return 0;
+ }
+
+ /* Read a char */
+ nread = read(ls->ifd,&c,1);
+ if (nread <= 0) {
+ freeCompletions(&lc);
+ return -1;
+ }
+
+ /* Not a tab */
+ if (c != 9) {
+ freeCompletions(&lc);
+ return c;
+ }
+
+ /* Learn terminal window size */
+ ioctl(ls->ifd, TIOCGWINSZ, &w);
+
+ /* Learn the longest hint */
+ hint_len = strlen(lc.cvec[0]);
+ for (i = 1; i < lc.len; ++i) {
+ if (strlen(lc.cvec[i]) > (unsigned)hint_len) {
+ hint_len = strlen(lc.cvec[i]);
+ }
+ }
+
+ /* Learn the number of hints that fit a line */
+ hint_line_count = 0;
+ do {
+ /* Still fits, always at least one hint */
+ ++hint_line_count;
+
+ char_count = 0;
+ if (hint_line_count) {
+ char_count += hint_line_count * (hint_len + 2);
+ }
+ char_count += hint_len;
+
+ /* Too much */
+ } while (char_count <= w.ws_col);
+
+ while (c == 9) {
+ /* Second tab */
+ linenoiseDisableRawMode(ls->ifd);
+ printf("\n");
+ for (i = 0; i < lc.len; ++i) {
+ printf("%-*s", hint_len, lc.cvec[i]);
+ /* Line full or last hint */
+ if (((i + 1) % hint_line_count == 0) || (i == lc.len - 1)) {
+ printf("\n");
+ } else {
+ printf(" ");
+ }
+ }
+ linenoiseEnableRawMode(ls->ifd);
+ linenoiseRefreshLine();
+
+ /* Read a char */
+ nread = read(ls->ifd,&c,1);
+ if (nread <= 0) {
+ freeCompletions(&lc);
+ return -1;
+ }
+ }
+ }
+
+ freeCompletions(&lc);
+ return c; /* Return last read character */
+}
+
+/* Register a callback function to be called for tab-completion. */
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+ completionCallback = fn;
+}
+
+/* This function can be called in user completion callback to fill
+ * path completion for them. hint parameter is actually the whole path
+ * and buf is unused, but included to match the completion callback prototype. */
+void linenoisePathCompletion(const char *buf, const char *hint, linenoiseCompletions *lc) {
+ const char *ptr;
+ char *full_path, *hint_ptr, match[FILENAME_MAX + 2];
+ DIR *dir;
+ struct dirent *ent;
+ struct stat st;
+
+ (void)buf;
+
+ lc->path = 1;
+
+ ptr = strrchr(hint, '/');
+
+ /* new relative path */
+ if (ptr == NULL) {
+ full_path = malloc(2 + FILENAME_MAX + 1);
+ strcpy(full_path, "./");
+
+ ptr = hint;
+ } else {
+ full_path = malloc((int)(ptr - hint) + FILENAME_MAX + 1);
+ ++ptr;
+ sprintf(full_path, "%.*s", (int)(ptr - hint), hint);
+ }
+ hint_ptr = full_path + strlen(full_path);
+
+ dir = opendir(full_path);
+ if (dir == NULL) {
+ free(full_path);
+ return;
+ }
+
+ while ((ent = readdir(dir))) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+
+ if (!strncmp(ptr, ent->d_name, strlen(ptr))) {
+ /* is it a directory? */
+ strcpy(hint_ptr, ent->d_name);
+ if (stat(full_path, &st)) {
+ /* skip this item */
+ continue;
+ }
+
+ strcpy(match, ent->d_name);
+ if (S_ISDIR(st.st_mode)) {
+ strcat(match, "/");
+ }
+
+ linenoiseAddCompletion(lc, match);
+ }
+ }
+
+ free(full_path);
+ closedir(dir);
+}
+
+/* This function is used by the callback function registered by the user
+ * in order to add completion options given the input string when the
+ * user typed <tab>. See the example.c source code for a very easy to
+ * understand example. */
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+ size_t len = strlen(str);
+ char *copy, **cvec;
+
+ copy = malloc(len+1);
+ if (copy == NULL) return;
+ memcpy(copy,str,len+1);
+ cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+ if (cvec == NULL) {
+ free(copy);
+ return;
+ }
+ lc->cvec = cvec;
+ lc->cvec[lc->len++] = copy;
+}
+
+/* =========================== Line editing ================================= */
+
+/* We define a very simple "append buffer" structure, that is an heap
+ * allocated string where we can append to. This is useful in order to
+ * write all the escape sequences in a buffer and flush them to the standard
+ * output in a single call, to avoid flickering effects. */
+struct abuf {
+ char *b;
+ int len;
+};
+
+static void abInit(struct abuf *ab) {
+ ab->b = NULL;
+ ab->len = 0;
+}
+
+static void abAppend(struct abuf *ab, const char *s, int len) {
+ char *new = realloc(ab->b,ab->len+len);
+
+ if (new == NULL) return;
+ memcpy(new+ab->len,s,len);
+ ab->b = new;
+ ab->len += len;
+}
+
+static void abFree(struct abuf *ab) {
+ free(ab->b);
+}
+
+/* Single line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshSingleLine(struct linenoiseState *l) {
+ char seq[64];
+ size_t plen = strlen(l->prompt);
+ int fd = l->ofd;
+ char *buf = l->buf;
+ size_t len = l->len;
+ size_t pos = l->pos;
+ struct abuf ab;
+
+ while((plen+pos) >= l->cols) {
+ buf++;
+ len--;
+ pos--;
+ }
+ while (plen+len > l->cols) {
+ len--;
+ }
+
+ abInit(&ab);
+ /* Cursor to left edge */
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+ /* Write the prompt and the current buffer content */
+ abAppend(&ab,l->prompt,strlen(l->prompt));
+ abAppend(&ab,buf,len);
+ /* Erase to right */
+ snprintf(seq,64,"\x1b[0K");
+ abAppend(&ab,seq,strlen(seq));
+ /* Move cursor to original position. */
+ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
+ abAppend(&ab,seq,strlen(seq));
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+ abFree(&ab);
+}
+
+/* Multi line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshMultiLine(struct linenoiseState *l) {
+ char seq[64];
+ int plen = strlen(l->prompt);
+ int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
+ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */
+ int rpos2; /* rpos after refresh. */
+ int col; /* colum position, zero-based. */
+ int old_rows = l->maxrows;
+ int fd = l->ofd, j;
+ struct abuf ab;
+
+ /* Update maxrows if needed. */
+ if (rows > (int)l->maxrows) l->maxrows = rows;
+
+ /* First step: clear all the lines used before. To do so start by
+ * going to the last row. */
+ abInit(&ab);
+ if (old_rows-rpos > 0) {
+ lndebug("go down %d", old_rows-rpos);
+ snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Now for every row clear it, go up. */
+ for (j = 0; j < old_rows-1; j++) {
+ lndebug("clear+up");
+ snprintf(seq,64,"\r\x1b[0K\x1b[1A");
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Clean the top line. */
+ lndebug("clear");
+ snprintf(seq,64,"\r\x1b[0K");
+ abAppend(&ab,seq,strlen(seq));
+
+ /* Write the prompt and the current buffer content */
+ abAppend(&ab,l->prompt,strlen(l->prompt));
+ abAppend(&ab,l->buf,l->len);
+
+ /* If we are at the very end of the screen with our prompt, we need to
+ * emit a newline and move the prompt to the first column. */
+ if (l->pos &&
+ l->pos == l->len &&
+ (l->pos+plen) % l->cols == 0)
+ {
+ lndebug("<newline>");
+ abAppend(&ab,"\n",1);
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+ rows++;
+ if (rows > (int)l->maxrows) l->maxrows = rows;
+ }
+
+ /* Move cursor to right position. */
+ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
+ lndebug("rpos2 %d", rpos2);
+
+ /* Go up till we reach the expected positon. */
+ if (rows-rpos2 > 0) {
+ lndebug("go-up %d", rows-rpos2);
+ snprintf(seq,64,"\x1b[%dA", rows-rpos2);
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Set column. */
+ col = (plen+(int)l->pos) % (int)l->cols;
+ lndebug("set col %d", 1+col);
+ if (col)
+ snprintf(seq,64,"\r\x1b[%dC", col);
+ else
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+
+ lndebug("\n");
+ l->oldpos = l->pos;
+
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+ abFree(&ab);
+}
+
+/* Calls the two low level functions refreshSingleLine() or
+ * refreshMultiLine() according to the selected mode. */
+void linenoiseRefreshLine(void) {
+ /* Update columns in case the terminal was resized */
+ lss.cols = getColumns(STDIN_FILENO, STDOUT_FILENO);
+
+ if (mlmode)
+ refreshMultiLine(&lss);
+ else
+ refreshSingleLine(&lss);
+}
+
+/* Insert the character 'c' at cursor current position.
+ *
+ * On error writing to the terminal -1 is returned, otherwise 0. */
+int linenoiseEditInsert(struct linenoiseState *l, char c) {
+ if (l->len < l->buflen) {
+ if (l->len == l->pos) {
+ l->buf[l->pos] = c;
+ l->pos++;
+ l->len++;
+ l->buf[l->len] = '\0';
+ if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {
+ /* Avoid a full update of the line in the
+ * trivial case. */
+ if (write(l->ofd,&c,1) == -1) return -1;
+ } else {
+ linenoiseRefreshLine();
+ }
+ } else {
+ memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
+ l->buf[l->pos] = c;
+ l->len++;
+ l->pos++;
+ l->buf[l->len] = '\0';
+ linenoiseRefreshLine();
+ }
+ }
+ return 0;
+}
+
+/* Move cursor on the left. */
+void linenoiseEditMoveLeft(struct linenoiseState *l) {
+ if (l->pos > 0) {
+ l->pos--;
+ linenoiseRefreshLine();
+ }
+}
+
+/* Move cursor on the right. */
+void linenoiseEditMoveRight(struct linenoiseState *l) {
+ if (l->pos != l->len) {
+ l->pos++;
+ linenoiseRefreshLine();
+ }
+}
+
+/* Move cursor to the start of the line. */
+void linenoiseEditMoveHome(struct linenoiseState *l) {
+ if (l->pos != 0) {
+ l->pos = 0;
+ linenoiseRefreshLine();
+ }
+}
+
+/* Move cursor to the end of the line. */
+void linenoiseEditMoveEnd(struct linenoiseState *l) {
+ if (l->pos != l->len) {
+ l->pos = l->len;
+ linenoiseRefreshLine();
+ }
+}
+
+/* Substitute the currently edited line with the next or previous history
+ * entry as specified by 'dir'. */
+#define LINENOISE_HISTORY_NEXT 0
+#define LINENOISE_HISTORY_PREV 1
+void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
+ if (history_len > 1) {
+ /* Update the current history entry before to
+ * overwrite it with the next one. */
+ free(history[history_len - 1 - l->history_index]);
+ history[history_len - 1 - l->history_index] = strdup(l->buf);
+ /* Show the new entry */
+ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
+ if (l->history_index < 0) {
+ l->history_index = 0;
+ return;
+ } else if (l->history_index >= history_len) {
+ l->history_index = history_len-1;
+ return;
+ }
+ strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
+ l->buf[l->buflen-1] = '\0';
+ l->len = l->pos = strlen(l->buf);
+ linenoiseRefreshLine();
+ }
+}
+
+/* Delete the character at the right of the cursor without altering the cursor
+ * position. Basically this is what happens with the "Delete" keyboard key. */
+void linenoiseEditDelete(struct linenoiseState *l) {
+ if (l->len > 0 && l->pos < l->len) {
+ memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1);
+ l->len--;
+ l->buf[l->len] = '\0';
+ linenoiseRefreshLine();
+ }
+}
+
+/* Backspace implementation. */
+void linenoiseEditBackspace(struct linenoiseState *l) {
+ if (l->pos > 0 && l->len > 0) {
+ memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos);
+ l->pos--;
+ l->len--;
+ l->buf[l->len] = '\0';
+ linenoiseRefreshLine();
+ }
+}
+
+/* Delete the previosu word, maintaining the cursor at the start of the
+ * current word. */
+void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
+ size_t old_pos = l->pos;
+ size_t diff;
+
+ while (l->pos > 0 && l->buf[l->pos-1] == ' ')
+ l->pos--;
+ while (l->pos > 0 && l->buf[l->pos-1] != ' ')
+ l->pos--;
+ diff = old_pos - l->pos;
+ memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
+ l->len -= diff;
+ linenoiseRefreshLine();
+}
+
+/* This function is the core of the line editing capability of linenoise.
+ * It expects 'fd' to be already in "raw mode" so that every key pressed
+ * will be returned ASAP to read().
+ *
+ * The resulting string is put into 'buf' when the user type enter, or
+ * when ctrl+d is typed.
+ *
+ * The function returns the length of the current buffer. */
+static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+{
+ /* Populate the linenoise state that we pass to functions implementing
+ * specific editing functionalities. */
+ lss.ifd = stdin_fd;
+ lss.ofd = stdout_fd;
+ lss.buf = buf;
+ lss.buflen = buflen;
+ lss.prompt = prompt;
+ lss.plen = strlen(prompt);
+ lss.oldpos = lss.pos = 0;
+ lss.len = 0;
+ lss.cols = getColumns(stdin_fd, stdout_fd);
+ lss.maxrows = 0;
+ lss.history_index = 0;
+
+ /* Buffer starts empty. */
+ lss.buf[0] = '\0';
+ lss.buflen--; /* Make sure there is always space for the nulterm */
+
+ /* The latest history entry is always our current buffer, that
+ * initially is just an empty string. */
+ linenoiseHistoryAdd("");
+
+ if (write(lss.ofd,prompt,lss.plen) == -1) return -1;
+ while(1) {
+ char c = 0;
+ int nread;
+ char seq[3];
+
+ nread = read(lss.ifd,&c,sizeof c);
+ if (nread <= 0) return lss.len;
+
+ /* Only autocomplete when the callback is set. It returns < 0 when
+ * there was an error reading from fd. Otherwise it will return the
+ * character that should be handled next. */
+ if (c == 9 && completionCallback != NULL) {
+ c = completeLine(&lss);
+ /* Return on errors */
+ if (c < 0) return lss.len;
+ /* Read next character when 0 */
+ if (c == 0) continue;
+ }
+
+ switch(c) {
+ case ENTER: /* enter */
+ history_len--;
+ free(history[history_len]);
+ if (mlmode) linenoiseEditMoveEnd(&lss);
+ return (int)lss.len;
+ case CTRL_C: /* ctrl-c */
+ errno = EAGAIN;
+ return -1;
+ case BACKSPACE: /* backspace */
+ case 8: /* ctrl-h */
+ linenoiseEditBackspace(&lss);
+ break;
+ case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
+ line is empty, act as end-of-file. */
+ if (lss.len > 0) {
+ linenoiseEditDelete(&lss);
+ } else {
+ history_len--;
+ free(history[history_len]);
+ return -1;
+ }
+ break;
+ case CTRL_T: /* ctrl-t, swaps current character with previous. */
+ if (lss.pos > 0 && lss.pos < lss.len) {
+ int aux = buf[lss.pos-1];
+ buf[lss.pos-1] = buf[lss.pos];
+ buf[lss.pos] = aux;
+ if (lss.pos != lss.len-1) lss.pos++;
+ linenoiseRefreshLine();
+ }
+ break;
+ case CTRL_B: /* ctrl-b */
+ linenoiseEditMoveLeft(&lss);
+ break;
+ case CTRL_F: /* ctrl-f */
+ linenoiseEditMoveRight(&lss);
+ break;
+ case CTRL_P: /* ctrl-p */
+ linenoiseEditHistoryNext(&lss, LINENOISE_HISTORY_PREV);
+ break;
+ case CTRL_N: /* ctrl-n */
+ linenoiseEditHistoryNext(&lss, LINENOISE_HISTORY_NEXT);
+ break;
+ case ESC: /* escape sequence */
+ /* Read the next two bytes representing the escape sequence.
+ * Use two calls to handle slow terminals returning the two
+ * chars at different times. */
+ if (read(lss.ifd,seq,1) == -1) break;
+ if (read(lss.ifd,seq+1,1) == -1) break;
+
+ /* ESC [ sequences. */
+ if (seq[0] == '[') {
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ /* Extended escape, read additional byte. */
+ if (read(lss.ifd, seq + 2, 1) == -1) break;
+ if ((seq[1] == '3') && (seq[2] == '~')) {
+ /* Delete key. */
+ linenoiseEditDelete(&lss);
+ }
+ } else {
+ switch(seq[1]) {
+ case 'A': /* Up */
+ linenoiseEditHistoryNext(&lss, LINENOISE_HISTORY_PREV);
+ break;
+ case 'B': /* Down */
+ linenoiseEditHistoryNext(&lss, LINENOISE_HISTORY_NEXT);
+ break;
+ case 'C': /* Right */
+ linenoiseEditMoveRight(&lss);
+ break;
+ case 'D': /* Left */
+ linenoiseEditMoveLeft(&lss);
+ break;
+ case 'H': /* Home */
+ linenoiseEditMoveHome(&lss);
+ break;
+ case 'F': /* End*/
+ linenoiseEditMoveEnd(&lss);
+ break;
+ }
+ }
+ }
+
+ /* ESC O sequences. */
+ else if (seq[0] == 'O') {
+ switch(seq[1]) {
+ case 'H': /* Home */
+ linenoiseEditMoveHome(&lss);
+ break;
+ case 'F': /* End*/
+ linenoiseEditMoveEnd(&lss);
+ break;
+ }
+ }
+ break;
+ default:
+ if (linenoiseEditInsert(&lss,c)) return -1;
+ break;
+ case CTRL_U: /* Ctrl+u, delete the whole line. */
+ buf[0] = '\0';
+ lss.pos = lss.len = 0;
+ linenoiseRefreshLine();
+ break;
+ case CTRL_K: /* Ctrl+k, delete from current to end of line. */
+ buf[lss.pos] = '\0';
+ lss.len = lss.pos;
+ linenoiseRefreshLine();
+ break;
+ case CTRL_A: /* Ctrl+a, go to the start of the line */
+ linenoiseEditMoveHome(&lss);
+ break;
+ case CTRL_E: /* ctrl+e, go to the end of the line */
+ linenoiseEditMoveEnd(&lss);
+ break;
+ case CTRL_L: /* ctrl+l, clear screen */
+ linenoiseClearScreen();
+ linenoiseRefreshLine();
+ break;
+ case CTRL_W: /* ctrl+w, delete previous word */
+ linenoiseEditDeletePrevWord(&lss);
+ break;
+ }
+ }
+ return lss.len;
+}
+
+/* This special mode is used by linenoise in order to print scan codes
+ * on screen for debugging / development purposes. It is implemented
+ * by the linenoise_example program using the --keycodes option. */
+void linenoisePrintKeyCodes(void) {
+ char quit[4];
+
+ printf("Linenoise key codes debugging mode.\n"
+ "Press keys to see scan codes. Type 'quit' at any time to exit.\n");
+ if (linenoiseEnableRawMode(STDIN_FILENO) == -1) return;
+ memset(quit,' ',4);
+ while(1) {
+ char c;
+ int nread;
+
+ nread = read(STDIN_FILENO,&c,1);
+ if (nread <= 0) continue;
+ memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
+ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
+ if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
+
+ printf("'%c' %02x (%d) (type quit to exit)\n",
+ isprint(c) ? c : '?', (int)c, (int)c);
+ printf("\r"); /* Go left edge manually, we are in raw mode. */
+ fflush(stdout);
+ }
+ linenoiseDisableRawMode(STDIN_FILENO);
+}
+
+/* This function calls the line editing function linenoiseEdit() using
+ * the STDIN file descriptor set in raw mode. */
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+ int count;
+
+ if (buflen == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!isatty(STDIN_FILENO)) {
+ /* Not a tty: read from file / pipe. */
+ if (fgets(buf, buflen, stdin) == NULL) return -1;
+ count = strlen(buf);
+ if (count && buf[count-1] == '\n') {
+ count--;
+ buf[count] = '\0';
+ }
+ } else {
+ /* Interactive editing. */
+ if (linenoiseEnableRawMode(STDIN_FILENO) == -1) return -1;
+ count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
+ linenoiseDisableRawMode(STDIN_FILENO);
+ printf("\n");
+ }
+ return count;
+}
+
+/* The high level function that is the main API of the linenoise library.
+ * This function checks if the terminal has basic capabilities, just checking
+ * for a blacklist of stupid terminals, and later either calls the line
+ * editing function or uses dummy fgets() so that you will be able to type
+ * something even in the most desperate of the conditions. */
+char *linenoise(const char *prompt) {
+ char buf[LINENOISE_MAX_LINE];
+ int count;
+
+ if (isUnsupportedTerm()) {
+ size_t len;
+
+ printf("%s",prompt);
+ fflush(stdout);
+ if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+ len = strlen(buf);
+ while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+ len--;
+ buf[len] = '\0';
+ }
+ return strdup(buf);
+ } else {
+ count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+ if (count == -1) return NULL;
+ return strdup(buf);
+ }
+}
+
+/* ================================ History ================================= */
+
+/* Free the history, but does not reset it. Only used when we have to
+ * exit() to avoid memory leaks are reported by valgrind & co. */
+static void freeHistory(void) {
+ if (history) {
+ int j;
+
+ for (j = 0; j < history_len; j++)
+ free(history[j]);
+ free(history);
+ }
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+ linenoiseDisableRawMode(STDIN_FILENO);
+ freeHistory();
+}
+
+/* This is the API call to add a new entry in the linenoise history.
+ * It uses a fixed array of char pointers that are shifted (memmoved)
+ * when the history max length is reached in order to remove the older
+ * entry and make room for the new one, so it is not exactly suitable for huge
+ * histories, but will work well for a few hundred of entries.
+ *
+ * Using a circular buffer is smarter, but a bit more complex to handle. */
+int linenoiseHistoryAdd(const char *line) {
+ char *linecopy;
+
+ if (history_max_len == 0) return 0;
+
+ /* Initialization on first call. */
+ if (history == NULL) {
+ history = malloc(sizeof(char*)*history_max_len);
+ if (history == NULL) return 0;
+ memset(history,0,(sizeof(char*)*history_max_len));
+ }
+
+ /* Don't add duplicated lines. */
+ if (history_len && !strcmp(history[history_len-1], line)) return 0;
+
+ /* Add an heap allocated copy of the line in the history.
+ * If we reached the max length, remove the older line. */
+ linecopy = strdup(line);
+ if (!linecopy) return 0;
+ if (history_len == history_max_len) {
+ free(history[0]);
+ memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+ history_len--;
+ }
+ history[history_len] = linecopy;
+ history_len++;
+ return 1;
+}
+
+/* Set the maximum length for the history. This function can be called even
+ * if there is already some history, the function will make sure to retain
+ * just the latest 'len' elements if the new history length value is smaller
+ * than the amount of items already inside the history. */
+int linenoiseHistorySetMaxLen(int len) {
+ char **new;
+
+ if (len < 1) return 0;
+ if (history) {
+ int tocopy = history_len;
+
+ new = malloc(sizeof(char*)*len);
+ if (new == NULL) return 0;
+
+ /* If we can't copy everything, free the elements we'll not use. */
+ if (len < tocopy) {
+ int j;
+
+ for (j = 0; j < tocopy-len; j++) free(history[j]);
+ tocopy = len;
+ }
+ memset(new,0,sizeof(char*)*len);
+ memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
+ free(history);
+ history = new;
+ }
+ history_max_len = len;
+ if (history_len > history_max_len)
+ history_len = history_max_len;
+ return 1;
+}
+
+/* Save the history in the specified file. On success 0 is returned
+ * otherwise -1 is returned. */
+int linenoiseHistorySave(const char *filename) {
+ FILE *fp = fopen(filename,"w");
+ int j;
+
+ if (fp == NULL) return -1;
+ for (j = 0; j < history_len; j++)
+ fprintf(fp,"%s\n",history[j]);
+ fclose(fp);
+ return 0;
+}
+
+/* Load the history from the specified file. If the file does not exist
+ * zero is returned and no operation is performed.
+ *
+ * If the file exists and the operation succeeded 0 is returned, otherwise
+ * on error -1 is returned. */
+int linenoiseHistoryLoad(const char *filename) {
+ FILE *fp = fopen(filename,"r");
+ char buf[LINENOISE_MAX_LINE];
+
+ if (fp == NULL) return -1;
+
+ while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
+ char *p;
+
+ p = strchr(buf,'\r');
+ if (!p) p = strchr(buf,'\n');
+ if (p) *p = '\0';
+ linenoiseHistoryAdd(buf);
+ }
+ fclose(fp);
+ return 0;
+}
diff --git a/tools/lint/linenoise/linenoise.h b/tools/lint/linenoise/linenoise.h
new file mode 100644
index 0000000..8362b1f
--- /dev/null
+++ b/tools/lint/linenoise/linenoise.h
@@ -0,0 +1,94 @@
+/* linenoise.h -- VERSION 1.0
+ *
+ * Guerrilla line editing library against the idea that a line editing lib
+ * needs to be 20,000 lines of C code.
+ *
+ * See linenoise.c for more information.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINENOISE_H
+#define __LINENOISE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct linenoiseState {
+ int ifd; /* Terminal stdin file descriptor. */
+ int ofd; /* Terminal stdout file descriptor. */
+ char *buf; /* Edited line buffer. */
+ size_t buflen; /* Edited line buffer size. */
+ const char *prompt; /* Prompt to display. */
+ size_t plen; /* Prompt length. */
+ size_t pos; /* Current cursor position. */
+ size_t oldpos; /* Previous refresh cursor position. */
+ size_t len; /* Current edited line length. */
+ size_t cols; /* Number of columns in terminal. */
+ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
+ int rawmode;
+ int history_index; /* The history index we are currently editing. */
+};
+
+extern struct linenoiseState lss;
+
+typedef struct linenoiseCompletions {
+ int path;
+ size_t len;
+ char **cvec;
+} linenoiseCompletions;
+
+typedef void(linenoiseCompletionCallback)(const char *, const char *, linenoiseCompletions *);
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
+void linenoiseAddCompletion(linenoiseCompletions *, const char *);
+
+char *linenoise(const char *prompt);
+int linenoiseHistoryAdd(const char *line);
+int linenoiseHistorySetMaxLen(int len);
+int linenoiseHistorySave(const char *filename);
+int linenoiseHistoryLoad(const char *filename);
+void linenoiseClearScreen(void);
+void linenoiseSetMultiLine(int ml);
+void linenoisePrintKeyCodes(void);
+
+void linenoisePathCompletion(const char *, const char *, linenoiseCompletions *);
+void linenoiseRefreshLine(void);
+int linenoiseEnableRawMode(int fd);
+void linenoiseDisableRawMode(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LINENOISE_H */
diff --git a/tools/lint/main.c b/tools/lint/main.c
new file mode 100644
index 0000000..9f0d027
--- /dev/null
+++ b/tools/lint/main.c
@@ -0,0 +1,102 @@
+/**
+ * @file main.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "cmd.h"
+#include "common.h"
+#include "completion.h"
+#include "configuration.h"
+#include "linenoise/linenoise.h"
+
+int done;
+struct ly_ctx *ctx = NULL;
+
+/* main_ni.c */
+int main_ni(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ char *cmdline;
+ int cmdlen;
+
+ if (argc > 1) {
+ /* run in non-interactive mode */
+ return main_ni(argc, argv);
+ }
+
+ /* continue in interactive mode */
+ linenoiseSetCompletionCallback(complete_cmd);
+ load_config();
+
+ if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {
+ YLMSG_E("Failed to create context.\n");
+ return 1;
+ }
+
+ while (!done) {
+ uint8_t executed = 0;
+
+ /* get the command from user */
+ cmdline = linenoise(PROMPT);
+
+ /* EOF -> exit */
+ if (cmdline == NULL) {
+ done = 1;
+ cmdline = strdup("quit");
+ }
+
+ /* empty line -> wait for another command */
+ if (*cmdline == '\0') {
+ free(cmdline);
+ continue;
+ }
+
+ /* isolate the command word. */
+ for (cmdlen = 0; cmdline[cmdlen] && (cmdline[cmdlen] != ' '); cmdlen++) {}
+
+ /* execute the command if any valid specified */
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (strncmp(cmdline, commands[i].name, (size_t)cmdlen) || (commands[i].name[cmdlen] != '\0')) {
+ continue;
+ }
+
+ commands[i].func(&ctx, cmdline);
+ executed = 1;
+ break;
+ }
+
+ if (!executed) {
+ /* if unknown command specified, tell it to user */
+ YLMSG_E("Unknown command \"%.*s\", type 'help' for more information.\n", cmdlen, cmdline);
+ }
+
+ linenoiseHistoryAdd(cmdline);
+ free(cmdline);
+ }
+
+ store_config();
+ ly_ctx_destroy(ctx);
+
+ return 0;
+}
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
new file mode 100644
index 0000000..04c2340
--- /dev/null
+++ b/tools/lint/main_ni.c
@@ -0,0 +1,1027 @@
+/**
+ * @file main_ni.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool - non-interactive code
+ *
+ * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+
+#include "libyang.h"
+#include "plugins_exts.h"
+
+#include "common.h"
+#include "out.h"
+#include "tools/config.h"
+
+/**
+ * @brief Context structure to hold and pass variables in a structured form.
+ */
+struct context {
+ /* libyang context for the run */
+ const char *yang_lib_file;
+ uint16_t ctx_options;
+ struct ly_ctx *ctx;
+
+ /* prepared output (--output option or stdout by default) */
+ struct ly_out *out;
+
+ char *searchpaths;
+
+ /* options flags */
+ uint8_t list; /* -l option to print list of schemas */
+
+ /* line length for 'tree' format */
+ size_t line_length; /* --tree-line-length */
+
+ /*
+ * schema
+ */
+ /* set schema modules' features via --features option (struct schema_features *) */
+ struct ly_set schema_features;
+
+ /* set of loaded schema modules (struct lys_module *) */
+ struct ly_set schema_modules;
+
+ /* options to parse and print schema modules */
+ uint32_t schema_parse_options;
+ uint32_t schema_print_options;
+
+ /* specification of printing schema node subtree, option --schema-node */
+ const char *schema_node_path;
+ const struct lysc_node *schema_node;
+ const char *submodule;
+
+ /* name of file containing explicit context passed to callback
+ * for schema-mount extension. This also causes a callback to
+ * be registered.
+ */
+ char *schema_context_filename;
+
+ /* value of --format in case of schema format */
+ LYS_OUTFORMAT schema_out_format;
+ ly_bool feature_param_format;
+
+ /*
+ * data
+ */
+ /* various options based on --type option */
+ enum lyd_type data_type;
+ uint32_t data_parse_options;
+ uint32_t data_validate_options;
+ uint32_t data_print_options;
+
+ /* flag for --merge option */
+ uint8_t data_merge;
+
+ /* value of --format in case of data format */
+ LYD_FORMAT data_out_format;
+
+ /* input data files (struct cmdline_file *) */
+ struct ly_set data_inputs;
+
+ /* storage for --operational */
+ struct cmdline_file data_operational;
+
+ /* storage for --reply-rpc */
+ struct cmdline_file reply_rpc;
+};
+
+static void
+erase_context(struct context *c)
+{
+ /* data */
+ ly_set_erase(&c->data_inputs, free_cmdline_file);
+ ly_in_free(c->data_operational.in, 1);
+
+ /* schema */
+ ly_set_erase(&c->schema_features, free_features);
+ ly_set_erase(&c->schema_modules, NULL);
+
+ /* context */
+ free(c->searchpaths);
+ c->searchpaths = NULL;
+
+ ly_out_free(c->out, NULL, 0);
+ ly_ctx_destroy(c->ctx);
+
+ if (c->schema_context_filename) {
+ free(c->schema_context_filename);
+ }
+}
+
+static void
+version(void)
+{
+ printf("yanglint %s\n", PROJECT_VERSION);
+}
+
+static void
+help(int shortout)
+{
+
+ printf("Example usage:\n"
+ " yanglint [-f { yang | yin | info}] <schema>...\n"
+ " Validates the YANG module <schema>(s) and all its dependencies, optionally printing\n"
+ " them in the specified format.\n\n"
+ " yanglint [-f { xml | json }] <schema>... <file>...\n"
+ " Validates the YANG modeled data <file>(s) according to the <schema>(s) optionally\n"
+ " printing them in the specified format.\n\n"
+ " yanglint -t (nc-)rpc/notif [-O <operational-file>] <schema>... <file>\n"
+ " Validates the YANG/NETCONF RPC/notification <file> according to the <schema>(s) using\n"
+ " <operational-file> with possible references to the operational datastore data.\n\n"
+ " yanglint -t nc-reply -R <rpc-file> [-O <operational-file>] <schema>... <file>\n"
+ " Validates the NETCONF rpc-reply <file> of RPC <rpc-file> according to the <schema>(s)\n"
+ " using <operational-file> with possible references to the operational datastore data.\n\n"
+ " yanglint\n"
+ " Starts interactive mode with more features.\n\n");
+
+ if (shortout) {
+ return;
+ }
+ printf("Options:\n"
+ " -h, --help Show this help message and exit.\n"
+ " -v, --version Show version number and exit.\n"
+ " -V, --verbose Increase libyang verbosity and show verbose messages. If specified\n"
+ " a second time, show even debug messages.\n"
+ " -Q, --quiet Decrease libyang verbosity and hide warnings. If specified a second\n"
+ " time, hide errors so no libyang messages are printed.\n");
+
+ printf(" -f FORMAT, --format=FORMAT\n"
+ " Convert input into FORMAT. Supported formats: \n"
+ " yang, yin, tree, info and feature-param for schemas,\n"
+ " xml, json, and lyb for data.\n\n");
+
+ printf(" -p PATH, --path=PATH\n"
+ " Search path for schema (YANG/YIN) modules. The option can be\n"
+ " used multiple times. The current working directory and the\n"
+ " path of the module being added is used implicitly.\n\n");
+
+ printf(" -D, --disable-searchdir\n"
+ " Do not implicitly search in current working directory for\n"
+ " schema modules. If specified a second time, do not even\n"
+ " search in the module directory (all modules must be \n"
+ " explicitly specified).\n\n");
+
+ printf(" -F FEATURES, --features=FEATURES\n"
+ " Specific module features to support in the form <module-name>:(<feature>,)*\n"
+ " Use <feature> '*' to enable all features of a module. This option can be\n"
+ " specified multiple times, to enable features in multiple modules. If this\n"
+ " option is not specified, all the features in all the implemented modules\n"
+ " are enabled.\n\n");
+
+ printf(" -i, --make-implemented\n"
+ " Make the imported modules \"referenced\" from any loaded\n"
+ " module also implemented. If specified a second time, all the\n"
+ " modules are set implemented.\n\n");
+
+ printf(" -P PATH, --schema-node=PATH\n"
+ " Print only the specified subtree of the schema.\n"
+ " The PATH is the XPath subset mentioned in documentation as\n"
+ " the Path format. The option can be combined with --single-node\n"
+ " option to print information only about the specified node.\n"
+ " -q, --single-node\n"
+ " Supplement to the --schema-node option to print information\n"
+ " only about a single node specified as PATH argument.\n\n");
+
+ printf(" -s SUBMODULE, --submodule=SUBMODULE\n"
+ " Print the specific submodule instead of the main module.\n\n");
+
+ printf(" -x FILE, --ext-data=FILE\n"
+ " File containing the specific data required by an extension. Required by\n"
+ " the schema-mount extension, for example, when the operational data are\n"
+ " expected in the file. File format is guessed.\n\n");
+
+ printf(" -n, --not-strict\n"
+ " Do not require strict data parsing (silently skip unknown data),\n"
+ " has no effect for schemas.\n\n");
+
+ printf(" -e, --present Validate only with the schema modules whose data actually\n"
+ " exist in the provided input data files. Takes effect only\n"
+ " with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
+ " mandatory nodes from modules which data are not present in the\n"
+ " provided input data files.\n\n");
+
+ printf(" -t TYPE, --type=TYPE\n"
+ " Specify data tree type in the input data file(s):\n"
+ " data - Complete datastore with status data (default type).\n"
+ " config - Configuration datastore (without status data).\n"
+ " get - Data returned by the NETCONF <get> operation.\n"
+ " getconfig - Data returned by the NETCONF <get-config> operation.\n"
+ " edit - Config content of the NETCONF <edit-config> operation.\n"
+ " rpc - Invocation of a YANG RPC/action, defined as input.\n"
+ " nc-rpc - Similar to 'rpc' but expect and check also the NETCONF\n"
+ " envelopes <rpc> or <action>.\n"
+ " reply - Reply to a YANG RPC/action, defined as output. Note that\n"
+ " the reply data are expected inside a container representing\n"
+ " the original RPC/action invocation.\n"
+ " nc-reply - Similar to 'reply' but expect and check also the NETCONF\n"
+ " envelope <rpc-reply> with output data nodes as direct\n"
+ " descendants. The original RPC/action invocation is expected\n"
+ " in a separate parameter '-R' and is parsed as 'nc-rpc'.\n"
+ " notif - Notification instance of a YANG notification.\n"
+ " nc-notif - Similar to 'notif' but expect and check also the NETCONF\n"
+ " envelope <notification> with element <eventTime> and its\n"
+ " sibling as the actual notification.\n\n");
+
+ printf(" -d MODE, --default=MODE\n"
+ " Print data with default values, according to the MODE\n"
+ " (to print attributes, ietf-netconf-with-defaults model\n"
+ " must be loaded):\n"
+ " all - Add missing default nodes.\n"
+ " all-tagged - Add missing default nodes and mark all the default\n"
+ " nodes with the attribute.\n"
+ " trim - Remove all nodes with a default value.\n"
+ " implicit-tagged - Add missing nodes and mark them with the attribute.\n\n");
+
+ printf(" -l, --list Print info about the loaded schemas.\n"
+ " (i - imported module, I - implemented module)\n"
+ " In case the '-f' option with data encoding is specified,\n"
+ " the list is printed as \"ietf-yang-library\" data.\n\n");
+
+ printf(" -L LINE_LENGTH, --tree-line-length=LINE_LENGTH\n"
+ " The limit of the maximum line length on which the 'tree'\n"
+ " format will try to be printed.\n\n");
+
+ printf(" -o OUTFILE, --output=OUTFILE\n"
+ " Write the output to OUTFILE instead of stdout.\n\n");
+
+ printf(" -O FILE, --operational=FILE\n"
+ " Provide optional data to extend validation of the '(nc-)rpc',\n"
+ " '(nc-)reply' or '(nc-)notif' TYPEs. The FILE is supposed to contain\n"
+ " the operational datastore referenced from the operation.\n"
+ " In case of a nested operation, its parent existence is also\n"
+ " checked in these operational data.\n\n");
+
+ printf(" -R FILE, --reply-rpc=FILE\n"
+ " Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
+ " is supposed to contain the source 'nc-rpc' operation of the reply.\n\n");
+
+ printf(" -m, --merge Merge input data files into a single tree and validate at\n"
+ " once. The option has effect only for 'data' and 'config' TYPEs.\n\n");
+
+ printf(" -y, --yang-library\n"
+ " Load and implement internal \"ietf-yang-library\" YANG module.\n"
+ " Note that this module includes definitions of mandatory state\n"
+ " data that can result in unexpected data validation errors.\n\n");
+
+ printf(" -Y FILE, --yang-library-file=FILE\n"
+ " Parse FILE with \"ietf-yang-library\" data and use them to\n"
+ " create an exact YANG schema context. If specified, the '-F'\n"
+ " parameter (enabled features) is ignored.\n\n");
+
+#ifndef NDEBUG
+ printf(" -G GROUPS, --debug=GROUPS\n"
+ " Enable printing of specific debugging message group\n"
+ " (nothing will be printed unless verbosity is set to debug):\n"
+ " <group>[,<group>]* (dict, xpath, dep-sets)\n\n");
+#endif
+}
+
+static void
+libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+ char *levstr;
+
+ switch (level) {
+ case LY_LLERR:
+ levstr = "err :";
+ break;
+ case LY_LLWRN:
+ levstr = "warn:";
+ break;
+ case LY_LLVRB:
+ levstr = "verb:";
+ break;
+ default:
+ levstr = "dbg :";
+ break;
+ }
+ if (path) {
+ fprintf(stderr, "libyang %s %s (%s)\n", levstr, msg, path);
+ } else {
+ fprintf(stderr, "libyang %s %s\n", levstr, msg);
+ }
+}
+
+static struct schema_features *
+get_features_not_applied(const struct ly_set *fset)
+{
+ for (uint32_t u = 0; u < fset->count; ++u) {
+ struct schema_features *sf = fset->objs[u];
+
+ if (!sf->applied) {
+ return sf;
+ }
+ }
+
+ return NULL;
+}
+
+static LY_ERR
+ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *data = NULL;
+
+ ctx = ext->module->ctx;
+ if (user_data) {
+ lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+searchpath_strcat(char **searchpaths, const char *path)
+{
+ uint64_t len;
+ char *new;
+
+ if (!(*searchpaths)) {
+ *searchpaths = strdup(path);
+ return LY_SUCCESS;
+ }
+
+ len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
+ new = realloc(*searchpaths, sizeof(char) * len + 1);
+ if (!new) {
+ return LY_EMEM;
+ }
+ strcat(new, PATH_SEPARATOR);
+ strcat(new, path);
+ *searchpaths = new;
+
+ return LY_SUCCESS;
+}
+
+static int
+fill_context_inputs(int argc, char *argv[], struct context *c)
+{
+ struct ly_in *in = NULL;
+ struct schema_features *sf;
+ struct lys_module *mod;
+ const char *all_features[] = {"*", NULL};
+ char *dir = NULL, *module = NULL;
+
+ /* Create libyang context. */
+ if (c->yang_lib_file) {
+ /* ignore features */
+ ly_set_erase(&c->schema_features, free_features);
+
+ if (ly_ctx_new_ylpath(c->searchpaths, c->yang_lib_file, LYD_UNKNOWN, c->ctx_options, &c->ctx)) {
+ YLMSG_E("Unable to modify libyang context with yang-library data.\n");
+ return -1;
+ }
+ } else {
+ /* set imp feature flag if all should be enabled */
+ c->ctx_options |= !c->schema_features.count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
+
+ if (ly_ctx_new(c->searchpaths, c->ctx_options, &c->ctx)) {
+ YLMSG_E("Unable to create libyang context\n");
+ return -1;
+ }
+ }
+
+ /* set callback providing run-time extension instance data */
+ if (c->schema_context_filename) {
+ ly_ctx_set_ext_data_clb(c->ctx, ext_data_clb, c->schema_context_filename);
+ }
+
+ /* process the operational and/or reply RPC content if any */
+ if (c->data_operational.path) {
+ if (get_input(c->data_operational.path, NULL, &c->data_operational.format, &c->data_operational.in)) {
+ return -1;
+ }
+ }
+ if (c->reply_rpc.path) {
+ if (get_input(c->reply_rpc.path, NULL, &c->reply_rpc.format, &c->reply_rpc.in)) {
+ return -1;
+ }
+ }
+
+ for (int i = 0; i < argc - optind; i++) {
+ LYS_INFORMAT format_schema = LYS_IN_UNKNOWN;
+ LYD_FORMAT format_data = LYD_UNKNOWN;
+
+ if (get_input(argv[optind + i], &format_schema, &format_data, &in)) {
+ goto error;
+ }
+
+ if (format_schema) {
+ LY_ERR ret;
+ uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
+ const char **features;
+
+ /* parse the input */
+ if (parse_schema_path(argv[optind + i], &dir, &module)) {
+ goto error;
+ }
+
+ if (c->yang_lib_file) {
+ /* just get the module, it should already be parsed */
+ mod = ly_ctx_get_module_implemented(c->ctx, module);
+ if (!mod) {
+ YLMSG_E("Schema module \"%s\" not implemented in yang-library data.\n", module);
+ goto error;
+ }
+ } else {
+ /* add temporarily also the path of the module itself */
+ if (ly_ctx_set_searchdir(c->ctx, dir) == LY_EEXIST) {
+ path_unset = 0;
+ }
+
+ /* get features list for this module */
+ if (!c->schema_features.count) {
+ features = all_features;
+ } else {
+ get_features(&c->schema_features, module, &features);
+ }
+
+ /* parse module */
+ ret = lys_parse(c->ctx, in, format_schema, features, &mod);
+ ly_ctx_unset_searchdir_last(c->ctx, path_unset);
+ if (ret) {
+ YLMSG_E("Parsing schema module \"%s\" failed.\n", argv[optind + i]);
+ goto error;
+ }
+ }
+
+ /* temporary cleanup */
+ free(dir);
+ dir = NULL;
+ free(module);
+ module = NULL;
+
+ if (c->schema_out_format || c->feature_param_format) {
+ /* module will be printed */
+ if (ly_set_add(&c->schema_modules, (void *)mod, 1, NULL)) {
+ YLMSG_E("Storing parsed schema module (%s) for print failed.\n", argv[optind + i]);
+ goto error;
+ }
+ }
+ } else if (format_data) {
+ if (!fill_cmdline_file(&c->data_inputs, in, argv[optind + i], format_data)) {
+ goto error;
+ }
+ in = NULL;
+ }
+
+ ly_in_free(in, 1);
+ in = NULL;
+ }
+
+ /* check that all specified features were applied, apply now if possible */
+ while ((sf = get_features_not_applied(&c->schema_features))) {
+ /* try to find implemented or the latest revision of this module */
+ mod = ly_ctx_get_module_implemented(c->ctx, sf->mod_name);
+ if (!mod) {
+ mod = ly_ctx_get_module_latest(c->ctx, sf->mod_name);
+ }
+ if (!mod) {
+ YLMSG_E("Specified features not applied, module \"%s\" not loaded.\n", sf->mod_name);
+ goto error;
+ }
+
+ /* we have the module, implement it if needed and enable the specific features */
+ if (lys_set_implemented(mod, (const char **)sf->features)) {
+ YLMSG_E("Implementing module \"%s\" failed.\n", mod->name);
+ goto error;
+ }
+ sf->applied = 1;
+ }
+
+ return 0;
+
+error:
+ ly_in_free(in, 1);
+ free(dir);
+ free(module);
+ return -1;
+}
+
+/**
+ * @brief Process command line options and store the settings into the context.
+ *
+ * return -1 in case of error;
+ * return 0 in case of success and ready to process
+ * return 1 in case of success, but expect to exit.
+ */
+static int
+fill_context(int argc, char *argv[], struct context *c)
+{
+ int ret;
+
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"verbose", no_argument, NULL, 'V'},
+ {"quiet", no_argument, NULL, 'Q'},
+ {"format", required_argument, NULL, 'f'},
+ {"path", required_argument, NULL, 'p'},
+ {"disable-searchdir", no_argument, NULL, 'D'},
+ {"features", required_argument, NULL, 'F'},
+ {"make-implemented", no_argument, NULL, 'i'},
+ {"schema-node", required_argument, NULL, 'P'},
+ {"single-node", no_argument, NULL, 'q'},
+ {"submodule", required_argument, NULL, 's'},
+ {"ext-data", required_argument, NULL, 'x'},
+ {"not-strict", no_argument, NULL, 'n'},
+ {"present", no_argument, NULL, 'e'},
+ {"type", required_argument, NULL, 't'},
+ {"default", required_argument, NULL, 'd'},
+ {"list", no_argument, NULL, 'l'},
+ {"tree-line-length", required_argument, NULL, 'L'},
+ {"output", required_argument, NULL, 'o'},
+ {"operational", required_argument, NULL, 'O'},
+ {"reply-rpc", required_argument, NULL, 'R'},
+ {"merge", no_argument, NULL, 'm'},
+ {"yang-library", no_argument, NULL, 'y'},
+ {"yang-library-file", required_argument, NULL, 'Y'},
+#ifndef NDEBUG
+ {"debug", required_argument, NULL, 'G'},
+#endif
+ {NULL, 0, NULL, 0}
+ };
+ uint8_t data_type_set = 0;
+
+ c->ctx_options = YL_DEFAULT_CTX_OPTIONS;
+ c->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
+ c->line_length = 0;
+
+ opterr = 0;
+#ifndef NDEBUG
+ while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:G:", options, &opt_index)) != -1)
+#else
+ while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:", options, &opt_index)) != -1)
+#endif
+ {
+ switch (opt) {
+ case 'h': /* --help */
+ help(0);
+ return 1;
+
+ case 'v': /* --version */
+ version();
+ return 1;
+
+ case 'V': { /* --verbose */
+ LY_LOG_LEVEL verbosity = ly_log_level(LY_LLERR);
+
+ if (verbosity < LY_LLDBG) {
+ ++verbosity;
+ }
+ ly_log_level(verbosity);
+ break;
+ } /* case 'V' */
+
+ case 'Q': { /* --quiet */
+ LY_LOG_LEVEL verbosity = ly_log_level(LY_LLERR);
+
+ if (verbosity == LY_LLERR) {
+ /* turn logging off */
+ ly_log_options(LY_LOSTORE_LAST);
+ } else if (verbosity > LY_LLERR) {
+ --verbosity;
+ }
+ ly_log_level(verbosity);
+ break;
+ } /* case 'Q' */
+
+ case 'f': /* --format */
+ if (!strcasecmp(optarg, "yang")) {
+ c->schema_out_format = LYS_OUT_YANG;
+ c->data_out_format = 0;
+ } else if (!strcasecmp(optarg, "yin")) {
+ c->schema_out_format = LYS_OUT_YIN;
+ c->data_out_format = 0;
+ } else if (!strcasecmp(optarg, "info")) {
+ c->schema_out_format = LYS_OUT_YANG_COMPILED;
+ c->data_out_format = 0;
+ } else if (!strcasecmp(optarg, "tree")) {
+ c->schema_out_format = LYS_OUT_TREE;
+ c->data_out_format = 0;
+ } else if (!strcasecmp(optarg, "xml")) {
+ c->schema_out_format = 0;
+ c->data_out_format = LYD_XML;
+ } else if (!strcasecmp(optarg, "json")) {
+ c->schema_out_format = 0;
+ c->data_out_format = LYD_JSON;
+ } else if (!strcasecmp(optarg, "lyb")) {
+ c->schema_out_format = 0;
+ c->data_out_format = LYD_LYB;
+ } else if (!strcasecmp(optarg, "feature-param")) {
+ c->feature_param_format = 1;
+ } else {
+ YLMSG_E("Unknown output format %s\n", optarg);
+ help(1);
+ return -1;
+ }
+ break;
+
+ case 'p': { /* --path */
+ struct stat st;
+
+ if (stat(optarg, &st) == -1) {
+ YLMSG_E("Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
+ return -1;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ YLMSG_E("Provided search path is not a directory.\n");
+ return -1;
+ }
+
+ if (searchpath_strcat(&c->searchpaths, optarg)) {
+ YLMSG_E("Storing searchpath failed.\n");
+ return -1;
+ }
+
+ break;
+ } /* case 'p' */
+
+ case 'D': /* --disable-search */
+ if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
+ YLMSG_W("The -D option specified too many times.\n");
+ }
+ if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIR_CWD) {
+ c->ctx_options &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
+ c->ctx_options |= LY_CTX_DISABLE_SEARCHDIRS;
+ } else {
+ c->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ }
+ break;
+
+ case 'F': /* --features */
+ if (parse_features(optarg, &c->schema_features)) {
+ return -1;
+ }
+ break;
+
+ case 'i': /* --make-implemented */
+ if (c->ctx_options & LY_CTX_REF_IMPLEMENTED) {
+ c->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
+ c->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
+ } else {
+ c->ctx_options |= LY_CTX_REF_IMPLEMENTED;
+ }
+ break;
+
+ case 'P': /* --schema-node */
+ c->schema_node_path = optarg;
+ break;
+
+ case 'q': /* --single-node */
+ c->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
+ break;
+
+ case 's': /* --submodule */
+ c->submodule = optarg;
+ break;
+
+ case 'x': /* --ext-data */
+ c->schema_context_filename = strdup(optarg);
+ break;
+
+ case 'n': /* --not-strict */
+ c->data_parse_options &= ~LYD_PARSE_STRICT;
+ break;
+
+ case 'e': /* --present */
+ c->data_validate_options |= LYD_VALIDATE_PRESENT;
+ break;
+
+ case 't': /* --type */
+ if (data_type_set) {
+ YLMSG_E("The data type (-t) cannot be set multiple times.\n");
+ return -1;
+ }
+
+ if (!strcasecmp(optarg, "config")) {
+ c->data_parse_options |= LYD_PARSE_NO_STATE;
+ c->data_validate_options |= LYD_VALIDATE_NO_STATE;
+ } else if (!strcasecmp(optarg, "get")) {
+ c->data_parse_options |= LYD_PARSE_ONLY;
+ } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config")) {
+ c->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+ } else if (!strcasecmp(optarg, "edit")) {
+ c->data_parse_options |= LYD_PARSE_ONLY;
+ } else if (!strcasecmp(optarg, "rpc")) {
+ c->data_type = LYD_TYPE_RPC_YANG;
+ } else if (!strcasecmp(optarg, "nc-rpc")) {
+ c->data_type = LYD_TYPE_RPC_NETCONF;
+ } else if (!strcasecmp(optarg, "reply")) {
+ c->data_type = LYD_TYPE_REPLY_YANG;
+ } else if (!strcasecmp(optarg, "nc-reply")) {
+ c->data_type = LYD_TYPE_REPLY_NETCONF;
+ } else if (!strcasecmp(optarg, "notif")) {
+ c->data_type = LYD_TYPE_NOTIF_YANG;
+ } else if (!strcasecmp(optarg, "nc-notif")) {
+ c->data_type = LYD_TYPE_NOTIF_NETCONF;
+ } else if (!strcasecmp(optarg, "data")) {
+ /* default option */
+ } else {
+ YLMSG_E("Unknown data tree type %s\n", optarg);
+ help(1);
+ return -1;
+ }
+
+ data_type_set = 1;
+ break;
+
+ case 'd': /* --default */
+ if (!strcasecmp(optarg, "all")) {
+ c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
+ } else if (!strcasecmp(optarg, "all-tagged")) {
+ c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
+ } else if (!strcasecmp(optarg, "trim")) {
+ c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
+ } else if (!strcasecmp(optarg, "implicit-tagged")) {
+ c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
+ } else {
+ YLMSG_E("Unknown default mode %s\n", optarg);
+ help(1);
+ return -1;
+ }
+ break;
+
+ case 'l': /* --list */
+ c->list = 1;
+ break;
+
+ case 'L': /* --tree-line-length */
+ c->line_length = atoi(optarg);
+ break;
+
+ case 'o': /* --output */
+ if (c->out) {
+ YLMSG_E("Only a single output can be specified.\n");
+ return -1;
+ } else {
+ if (ly_out_new_filepath(optarg, &c->out)) {
+ YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
+ return -1;
+ }
+ }
+ break;
+
+ case 'O': /* --operational */
+ if (c->data_operational.path) {
+ YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
+ return -1;
+ }
+ c->data_operational.path = optarg;
+ break;
+
+ case 'R': /* --reply-rpc */
+ if (c->reply_rpc.path) {
+ YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.\n");
+ return -1;
+ }
+ c->reply_rpc.path = optarg;
+ break;
+
+ case 'm': /* --merge */
+ c->data_merge = 1;
+ break;
+
+ case 'y': /* --yang-library */
+ c->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ break;
+
+ case 'Y': /* --yang-library-file */
+ c->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ c->yang_lib_file = optarg;
+ break;
+
+#ifndef NDEBUG
+ case 'G': { /* --debug */
+ uint32_t dbg_groups = 0;
+ const char *ptr = optarg;
+
+ while (ptr[0]) {
+ if (!strncasecmp(ptr, "dict", sizeof "dict" - 1)) {
+ dbg_groups |= LY_LDGDICT;
+ ptr += sizeof "dict" - 1;
+ } else if (!strncasecmp(ptr, "xpath", sizeof "xpath" - 1)) {
+ dbg_groups |= LY_LDGXPATH;
+ ptr += sizeof "xpath" - 1;
+ } else if (!strncasecmp(ptr, "dep-sets", sizeof "dep-sets" - 1)) {
+ dbg_groups |= LY_LDGDEPSETS;
+ ptr += sizeof "dep-sets" - 1;
+ }
+
+ if (ptr[0]) {
+ if (ptr[0] != ',') {
+ YLMSG_E("Unknown debug group string \"%s\"\n", optarg);
+ return -1;
+ }
+ ++ptr;
+ }
+ }
+ ly_log_dbg_groups(dbg_groups);
+ break;
+ } /* case 'G' */
+#endif
+ default:
+ YLMSG_E("Invalid option or missing argument: -%c\n", optopt);
+ return -1;
+ } /* switch */
+ }
+
+ /* additional checks for the options combinations */
+ if (!c->list && (optind >= argc)) {
+ help(1);
+ YLMSG_E("Missing <schema> to process.\n");
+ return 1;
+ }
+
+ if (c->data_merge) {
+ if (c->data_type || (c->data_parse_options & LYD_PARSE_ONLY)) {
+ /* switch off the option, incompatible input data type */
+ c->data_merge = 0;
+ } else {
+ /* postpone validation after the merge of all the input data */
+ c->data_parse_options |= LYD_PARSE_ONLY;
+ }
+ }
+
+ if (c->data_operational.path && !c->data_type) {
+ YLMSG_E("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.\n");
+ c->data_operational.path = NULL;
+ }
+
+ if (c->reply_rpc.path && (c->data_type != LYD_TYPE_REPLY_NETCONF)) {
+ YLMSG_E("Source RPC is needed only for NETCONF Reply input data type.\n");
+ c->data_operational.path = NULL;
+ } else if (!c->reply_rpc.path && (c->data_type == LYD_TYPE_REPLY_NETCONF)) {
+ YLMSG_E("Missing source RPC (-R) for NETCONF Reply input data type.\n");
+ return -1;
+ }
+
+ if ((c->schema_out_format != LYS_OUT_TREE) && c->line_length) {
+ YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
+ }
+
+ /* default output stream */
+ if (!c->out) {
+ if (ly_out_new_file(stdout, &c->out)) {
+ YLMSG_E("Unable to set stdout as output.\n");
+ return -1;
+ }
+ }
+
+ if (c->schema_out_format == LYS_OUT_TREE) {
+ /* print tree from lysc_nodes */
+ c->ctx_options |= LY_CTX_SET_PRIV_PARSED;
+ }
+
+ /* process input files provided as standalone command line arguments,
+ * schema modules are parsed and inserted into the context,
+ * data files are just checked and prepared into internal structures for further processing */
+ ret = fill_context_inputs(argc, argv, c);
+ if (ret) {
+ return ret;
+ }
+
+ /* the second batch of checks */
+ if (c->schema_print_options && !c->schema_out_format) {
+ YLMSG_W("Schema printer options specified, but the schema output format is missing.\n");
+ }
+ if (c->schema_parse_options && !c->schema_modules.count) {
+ YLMSG_W("Schema parser options specified, but no schema input file provided.\n");
+ }
+ if (c->data_print_options && !c->data_out_format) {
+ YLMSG_W("data printer options specified, but the data output format is missing.\n");
+ }
+ if (((c->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || c->data_type) && !c->data_inputs.count) {
+ YLMSG_W("Data parser options specified, but no data input file provided.\n");
+ }
+
+ if (c->schema_node_path) {
+ c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 0);
+ if (!c->schema_node) {
+ c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 1);
+
+ if (!c->schema_node) {
+ YLMSG_E("Invalid schema path.\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+main_ni(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS, r;
+ struct context c = {0};
+ char *features_output = NULL;
+ struct ly_set set = {0};
+ uint32_t u;
+
+ /* set callback for printing libyang messages */
+ ly_set_log_clb(libyang_verbclb, 1);
+
+ r = fill_context(argc, argv, &c);
+ if (r < 0) {
+ ret = EXIT_FAILURE;
+ }
+ if (r) {
+ goto cleanup;
+ }
+
+ /* do the required job - parse, validate, print */
+
+ if (c.list) {
+ /* print the list of schemas */
+ print_list(c.out, c.ctx, c.data_out_format);
+ } else {
+ if (c.feature_param_format) {
+ for (u = 0; u < c.schema_modules.count; u++) {
+ if (collect_features(c.schema_modules.objs[u], &set)) {
+ YLMSG_E("Unable to read features from a module.\n");
+ goto cleanup;
+ }
+ if (generate_features_output(c.schema_modules.objs[u], &set, &features_output)) {
+ YLMSG_E("Unable to generate feature command output.\n");
+ goto cleanup;
+ }
+ ly_set_erase(&set, NULL);
+ }
+ ly_print(c.out, "%s\n", features_output);
+ } else if (c.schema_out_format) {
+ if (c.schema_node) {
+ ret = lys_print_node(c.out, c.schema_node, c.schema_out_format, 0, c.schema_print_options);
+ if (ret) {
+ YLMSG_E("Unable to print schema node %s.\n", c.schema_node_path);
+ goto cleanup;
+ }
+ } else if (c.submodule) {
+ const struct lysp_submodule *submod = ly_ctx_get_submodule_latest(c.ctx, c.submodule);
+
+ if (!submod) {
+ YLMSG_E("Unable to find submodule %s.\n", c.submodule);
+ goto cleanup;
+ }
+
+ ret = lys_print_submodule(c.out, submod, c.schema_out_format, c.line_length, c.schema_print_options);
+ if (ret) {
+ YLMSG_E("Unable to print submodule %s.\n", submod->name);
+ goto cleanup;
+ }
+ } else {
+ for (u = 0; u < c.schema_modules.count; ++u) {
+ ret = lys_print_module(c.out, (struct lys_module *)c.schema_modules.objs[u], c.schema_out_format,
+ c.line_length, c.schema_print_options);
+ /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
+ if ((c.schema_out_format == LYS_OUT_TREE) && (u + 1 < c.schema_modules.count)) {
+ ly_print(c.out, "\n");
+ }
+ if (ret) {
+ YLMSG_E("Unable to print module %s.\n", ((struct lys_module *)c.schema_modules.objs[u])->name);
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ /* do the data validation despite the schema was printed */
+ if (c.data_inputs.size) {
+ ret = process_data(c.ctx, c.data_type, c.data_merge, c.data_out_format, c.out, c.data_parse_options,
+ c.data_validate_options, c.data_print_options, &c.data_operational, &c.reply_rpc, &c.data_inputs, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ /* cleanup */
+ erase_context(&c);
+ free(features_output);
+ ly_set_erase(&set, NULL);
+
+ return ret;
+}
diff --git a/tools/lint/main_ni_only.c b/tools/lint/main_ni_only.c
new file mode 100644
index 0000000..d55f2c2
--- /dev/null
+++ b/tools/lint/main_ni_only.c
@@ -0,0 +1,22 @@
+/**
+ * @file main_ni_only.c
+ * @brief non-interactive implementation of main() for those platforms without the linenoise library
+ *
+ * Copyright (c) 2015-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+int main_ni(int argc, char *argv[]);
+
+int done; /* for cmd.c */
+
+int
+main(int argc, char *argv[])
+{
+ return main_ni(argc, argv);
+}
diff --git a/tools/lint/tests/expect/common.exp b/tools/lint/tests/expect/common.exp
new file mode 100644
index 0000000..0381e6c
--- /dev/null
+++ b/tools/lint/tests/expect/common.exp
@@ -0,0 +1,68 @@
+# detect the path to the yanglint binary
+if { [info exists ::env(YANGLINT)] } {
+ set yanglint "$env(YANGLINT)"
+} else {
+ set yanglint "../../../../build/yanglint"
+}
+
+# set the timeout to 1 second
+set timeout 1
+
+# expect a single line of anchored regex output
+proc expect_output {output} {
+ expect {
+ -re "^${output}$" {}
+ timeout {exit 1}
+ }
+}
+
+# send a command and either expect some anchored regex output if specified or just an empty line
+proc expect_command {command has_output output} {
+ send -- "${command}\r"
+
+ if ($has_output==1) {
+ expect {
+ -re "^${command}\r\n${output}$" {}
+ timeout {exit 1}
+ }
+ } else {
+ # input echoes
+ expect {
+ -re "^${command}\r\n$" {}
+ timeout {exit 1}
+ }
+ expect {
+ -re "^> $" {}
+ timeout {exit 1}
+ }
+ }
+}
+
+# send a completion request and check if the anchored regex output matches
+proc expect_completion {input output} {
+ send -- "${input}\t"
+
+ expect {
+ # expecting echoing input, output and 10 terminal control characters
+ -re "^${input}\r> ${output}.*\r.*$" {}
+ timeout {exit 1}
+ }
+}
+
+# send a completion request and check if the anchored regex hint options match
+proc expect_hint {input prev_input hints} {
+ set output {}
+ foreach i $hints {
+ # each element might have some number of spaces and CRLF around it
+ append output "${i} *(?:\\r\\n)?"
+ }
+
+ send -- "${input}\t"
+
+ expect {
+ # expecting the hints, previous input from which the hints were generated
+ # and some number of terminal control characters
+ -re "^\r\n${output}\r> ${prev_input}.*\r.*$" {}
+ timeout {exit 1}
+ }
+}
diff --git a/tools/lint/tests/expect/completion.exp b/tools/lint/tests/expect/completion.exp
new file mode 100755
index 0000000..ed4f6bd
--- /dev/null
+++ b/tools/lint/tests/expect/completion.exp
@@ -0,0 +1,60 @@
+#!/usr/bin/expect -f
+
+if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
+ source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
+ set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
+} else {
+ source "common.exp"
+ set yang_dir "../../examples"
+}
+
+spawn $yanglint
+
+# skip no dir and/or no history warnings
+expect_output "(YANGLINT.*)*> "
+
+expect_command "clear -ii" 0 ""
+
+expect_command "add ${yang_dir}/ietf-ip.yang" 0 ""
+
+expect_completion "print -f info -P " "print -f info -P /ietf-"
+
+set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"}
+
+expect_hint "" "print -f info -P /ietf-" $hints
+
+expect_completion "i" "print -f info -P /ietf-interfaces:interfaces"
+
+expect_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface"
+
+set hints {"/ietf-interfaces:interfaces/interface"
+"/ietf-interfaces:interfaces/interface/name" "/ietf-interfaces:interfaces/interface/description"
+"/ietf-interfaces:interfaces/interface/type" "/ietf-interfaces:interfaces/interface/enabled"
+"/ietf-interfaces:interfaces/interface/link-up-down-trap-enable"
+"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6"}
+
+expect_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints
+
+expect_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv"
+
+expect_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4"
+
+set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled"
+"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu"
+"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor"
+}
+
+expect_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints
+
+expect_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled "
+
+send -- "\r"
+
+expect {
+ -re ".*\r\n> " {}
+ timeout {exit 1}
+}
+
+send -- "exit\r"
+
+expect eof
diff --git a/tools/lint/tests/expect/feature.exp b/tools/lint/tests/expect/feature.exp
new file mode 100755
index 0000000..37680b0
--- /dev/null
+++ b/tools/lint/tests/expect/feature.exp
@@ -0,0 +1,20 @@
+#!/usr/bin/expect -f
+
+if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
+ source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
+ set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
+} else {
+ source "common.exp"
+ set yang_dir "../../examples"
+}
+
+spawn $yanglint
+
+# skip no dir and/or no history warnings
+expect_output "(YANGLINT.*)*> "
+
+expect_command "feature -a" 1 "yang:\r\n\t\\(none\\)\r\n\r\nietf-yang-schema-mount:\r\n\t\\(none\\)\r\n\r\n> "
+
+send -- "exit\r"
+
+expect eof
diff --git a/tools/lint/tests/expect/list.exp b/tools/lint/tests/expect/list.exp
new file mode 100755
index 0000000..ec3cdba
--- /dev/null
+++ b/tools/lint/tests/expect/list.exp
@@ -0,0 +1,20 @@
+#!/usr/bin/expect -f
+
+if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
+ source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
+ set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
+} else {
+ source "common.exp"
+ set yang_dir "../../examples"
+}
+
+spawn $yanglint
+
+# skip no dir and/or no history warnings
+expect_output "(YANGLINT.*)*> "
+
+expect_command "list" 1 "List of the loaded models:\r\n *i ietf-yang-metadata@2016-08-05\r\n *I yang@2022-06-16\r\n *i ietf-inet-types@2013-07-15\r\n *i ietf-yang-types@2013-07-15\r\n *I ietf-yang-schema-mount@2019-01-14\r\n *i ietf-yang-structure-ext@2020-06-17\r\n> "
+
+send -- "exit\r"
+
+expect eof
diff --git a/tools/lint/tests/shunit2/feature.sh b/tools/lint/tests/shunit2/feature.sh
new file mode 100755
index 0000000..fb2ee88
--- /dev/null
+++ b/tools/lint/tests/shunit2/feature.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+testFeature() {
+ models=( "iana-if-type@2014-05-08.yang" "ietf-netconf@2011-06-01.yang" "ietf-netconf-with-defaults@2011-06-01.yang"
+ "sm.yang" "ietf-interfaces@2014-05-08.yang" "ietf-netconf-acm@2018-02-14.yang" "ietf-origin@2018-02-14.yang"
+ "ietf-ip@2014-06-16.yang" "ietf-restconf@2017-01-26.yang" )
+ features=( " -F iana-if-type:"
+ " -F ietf-netconf:writable-running,candidate,confirmed-commit,rollback-on-error,validate,startup,url,xpath"
+ " -F ietf-netconf-with-defaults:" " -F sm:" " -F ietf-interfaces:arbitrary-names,pre-provisioning,if-mib"
+ " -F ietf-netconf-acm:" " -F ietf-origin:" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf"
+ " -F ietf-restconf:" )
+
+ for i in ${!models[@]}; do
+ output=`${YANGLINT} -f feature-param ${YANG_MODULES_DIR}/${models[$i]}`
+ assertEquals "Unexpected features of module ${models[$i]}." "${features[$i]}" "${output}"
+ done
+}
+
+. shunit2
diff --git a/tools/lint/tests/shunit2/list.sh b/tools/lint/tests/shunit2/list.sh
new file mode 100755
index 0000000..d64503a
--- /dev/null
+++ b/tools/lint/tests/shunit2/list.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+LIST_BASE="List of the loaded models:
+ i ietf-yang-metadata@2016-08-05
+ I yang@2022-06-16
+ i ietf-inet-types@2013-07-15
+ i ietf-yang-types@2013-07-15
+ I ietf-yang-schema-mount@2019-01-14
+ i ietf-yang-structure-ext@2020-06-17"
+
+testListEmptyContext() {
+ output=`${YANGLINT} -l`
+ assertEquals "Unexpected list of modules in empty context." "${LIST_BASE}" "${output}"
+}
+
+testListAllImplemented() {
+ LIST_BASE_ALLIMPLEMENTED="List of the loaded models:
+ I ietf-yang-metadata@2016-08-05
+ I yang@2022-06-16
+ I ietf-inet-types@2013-07-15
+ I ietf-yang-types@2013-07-15
+ I ietf-yang-schema-mount@2019-01-14
+ I ietf-yang-structure-ext@2020-06-17"
+ output=`${YANGLINT} -lii`
+ assertEquals "Unexpected list of modules in empty context with -ii." "${LIST_BASE_ALLIMPLEMENTED}" "${output}"
+}
+
+. shunit2
diff --git a/tools/lint/yanglint.1 b/tools/lint/yanglint.1
new file mode 100644
index 0000000..4b7060d
--- /dev/null
+++ b/tools/lint/yanglint.1
@@ -0,0 +1,136 @@
+.\" Manpage for yanglint.
+.\" Process this file with
+.\" groff -man -Tascii yanglint.1
+.\"
+
+.TH YANGLINT 1 "2016-10-27" "libyang"
+.SH NAME
+yanglint \- YANG lint tool
+.
+.SH SYNOPSIS
+.B yanglint
+.br
+.B yanglint
+[\fIOPTIONS\fP]
+[\-f { \fByang\fP | \fByin\fP | \fBtree\fP } ]
+.I FILE ...
+.br
+.B yanglint
+[\fIOPTIONS\fP]
+[\-f { \fBxml\fP | \fBjson\fP } ]
+\fISCHEMA\fP...
+\fIFILE\fP...
+.
+.SH DESCRIPTION
+\fByanglint\fP is a command-line tool for validating and converting YANG
+schemas and the YANG modeled data. For a simple use, it validates the provided
+file and if the output format specified, it converts input data into the output
+format. If started with no argument, \fByanglint\fP opens interactive
+environment where the user is allowed to work with schemas and data in a more
+complex way.
+.
+.SH OPTIONS
+.TP
+.BR "\-h\fR,\fP \-\^\-help"
+Outputs usage help and exits.
+.TP
+.BR "\-v\fR,\fP \-\^\-version"
+Outputs the version number and exits.
+.TP
+.BR "\-V\fR,\fP \-\^\-verbose"
+Increases the verbosity level. If not specified, only errors are printed, with
+each appearance it adds: warnings, verbose messages, debug messages (if compiled
+with debug information).
+.TP
+.BR "\-p \fIPATH\fP\fR,\fP \-\^\-path=\fIPATH\fP"
+Specifies search path for getting imported modules or included submodules. The option
+can be used multiple times. The current working directory and path of the module
+being added is used implicitly.
+.TP
+.BR "\-s\fR,\fP \-\^\-strict"
+Changes handling of unknown data nodes - instead of silently ignoring unknown data,
+error is printed and data parsing fails. This option applies only on data parsing.
+.TP
+.BR "\-f \fIFORMAT\fP\fR,\fP \-\^\-format=\fIFORMAT\fP"
+Converts the content of the input \fIFILE\fPs into the specified \fIFORMAT\fP. If no
+\fIOUTFILE\fP is specified, the data are printed on the standard output. Only the
+compatible formats for the input \fIFILE\fPs are allowed, see the section \fBFORMATS\fP.
+.TP
+.BR "\-o \fIOUTFILE\fP\fR,\fP \-\^\-output=\fIOUTFILE\fP"
+Writes the output data into the specified \fIOUTFILE\fP. The option can be used
+only in combination with \fB--format\fR option. In case of converting schema, only
+a single input schema \fIFILE\fP is allowed. In case of data input \fIFILE\fPs,
+input is merged and printed into a single \fIOUTFILE\fP.
+.TP
+.BR "\-F \fIFEATURES\fP\fR,\fP \-\^\-features=\fIFEATURES\fP"
+Specifies the list of enabled features in the format
+\fIMODULE\fP:[\fIFEATURE\fP,...]. In case of processing multiple modules, the
+option can be used repeatedly. To disable all the features, use an empty list
+specified for the particular module.
+.TP
+.BR "\-d \fIMODE\fP\fR,\fP \-\^\-default=\fIMODE\fP"
+Print data with default values, according to the \fIMODE\fP (to print attributes,
+the ietf-netconf-with-defaults model must be loaded). The \fIMODE\fP is one of the following:
+ \[bu] \fBall\fP - add missing default nodes
+ \[bu] \fBall-tagged\fP - add missing default nodes and mark all the default nodes with the attribute
+ \[bu] \fBtrim\fP - remove all nodes with a default value
+ \[bu] \fBimplicit-tagged\fP - add missing nodes and mark them with the attribute
+.TP
+.BR "\-t \fITYPE\fP\fR,\fP \-\^\-type=\fITYPE\fP"
+Specify data tree type in the input data \fIFILE\fPs. The \fITYPE\fP is one of the following:
+ \[bu] \fBauto\fP - Resolve data type (one of the following) automatically (as pyang does). Applicable only on XML input data.
+ \[bu] \fBdata\fP - Complete datastore with status data (default type).
+ \[bu] \fBconfig\fP - Configuration datastore (without status data).
+ \[bu] \fBget\fP - Result of the NETCONF <get> operation.
+ \[bu] \fBgetconfig\fP - Result of the NETCONF <get-config> operation.
+ \[bu] \fBedit\fP - Content of the NETCONF <edit-config> operation.
+ \[bu] \fBrpc\fP - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.
+ \[bu] \fBrpcreply\fP - Reply to the RPC. This is just a virtual \fITYPE\fP, for parsing replies, '\fBauto\fP' must be used since the data \fIFILE\fPs are expected in pairs.
+.br
+ The first input data \fIFILE\fP is expected as '\fBrpc\fP' \fITYPE\fP, the second \fIFILE\fP is expected as reply to the previous RPC.
+ \[bu] \fBnotif\fP - Notification instance (content of the <notification> element without <eventTime>.
+.TP
+.BR "\-O \fIFILE\fP\fR,\fP \-\^\-operational=\fIFILE\fP]
+Optional parameter for '\fBrpc\fP' and '\fBnotif\fP' \fITYPE\fPs, the \fIFILE\fP contains running configuration datastore and
+state data referenced from the RPC/Notification. The same data apply to all input data \fIFILE\fPs. Note that the file
+is validated as '\fBdata\fP' \fITYPE\fP. Special value '\fB!\fP' can be used as \fIFILE\fP argument to ignore the external references.
+.TP
+.BR "\-y \fIYANGLIB_PATH\fP"
+Specify path to a yang-library data file (XML or JSON) describing the initial context.
+If provided, yanglint loads the modules according to the content of the yang-library data tree.
+Otherwise, an empty content with only the internal libyang modules is used. This does
+not limit user to load another modules explicitly specified as command line parameters.
+.
+.SH FORMATS
+There are two types of formats to use.
+.TP
+.I Schemas
+In case of schemas, the content can be converted into the '\fByang\fP', '\fByin\fP'
+and '\fBtree\fP' formats. As input, only YANG and YIN files are
+accepted. Note, that the corresponding file extension is required.
+.TP
+.I Data\ \ \
+In case of YANG modeled data, the content can be converted between '\fBxml\fP'
+and '\fBjson\fP' formats. Remember that the corresponding file extension of the
+input file is required.
+.
+
+.SH EXAMPLES
+.IP \[bu] 2
+Open interactive environment:
+ yanglint
+.IP \[bu]
+Convert YANG model into YIN and print it to the stdout:
+ yanglint --format=yin ./ietf-system.yang
+.IP \[bu]
+Convert ietf-system configuration data from XML to JSON:
+ yanglint --format=json --type=config --output=data.json ./ietf-system.yang ./data.xml
+
+.SH SEE ALSO
+https://github.com/CESNET/libyang (libyang homepage and Git repository)
+.
+.SH AUTHORS
+Radek Krejci <rkrejci@cesnet.cz>, Michal Vasko <mvasko@cesnet.cz>
+.
+.SH COPYRIGHT
+Copyright \(co 2015-2017 CESNET, a.l.e.
diff --git a/tools/re/CMakeLists.txt b/tools/re/CMakeLists.txt
new file mode 100644
index 0000000..7b6de22
--- /dev/null
+++ b/tools/re/CMakeLists.txt
@@ -0,0 +1,20 @@
+# yangre
+
+set(resrc
+ main.c)
+
+set(format_sources
+ ${format_sources}
+ ${CMAKE_CURRENT_SOURCE_DIR}/*.c
+ PARENT_SCOPE)
+
+add_executable(yangre ${resrc} ${compatsrc})
+target_link_libraries(yangre yang)
+install(TARGETS yangre DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/tools/re/yangre.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+target_include_directories(yangre BEFORE PRIVATE ${PROJECT_BINARY_DIR})
+
+if(WIN32)
+ target_include_directories(yangre PRIVATE ${GETOPT_INCLUDE_DIR})
+ target_link_libraries(yangre ${GETOPT_LIBRARY})
+endif()
diff --git a/tools/re/main.c b/tools/re/main.c
new file mode 100644
index 0000000..2292b2a
--- /dev/null
+++ b/tools/re/main.c
@@ -0,0 +1,309 @@
+/**
+ * @file main.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's YANG Regular Expression tool
+ *
+ * Copyright (c) 2017 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "libyang.h"
+
+#include "compat.h"
+#include "tools/config.h"
+
+void
+help(void)
+{
+ fprintf(stdout, "YANG Regular Expressions processor.\n");
+ fprintf(stdout, "Usage:\n");
+ fprintf(stdout, " yangre [-hv]\n");
+ fprintf(stdout, " yangre [-V] -p <regexp1> [-i] [-p <regexp2> [-i] ...] <string>\n");
+ fprintf(stdout, " yangre [-V] -f <file>\n");
+ fprintf(stdout, "Returns 0 if string matches the pattern(s), 1 if not and -1 on error.\n\n");
+ fprintf(stdout, "Options:\n"
+ " -h, --help Show this help message and exit.\n"
+ " -v, --version Show version number and exit.\n"
+ " -V, --verbose Print the processing information.\n"
+ " -i, --invert-match Invert-match modifier for the closest preceding\n"
+ " pattern.\n"
+ " -p, --pattern=\"REGEXP\" Regular expression including the quoting,\n"
+ " which is applied the same way as in a YANG module.\n"
+ " -f, --file=\"FILE\" List of patterns and the <string> (separated by an\n"
+ " empty line) are taken from <file>. Invert-match is\n"
+ " indicated by the single space character at the \n"
+ " beginning of the pattern line. YANG quotation around\n"
+ " patterns is still expected, but that avoids issues with\n"
+ " reading quotation by shell. Avoid newline at the end\n"
+ " of the string line to represent empty <string>.");
+ fprintf(stdout, "Examples:\n"
+ " pattern \"[0-9a-fA-F]*\"; -> yangre -p '\"[0-9a-fA-F]*\"' '1F'\n"
+ " pattern '[a-zA-Z0-9\\-_.]*'; -> yangre -p \"'[a-zA-Z0-9\\-_.]*'\" 'a-b'\n"
+ " pattern [xX][mM][lL].*; -> yangre -p '[xX][mM][lL].*' 'xml-encoding'\n\n");
+ fprintf(stdout, "Note that to pass YANG quoting through your shell, you are supposed to use\n"
+ "the other quotation around. For not-quoted patterns, use single quotes.\n\n");
+}
+
+void
+version(void)
+{
+ fprintf(stdout, "yangre %s\n", PROJECT_VERSION);
+}
+
+void
+pattern_error(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+ (void) path; /* unused */
+
+ if (level == LY_LLERR) {
+ fprintf(stderr, "yangre error: %s\n", msg);
+ }
+}
+
+static const char *module_start = "module yangre {"
+ "yang-version 1.1;"
+ "namespace urn:cesnet:libyang:yangre;"
+ "prefix re;"
+ "leaf pattern {"
+ " type string {";
+static const char *module_invertmatch = " { modifier invert-match; }";
+static const char *module_match = ";";
+static const char *module_end = "}}}";
+
+static int
+add_pattern(char ***patterns, int **inverts, int *counter, char *pattern)
+{
+ void *reallocated1, *reallocated2;
+
+ (*counter)++;
+ reallocated1 = realloc(*patterns, *counter * sizeof **patterns);
+ reallocated2 = realloc(*inverts, *counter * sizeof **inverts);
+ if (!reallocated1 || !reallocated2) {
+ fprintf(stderr, "yangre error: memory allocation error.\n");
+ free(reallocated1);
+ free(reallocated2);
+ return EXIT_FAILURE;
+ }
+ (*patterns) = reallocated1;
+ (*patterns)[*counter - 1] = strdup(pattern);
+ (*inverts) = reallocated2;
+ (*inverts)[*counter - 1] = 0;
+
+ return EXIT_SUCCESS;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LY_ERR match;
+ int i, opt_index = 0, ret = -1, verbose = 0, blankline = 0;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"file", required_argument, NULL, 'f'},
+ {"invert-match", no_argument, NULL, 'i'},
+ {"pattern", required_argument, NULL, 'p'},
+ {"version", no_argument, NULL, 'v'},
+ {"verbose", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+ char **patterns = NULL, *str = NULL, *modstr = NULL, *s;
+ int *invert_match = NULL;
+ int patterns_count = 0;
+ struct ly_ctx *ctx = NULL;
+ struct lys_module *mod;
+ FILE *infile = NULL;
+ size_t len = 0;
+ ssize_t l;
+
+ opterr = 0;
+ while ((i = getopt_long(argc, argv, "hf:ivVp:", options, &opt_index)) != -1) {
+ switch (i) {
+ case 'h':
+ help();
+ ret = -2; /* continue to allow printing version and help at once */
+ break;
+ case 'f':
+ if (infile) {
+ help();
+ fprintf(stderr, "yangre error: multiple input files are not supported.\n");
+ goto cleanup;
+ } else if (patterns_count) {
+ help();
+ fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
+ goto cleanup;
+ }
+ infile = fopen(optarg, "rb");
+ if (!infile) {
+ fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
+ goto cleanup;
+ }
+
+ while ((l = getline(&str, &len, infile)) != -1) {
+ if (!blankline && (str[0] == '\n')) {
+ /* blank line */
+ blankline = 1;
+ continue;
+ }
+ if ((str[0] != '\n') && (str[l - 1] == '\n')) {
+ /* remove ending newline */
+ str[l - 1] = '\0';
+ }
+ if (blankline) {
+ /* done - str is now the string to check */
+ blankline = 0;
+ break;
+ /* else read the patterns */
+ } else if (add_pattern(&patterns, &invert_match, &patterns_count,
+ (str[0] == ' ') ? &str[1] : str)) {
+ goto cleanup;
+ }
+ if (str[0] == ' ') {
+ /* set invert-match */
+ invert_match[patterns_count - 1] = 1;
+ }
+ }
+ if (blankline) {
+ /* corner case, no input after blankline meaning the pattern to check is empty */
+ if (str != NULL) {
+ free(str);
+ }
+ str = malloc(sizeof(char));
+ str[0] = '\0';
+ }
+ break;
+ case 'i':
+ if (!patterns_count || invert_match[patterns_count - 1]) {
+ help();
+ fprintf(stderr, "yangre error: invert-match option must follow some pattern.\n");
+ goto cleanup;
+ }
+ invert_match[patterns_count - 1] = 1;
+ break;
+ case 'p':
+ if (infile) {
+ help();
+ fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
+ goto cleanup;
+ }
+ if (add_pattern(&patterns, &invert_match, &patterns_count, optarg)) {
+ goto cleanup;
+ }
+ break;
+ case 'v':
+ version();
+ ret = -2; /* continue to allow printing version and help at once */
+ break;
+ case 'V':
+ verbose = 1;
+ break;
+ default:
+ help();
+ if (optopt) {
+ fprintf(stderr, "yangre error: invalid option: -%c\n", optopt);
+ } else {
+ fprintf(stderr, "yangre error: invalid option: %s\n", argv[optind - 1]);
+ }
+ goto cleanup;
+ }
+ }
+
+ if (ret == -2) {
+ goto cleanup;
+ }
+
+ if (!str) {
+ /* check options compatibility */
+ if (optind >= argc) {
+ help();
+ fprintf(stderr, "yangre error: missing <string> parameter to process.\n");
+ goto cleanup;
+ } else if (!patterns_count) {
+ help();
+ fprintf(stderr, "yangre error: missing pattern parameter to use.\n");
+ goto cleanup;
+ }
+ str = argv[optind];
+ }
+
+ for (modstr = (char *)module_start, i = 0; i < patterns_count; i++) {
+ if (asprintf(&s, "%s pattern %s%s", modstr, patterns[i], invert_match[i] ? module_invertmatch : module_match) == -1) {
+ fprintf(stderr, "yangre error: memory allocation failed.\n");
+ goto cleanup;
+ }
+ if (modstr != module_start) {
+ free(modstr);
+ }
+ modstr = s;
+ }
+ if (asprintf(&s, "%s%s", modstr, module_end) == -1) {
+ fprintf(stderr, "yangre error: memory allocation failed.\n");
+ goto cleanup;
+ }
+ if (modstr != module_start) {
+ free(modstr);
+ }
+ modstr = s;
+
+ if (ly_ctx_new(NULL, 0, &ctx)) {
+ goto cleanup;
+ }
+
+ ly_set_log_clb(pattern_error, 0);
+ if (lys_parse_mem(ctx, modstr, LYS_IN_YANG, &mod) || !mod->compiled || !mod->compiled->data) {
+ goto cleanup;
+ }
+
+ /* check the value */
+ match = lyd_value_validate(ctx, mod->compiled->data, str, strlen(str), NULL, NULL, NULL);
+
+ if (verbose) {
+ for (i = 0; i < patterns_count; i++) {
+ fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i]);
+ fprintf(stdout, "matching %d: %s\n", i + 1, invert_match[i] ? "inverted" : "regular");
+ }
+ fprintf(stdout, "string : %s\n", str);
+ if (match == LY_SUCCESS) {
+ fprintf(stdout, "result : matching\n");
+ } else if (match == LY_EVALID) {
+ fprintf(stdout, "result : not matching\n");
+ } else {
+ fprintf(stdout, "result : error (%s)\n", ly_errmsg(ctx));
+ }
+ }
+ if (match == LY_SUCCESS) {
+ ret = 0;
+ } else if (match == LY_EVALID) {
+ ret = 1;
+ } else {
+ ret = -1;
+ }
+
+cleanup:
+ ly_ctx_destroy(ctx);
+ for (i = 0; i < patterns_count; i++) {
+ free(patterns[i]);
+ }
+ free(patterns);
+ free(invert_match);
+ free(modstr);
+ if (infile) {
+ fclose(infile);
+ free(str);
+ }
+
+ return ret;
+}
diff --git a/tools/re/yangre.1 b/tools/re/yangre.1
new file mode 100644
index 0000000..e7b572b
--- /dev/null
+++ b/tools/re/yangre.1
@@ -0,0 +1,118 @@
+.\" Manpage for yanglint.
+.\" Process this file with
+.\" groff -man -Tascii yangre.1
+.\"
+
+.TH YANGRE 1 "2018-11-09" "libyang"
+.SH NAME
+yangre \- YANG regular expression processor
+.
+.SH SYNOPSIS
+.B yangre
+[\-V] \-p \fIREGEXP\fP [\-i] [\-p \fIREGEXP\fP [\-i]...] \fISTRING\fP
+.br
+.B yangre
+[\-V] \-f \fIFILE\fP
+.
+.SH DESCRIPTION
+\fByangre\fP is a command-line tool to test and evaluate regular expressions
+for use in YANG schemas. Supported regular expressions are defined by the
+W3C's XML-Schema standard.
+
+\fByangre\fP can be used either with regular expressions and a target string
+on the command line or with input from a file. The latter is particularly
+useful to avoid dealing with proper shell escaping of regular expression
+patterns, which can be somewhat tricky.
+.
+.SH GENERAL OPTIONS
+.TP
+.BR "\-h\fR,\fP \-\^\-help"
+.br
+Outputs usage help and exits.
+.TP
+.BR "\-v\fR,\fP \-\^\-version"
+.br
+Outputs the version number and exits.
+.TP
+.BR "\-V\fR,\fP \-\^\-verbose"
+Increases the verbosity level. If not specified, only errors are printed, with
+each appearance it adds: warnings, verbose messages, debug messages (if compiled
+with debug information).
+.SH COMMAND LINE INPUT
+.TP
+.BR "\-p \fIREGEXP\fP\fR,\fP \-\^\-pattern=\fIREGEXP\fP"
+.br
+One or more regular expression patterns to be tested against the input
+string. Supplied expressions are tested in the order they appear on the
+command line. Testing is aborted when an expression does not match (or
+does match, if the \fB-i\fP option is used.)
+.TP
+.BR "\-i\fR,\fP \-\^\-invert-match"
+.br
+Reverse match condition for the previous pattern. If the pattern matches,
+an error is printed and evaluation is aborted.
+.TP
+.BR "\fISTRING\fP"
+.br
+Target text input to match the regular expression(s) against. The same
+text is used for all regular expressions. Note that only the first
+argument is used by \fByangre\fP, if it contains spaces or other shell
+metacharacters they must be properly escaped. Additional arguments are
+silently ignored.
+.SH FILE INPUT
+.TP
+.BR "\-f \fIFILE\fP\fR,\fP \-\^\-file=\fIFILE\fP"
+Read both patterns and target text from the specified input file.
+
+\fIFILE\fP must consist of one or more YANG regular expressions, each on
+their own line, followed by a blank line and one line of target text. No
+preprocessing is done on file input, there are no comment lines and
+whitespace is not stripped. A single space character at the beginning of
+a pattern line inverts the match condition for the pattern on that line.
+Patterns must still be properly quoted as mandated by the YANG standard.
+.SH RETURN VALUES
+.TP
+0
+.I Successful match
+.br
+The target text matched for all patterns.
+.TP
+1
+.I Pattern mismatch
+.br
+One or more patterns did not match the target text. An error message is
+printed to stderr describing which pattern was the first not to match.
+.TP
+255
+.I Other error
+.br
+One or more patterns could not be processed or some other error occurred that
+precluded processing.
+.SH EXAMPLES
+.IP \[bu] 2
+Test a single pattern:
+ yangre -p 'te.*xt' text_text
+.IP \[bu]
+Test multiple patterns:
+ yangre -p '.*pat1' -p 'pat2.*' -p 'notpat' -i pat2testpat1
+.IP \[bu]
+Input from a file:
+ cat > /tmp/patterns <<EOF
+ .*pat1
+ pat2.*
+ notpat
+
+ pat2testpat1
+ EOF
+ yangre -f /tmp/patterns
+
+.SH SEE ALSO
+https://github.com/CESNET/libyang (libyang homepage and Git repository)
+.
+.SH AUTHORS
+Radek Krejci <rkrejci@cesnet.cz>, Michal Vasko <mvasko@cesnet.cz>
+.br
+This man page was written by David Lamparter <equinox@diac24.net>
+.
+.SH COPYRIGHT
+Copyright \(co 2015-2018 CESNET, a.l.e.
diff --git a/uncrustify.cfg b/uncrustify.cfg
new file mode 100644
index 0000000..ec52e71
--- /dev/null
+++ b/uncrustify.cfg
@@ -0,0 +1,3566 @@
+# Uncrustify-0.76.0_f
+
+#
+# General options
+#
+
+# Added specific file extensions.
+file_ext C .c.in
+file_ext C-Header .h.in
+
+# The type of line endings.
+#
+# Default: auto
+newlines = lf # lf/crlf/cr/auto
+
+# The original size of tabs in the input.
+#
+# Default: 8
+input_tab_size = 8 # unsigned number
+
+# The size of tabs in the output (only used if align_with_tabs=true).
+#
+# Default: 8
+output_tab_size = 8 # unsigned number
+
+# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^).
+#
+# Default: 92
+string_escape_char = 92 # unsigned number
+
+# Alternate string escape char (usually only used for Pawn).
+# Only works right before the quote char.
+string_escape_char2 = 0 # unsigned number
+
+# Replace tab characters found in string literals with the escape sequence \t
+# instead.
+string_replace_tab_chars = true # true/false
+
+# Allow interpreting '>=' and '>>=' as part of a template in code like
+# 'void f(list<list<B>>=val);'. If true, 'assert(x<0 && y>=3)' will be broken.
+# Improvements to template detection may make this option obsolete.
+tok_split_gte = false # true/false
+
+# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multiline macros)
+disable_processing_nl_cont = false # true/false
+
+# Specify the marker used in comments to disable processing of part of the
+# file.
+# The comment should be used alone in one line.
+#
+# Default: *INDENT-OFF*
+disable_processing_cmt = " *INDENT-OFF*" # string
+
+# Specify the marker used in comments to (re)enable processing in a file.
+# The comment should be used alone in one line.
+#
+# Default: *INDENT-ON*
+enable_processing_cmt = " *INDENT-ON*" # string
+
+# Enable parsing of digraphs.
+enable_digraphs = false # true/false
+
+# Option to allow both disable_processing_cmt and enable_processing_cmt
+# strings, if specified, to be interpreted as ECMAScript regular expressions.
+# If true, a regex search will be performed within comments according to the
+# specified patterns in order to disable/enable processing.
+processing_cmt_as_regex = false # true/false
+
+# Add or remove the UTF-8 BOM (recommend 'remove').
+utf8_bom = ignore # ignore/add/remove/force
+
+# If the file contains bytes with values between 128 and 255, but is not
+# UTF-8, then output as UTF-8.
+utf8_byte = false # true/false
+
+# Force the output encoding to UTF-8.
+utf8_force = true # true/false
+
+# Add or remove space between 'do' and '{'.
+sp_do_brace_open = force # ignore/add/remove/force
+
+# Add or remove space between '}' and 'while'.
+sp_brace_close_while = force # ignore/add/remove/force
+
+# Add or remove space between 'while' and '('.
+sp_while_paren_open = force # ignore/add/remove/force
+
+#
+# Spacing options
+#
+
+# Add or remove space around non-assignment symbolic operators ('+', '/', '%',
+# '<<', and so forth).
+sp_arith = force # ignore/add/remove/force
+
+# Add or remove space around arithmetic operators '+' and '-'.
+#
+# Overrides sp_arith.
+sp_arith_additive = force # ignore/add/remove/force
+
+# Add or remove space around assignment operator '=', '+=', etc.
+sp_assign = force # ignore/add/remove/force
+
+# Add or remove space around '=' in C++11 lambda capture specifications.
+#
+# Overrides sp_assign.
+sp_cpp_lambda_assign = force # ignore/add/remove/force
+
+# Add or remove space after the capture specification of a C++11 lambda when
+# an argument list is present, as in '[] <here> (int x){ ... }'.
+sp_cpp_lambda_square_paren = ignore # ignore/add/remove/force
+
+# Add or remove space after the capture specification of a C++11 lambda with
+# no argument list is present, as in '[] <here> { ... }'.
+sp_cpp_lambda_square_brace = ignore # ignore/add/remove/force
+
+# Add or remove space after the opening parenthesis and before the closing
+# parenthesis of a argument list of a C++11 lambda, as in
+# '[]( <here> int x <here> ){ ... }'.
+sp_cpp_lambda_argument_list = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space after the argument list of a C++11 lambda, as in
+# '[](int x) <here> { ... }'.
+sp_cpp_lambda_paren_brace = ignore # ignore/add/remove/force
+
+# Add or remove space between a lambda body and its call operator of an
+# immediately invoked lambda, as in '[]( ... ){ ... } <here> ( ... )'.
+sp_cpp_lambda_fparen = ignore # ignore/add/remove/force
+
+# Add or remove space around assignment operator '=' in a prototype.
+#
+# If set to ignore, use sp_assign.
+sp_assign_default = ignore # ignore/add/remove/force
+
+# Add or remove space before assignment operator '=', '+=', etc.
+#
+# Overrides sp_assign.
+sp_before_assign = force # ignore/add/remove/force
+
+# Add or remove space after assignment operator '=', '+=', etc.
+#
+# Overrides sp_assign.
+sp_after_assign = force # ignore/add/remove/force
+
+# Add or remove space in 'enum {'.
+#
+# Default: add
+sp_enum_brace = force # ignore/add/remove/force/not_defined
+
+# Add or remove space in 'NS_ENUM ('.
+sp_enum_paren = force # ignore/add/remove/force
+
+# Add or remove space around assignment '=' in enum.
+sp_enum_assign = force # ignore/add/remove/force
+
+# Add or remove space before assignment '=' in enum.
+#
+# Overrides sp_enum_assign.
+sp_enum_before_assign = force # ignore/add/remove/force
+
+# Add or remove space after assignment '=' in enum.
+#
+# Overrides sp_enum_assign.
+sp_enum_after_assign = force # ignore/add/remove/force
+
+# Add or remove space around assignment ':' in enum.
+sp_enum_colon = ignore # ignore/add/remove/force
+
+# Add or remove space around preprocessor '##' concatenation operator.
+#
+# Default: add
+sp_pp_concat = ignore # ignore/add/remove/force
+
+# Add or remove space after preprocessor '#' stringify operator.
+# Also affects the '#@' charizing operator.
+sp_pp_stringify = ignore # ignore/add/remove/force
+
+# Add or remove space before preprocessor '#' stringify operator
+# as in '#define x(y) L#y'.
+sp_before_pp_stringify = ignore # ignore/add/remove/force
+
+# Add or remove space around boolean operators '&&' and '||'.
+sp_bool = force # ignore/add/remove/force
+
+# Add or remove space around compare operator '<', '>', '==', etc.
+sp_compare = force # ignore/add/remove/force
+
+# Add or remove space inside '(' and ')'.
+sp_inside_paren = remove # ignore/add/remove/force
+
+# Add or remove space between nested parentheses, i.e. '((' vs. ') )'.
+sp_paren_paren = remove # ignore/add/remove/force
+
+# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('.
+sp_cparen_oparen = remove # ignore/add/remove/force
+
+# Whether to balance spaces inside nested parentheses.
+sp_balance_nested_parens = false # true/false
+
+# Add or remove space between ')' and '{'.
+sp_paren_brace = force # ignore/add/remove/force
+
+# Add or remove space between nested braces, i.e. '{{' vs '{ {'.
+sp_brace_brace = ignore # ignore/add/remove/force
+
+# Add or remove space before pointer star '*'.
+sp_before_ptr_star = force # ignore/add/remove/force
+
+# Add or remove space before pointer star '*' that isn't followed by a
+# variable name. If set to ignore, sp_before_ptr_star is used instead.
+sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force
+
+# Add or remove space between pointer stars '*'.
+sp_between_ptr_star = remove # ignore/add/remove/force
+
+# Add or remove space after pointer star '*', if followed by a word.
+#
+# Overrides sp_type_func.
+sp_after_ptr_star = remove # ignore/add/remove/force
+
+# Add or remove space after pointer caret '^', if followed by a word.
+#sp_after_ptr_block_caret = ignore # ignore/add/remove/force
+
+# Add or remove space after pointer star '*', if followed by a qualifier.
+sp_after_ptr_star_qualifier = force # ignore/add/remove/force
+
+# Add or remove space after a pointer star '*', if followed by a function
+# prototype or function definition.
+#
+# Overrides sp_after_ptr_star and sp_type_func.
+sp_after_ptr_star_func = ignore # ignore/add/remove/force
+
+# Add or remove space after a pointer star '*' in the trailing return of a
+# function prototype or function definition.
+sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between the pointer star '*' and the name of the variable
+# in a function pointer definition.
+sp_ptr_star_func_var = remove # ignore/add/remove/force/not_defined
+
+# Add or remove space between the pointer star '*' and the name of the type
+# in a function pointer type definition.
+sp_ptr_star_func_type = remove # ignore/add/remove/force/not_defined
+
+# Add or remove space after a pointer star '*', if followed by an open
+# parenthesis, as in 'void* (*)().
+sp_ptr_star_paren = remove # ignore/add/remove/force
+
+# Add or remove space before a pointer star '*', if followed by a function
+# prototype or function definition.
+sp_before_ptr_star_func = force # ignore/add/remove/force
+
+# Add or remove space before a pointer star '*' in the trailing return of a
+# function prototype or function definition.
+sp_before_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space before a reference sign '&'.
+sp_before_byref = ignore # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&' that isn't followed by a
+# variable name. If set to ignore, sp_before_byref is used instead.
+sp_before_unnamed_byref = ignore # ignore/add/remove/force
+
+# Add or remove space after reference sign '&', if followed by a word.
+#
+# Overrides sp_type_func.
+sp_after_byref = ignore # ignore/add/remove/force
+
+# Add or remove space after a reference sign '&', if followed by a function
+# prototype or function definition.
+#
+# Overrides sp_after_byref and sp_type_func.
+sp_after_byref_func = ignore # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&', if followed by a function
+# prototype or function definition.
+sp_before_byref_func = ignore # ignore/add/remove/force
+
+# Add or remove space after a reference sign '&', if followed by an open
+# parenthesis, as in 'char& (*)()'.
+sp_byref_paren = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between type and word.
+#
+# Default: force
+sp_after_type = ignore # ignore/add/remove/force
+
+# Add or remove space between 'decltype(...)' and word.
+sp_after_decltype = ignore # ignore/add/remove/force
+
+# (D) Add or remove space before the parenthesis in the D constructs
+# 'template Foo(' and 'class Foo('.
+sp_before_template_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'template' and '<'.
+# If set to ignore, sp_before_angle is used.
+sp_template_angle = ignore # ignore/add/remove/force
+
+# Add or remove space before '<'.
+sp_before_angle = ignore # ignore/add/remove/force
+
+# Add or remove space inside '<' and '>'.
+sp_inside_angle = ignore # ignore/add/remove/force
+
+# Add or remove space inside '<>'.
+sp_inside_angle_empty = ignore # ignore/add/remove/force
+
+# Add or remove space between '>' and ':'.
+sp_angle_colon = ignore # ignore/add/remove/force
+
+# Add or remove space after '>'.
+sp_after_angle = ignore # ignore/add/remove/force
+
+# Add or remove space between '>' and '(' as found in 'new List<byte>(foo);'.
+sp_angle_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between '>' and '()' as found in 'new List<byte>();'.
+sp_angle_paren_empty = ignore # ignore/add/remove/force
+
+# Add or remove space between '>' and a word as in 'List<byte> m;' or
+# 'template <typename T> static ...'.
+sp_angle_word = ignore # ignore/add/remove/force
+
+# Add or remove space between '>' and '>' in '>>' (template stuff).
+#
+# Default: add
+sp_angle_shift = ignore # ignore/add/remove/force
+
+# (C++11) Permit removal of the space between '>>' in 'foo<bar<int> >'. Note
+# that sp_angle_shift cannot remove the space without this option.
+sp_permit_cpp11_shift = false # true/false
+
+# Add or remove space before '(' of control statements ('if', 'for', 'switch',
+# 'while', etc.).
+sp_before_sparen = force # ignore/add/remove/force
+
+# Add or remove space inside '(' and ')' of control statements.
+sp_inside_sparen = remove # ignore/add/remove/force
+
+# Add or remove space after '(' of control statements.
+#
+# Overrides sp_inside_sparen.
+sp_inside_sparen_open = ignore # ignore/add/remove/force
+
+# Add or remove space before ')' of control statements.
+#
+# Overrides sp_inside_sparen.
+sp_inside_sparen_close = ignore # ignore/add/remove/force
+
+# Add or remove space inside '(' and ')' of 'for' statements.
+sp_inside_for = remove # ignore/add/remove/force/not_defined
+
+# Add or remove space after '(' of 'for' statements.
+#
+# Overrides sp_inside_for.
+sp_inside_for_open = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space before ')' of 'for' statements.
+#
+# Overrides sp_inside_for.
+sp_inside_for_close = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between '((' or '))' of control statements.
+sp_sparen_paren = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space after ')' of control statements.
+sp_after_sparen = force # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of of control statements.
+sp_sparen_brace = force # ignore/add/remove/force
+
+# (D) Add or remove space between 'invariant' and '('.
+sp_invariant_paren = ignore # ignore/add/remove/force
+
+# (D) Add or remove space after the ')' in 'invariant (C) c'.
+sp_after_invariant_paren = ignore # ignore/add/remove/force
+
+# Add or remove space before empty statement ';' on 'if', 'for' and 'while'.
+sp_special_semi = ignore # ignore/add/remove/force
+
+# Add or remove space before ';'.
+#
+# Default: remove
+sp_before_semi = remove # ignore/add/remove/force
+
+# Add or remove space before ';' in non-empty 'for' statements.
+sp_before_semi_for = remove # ignore/add/remove/force
+
+# Add or remove space before a semicolon of an empty part of a for statement.
+sp_before_semi_for_empty = force # ignore/add/remove/force
+
+# Add or remove space between the semicolons of an empty middle part of a for
+# statement, as in 'for ( ; <here> ; )'.
+sp_between_semi_for_empty = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space after ';', except when followed by a comment.
+#
+# Default: add
+sp_after_semi = force # ignore/add/remove/force
+
+# Add or remove space after ';' in non-empty 'for' statements.
+#
+# Default: force
+sp_after_semi_for = force # ignore/add/remove/force
+
+# Add or remove space after the final semicolon of an empty part of a for
+# statement, as in 'for ( ; ; <here> )'.
+sp_after_semi_for_empty = force # ignore/add/remove/force
+
+# Add or remove space before '[' (except '[]').
+sp_before_square = ignore # ignore/add/remove/force
+
+# Add or remove space before '[' for a variable definition.
+#
+# Default: remove
+sp_before_vardef_square = ignore # ignore/add/remove/force
+
+# Add or remove space before '[' for asm block.
+sp_before_square_asm_block = ignore # ignore/add/remove/force
+
+# Add or remove space before '[]'.
+sp_before_squares = ignore # ignore/add/remove/force
+
+# Add or remove space before C++17 structured bindings.
+sp_cpp_before_struct_binding = ignore # ignore/add/remove/force
+
+# Add or remove space inside a non-empty '[' and ']'.
+sp_inside_square = ignore # ignore/add/remove/force
+
+# Add or remove space inside '[]'.
+sp_inside_square_empty = remove # ignore/add/remove/force/not_defined
+
+# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and
+# ']'. If set to ignore, sp_inside_square is used.
+sp_inside_square_oc_array = ignore # ignore/add/remove/force
+
+# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'.
+sp_after_comma = add # ignore/add/remove/force
+
+# Add or remove space before ','.
+#
+# Default: remove
+sp_before_comma = remove # ignore/add/remove/force
+
+# (C#) Add or remove space between ',' and ']' in multidimensional array type
+# like 'int[,,]'.
+sp_after_mdatype_commas = ignore # ignore/add/remove/force
+
+# (C#) Add or remove space between '[' and ',' in multidimensional array type
+# like 'int[,,]'.
+sp_before_mdatype_commas = ignore # ignore/add/remove/force
+
+# (C#) Add or remove space between ',' in multidimensional array type
+# like 'int[,,]'.
+sp_between_mdatype_commas = ignore # ignore/add/remove/force
+
+# Add or remove space between an open parenthesis and comma,
+# i.e. '(,' vs. '( ,'.
+#
+# Default: force
+sp_paren_comma = force # ignore/add/remove/force
+
+# Add or remove space between a type and ':'.
+sp_type_colon = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space after the variadic '...' when preceded by a
+# non-punctuator.
+# The value REMOVE will be overriden with FORCE
+sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space before the variadic '...' when preceded by a
+# non-punctuator.
+sp_before_ellipsis = force # ignore/add/remove/force
+
+# Add or remove space between a type and '...'.
+sp_type_ellipsis = force # ignore/add/remove/force
+
+# Add or remove space between a '*' and '...'.
+sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between ')' and '...'.
+sp_paren_ellipsis = force # ignore/add/remove/force
+
+# Add or remove space between '&&' and '...'.
+sp_byref_ellipsis = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between ')' and a qualifier such as 'const'.
+sp_paren_qualifier = force # ignore/add/remove/force
+
+# Add or remove space between ')' and 'noexcept'.
+sp_paren_noexcept = ignore # ignore/add/remove/force
+
+# Add or remove space after class ':'.
+sp_after_class_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before class ':'.
+sp_before_class_colon = ignore # ignore/add/remove/force
+
+# Add or remove space after class constructor ':'.
+sp_after_constr_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before class constructor ':'.
+sp_before_constr_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before case ':'.
+#
+# Default: remove
+sp_before_case_colon = remove # ignore/add/remove/force
+
+# Add or remove space between 'operator' and operator sign.
+sp_after_operator = ignore # ignore/add/remove/force
+
+# Add or remove space between the operator symbol and the open parenthesis, as
+# in 'operator ++('.
+sp_after_operator_sym = ignore # ignore/add/remove/force
+
+# Overrides sp_after_operator_sym when the operator has no arguments, as in
+# 'operator *()'.
+sp_after_operator_sym_empty = ignore # ignore/add/remove/force
+
+# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or
+# '(int)a' vs. '(int) a'.
+sp_after_cast = ignore # ignore/add/remove/force
+
+# Add or remove spaces inside cast parentheses.
+sp_inside_paren_cast = remove # ignore/add/remove/force
+
+# Add or remove space between the type and open parenthesis in a C++ cast,
+# i.e. 'int(exp)' vs. 'int (exp)'.
+sp_cpp_cast_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'sizeof' and '('.
+sp_sizeof_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'sizeof' and '...'.
+sp_sizeof_ellipsis = remove # ignore/add/remove/force
+
+# Add or remove space between 'sizeof...' and '('.
+sp_sizeof_ellipsis_paren = remove # ignore/add/remove/force
+
+# Add or remove space between '...' and a parameter pack.
+sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between a parameter pack and '...'.
+sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space between 'decltype' and '('.
+sp_decltype_paren = ignore # ignore/add/remove/force
+
+# (Pawn) Add or remove space after the tag keyword.
+#sp_after_tag = ignore # ignore/add/remove/force
+
+# Add or remove space inside enum '{' and '}'.
+sp_inside_braces_enum = force # ignore/add/remove/force
+
+# Add or remove space inside struct/union '{' and '}'.
+sp_inside_braces_struct = force # ignore/add/remove/force
+
+# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}'
+#sp_inside_braces_oc_dict = ignore # ignore/add/remove/force
+
+# Add or remove space after open brace in an unnamed temporary
+# direct-list-initialization.
+sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force
+
+# Add or remove space before close brace in an unnamed temporary
+# direct-list-initialization.
+sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force
+
+# Add or remove space inside an unnamed temporary direct-list-initialization.
+sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force
+
+# Add or remove space inside '{' and '}'.
+sp_inside_braces = remove # ignore/add/remove/force
+
+# Add or remove space inside '{}'.
+sp_inside_braces_empty = remove # ignore/add/remove/force
+
+# Add or remove space around trailing return operator '->'.
+sp_trailing_return = ignore # ignore/add/remove/force
+
+# Add or remove space between return type and function name. A minimum of 1
+# is forced except for pointer return types.
+sp_type_func = remove # ignore/add/remove/force
+
+# Add or remove space between type and open brace of an unnamed temporary
+# direct-list-initialization.
+sp_type_brace_init_lst = ignore # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function declaration.
+sp_func_proto_paren = remove # ignore/add/remove/force
+
+# Add or remove space between function name and '()' on function declaration
+# without parameters.
+sp_func_proto_paren_empty = remove # ignore/add/remove/force
+
+# Add or remove space between function name and '(' with a typedef specifier.
+sp_func_type_paren = remove # ignore/add/remove/force
+
+# Add or remove space between alias name and '(' of a non-pointer function type typedef.
+sp_func_def_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between function name and '()' on function definition
+# without parameters.
+sp_func_def_paren_empty = remove # ignore/add/remove/force
+
+# Add or remove space inside empty function '()'.
+# Overrides sp_after_angle unless use_sp_after_angle_always is set to true.
+sp_inside_fparens = remove # ignore/add/remove/force
+
+# Add or remove space inside function '(' and ')'.
+sp_inside_fparen = remove # ignore/add/remove/force
+
+# Add or remove space inside the first parentheses in a function type, as in
+# 'void (*x)(...)'.
+sp_inside_tparen = remove # ignore/add/remove/force
+
+# Add or remove space between the ')' and '(' in a function type, as in
+# 'void (*x)(...)'.
+sp_after_tparen_close = remove # ignore/add/remove/force
+
+# Add or remove space between ']' and '(' when part of a function call.
+sp_square_fparen = ignore # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of function.
+sp_fparen_brace = ignore # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of s function call in object
+# initialization.
+#
+# Overrides sp_fparen_brace.
+sp_fparen_brace_initializer = ignore # ignore/add/remove/force
+
+# (Java) Add or remove space between ')' and '{{' of double brace initializer.
+sp_fparen_dbrace = ignore # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function calls.
+sp_func_call_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between function name and '()' on function calls without
+# parameters. If set to ignore (the default), sp_func_call_paren is used.
+sp_func_call_paren_empty = ignore # ignore/add/remove/force
+
+# Add or remove space between the user function name and '(' on function
+# calls. You need to set a keyword to be a user function in the config file,
+# like:
+# set func_call_user tr _ i18n
+sp_func_call_user_paren = ignore # ignore/add/remove/force
+
+# Add or remove space inside user function '(' and ')'.
+sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force
+
+# Add or remove space between nested parentheses with user functions,
+# i.e. '((' vs. '( ('.
+sp_func_call_user_paren_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between a constructor/destructor and the open
+# parenthesis.
+sp_func_class_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between a constructor without parameters or destructor
+# and '()'.
+sp_func_class_paren_empty = ignore # ignore/add/remove/force
+
+# Add or remove space after 'return'.
+#
+# Default: force
+sp_return = force # ignore/add/remove/force/not_defined
+
+# Add or remove space between 'return' and '('.
+sp_return_paren = force # ignore/add/remove/force
+
+# Add or remove space between 'return' and '{'.
+sp_return_brace = force # ignore/add/remove/force
+
+# Add or remove space between '__attribute__' and '('.
+sp_attribute_paren = remove # ignore/add/remove/force
+
+# Add or remove space between 'defined' and '(' in '#if defined (FOO)'.
+sp_defined_paren = force # ignore/add/remove/force
+
+# Add or remove space between 'throw' and '(' in 'throw (something)'.
+sp_throw_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'throw' and anything other than '(' as in
+# '@throw [...];'.
+sp_after_throw = ignore # ignore/add/remove/force
+
+# Add or remove space between 'catch' and '(' in 'catch (something) { }'.
+# If set to ignore, sp_before_sparen is used.
+sp_catch_paren = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between '@catch' and '('
+# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used.
+sp_oc_catch_paren = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space before Objective-C protocol list
+# as in '@protocol Protocol<here><Protocol_A>' or '@interface MyClass : NSObject<here><MyProtocol>'.
+sp_before_oc_proto_list = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between class name and '('
+# in '@interface className(categoryName)<ProtocolName>:BaseClass'
+sp_oc_classname_paren = ignore # ignore/add/remove/force
+
+# (D) Add or remove space between 'version' and '('
+# in 'version (something) { }'. If set to ignore, sp_before_sparen is used.
+sp_version_paren = ignore # ignore/add/remove/force
+
+# (D) Add or remove space between 'scope' and '('
+# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used.
+sp_scope_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'super' and '(' in 'super (something)'.
+#
+# Default: remove
+sp_super_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'this' and '(' in 'this (something)'.
+#
+# Default: remove
+sp_this_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between a macro name and its definition.
+sp_macro = force # ignore/add/remove/force
+
+# Add or remove space between a macro function ')' and its definition.
+sp_macro_func = force # ignore/add/remove/force
+
+# Add or remove space between 'else' and '{' if on the same line.
+sp_else_brace = force # ignore/add/remove/force
+
+# Add or remove space between '}' and 'else' if on the same line.
+sp_brace_else = force # ignore/add/remove/force
+
+# Add or remove space between '}' and the name of a typedef on the same line.
+sp_brace_typedef = force # ignore/add/remove/force
+
+# Add or remove space before the '{' of a 'catch' statement, if the '{' and
+# 'catch' are on the same line, as in 'catch (decl) <here> {'.
+sp_catch_brace = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{'
+# and '@catch' are on the same line, as in '@catch (decl) <here> {'.
+# If set to ignore, sp_catch_brace is used.
+sp_oc_catch_brace = ignore # ignore/add/remove/force
+
+# Add or remove space between '}' and 'catch' if on the same line.
+sp_brace_catch = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between '}' and '@catch' if on the same line.
+# If set to ignore, sp_brace_catch is used.
+sp_oc_brace_catch = ignore # ignore/add/remove/force
+
+# Add or remove space between 'finally' and '{' if on the same line.
+sp_finally_brace = ignore # ignore/add/remove/force
+
+# Add or remove space between '}' and 'finally' if on the same line.
+sp_brace_finally = ignore # ignore/add/remove/force
+
+# Add or remove space between 'try' and '{' if on the same line.
+sp_try_brace = ignore # ignore/add/remove/force
+
+# Add or remove space between get/set and '{' if on the same line.
+sp_getset_brace = ignore # ignore/add/remove/force
+
+# Add or remove space between a variable and '{' for C++ uniform
+# initialization.
+sp_word_brace_init_lst = ignore # ignore/add/remove/force
+
+# Add or remove space between a variable and '{' for a namespace.
+#
+# Default: add
+sp_word_brace_ns = ignore # ignore/add/remove/force
+
+# Add or remove space before the '::' operator.
+sp_before_dc = ignore # ignore/add/remove/force
+
+# Add or remove space after the '::' operator.
+sp_after_dc = ignore # ignore/add/remove/force
+
+# (D) Add or remove around the D named array initializer ':' operator.
+sp_d_array_colon = remove # ignore/add/remove/force
+
+# Add or remove space after the '!' (not) unary operator.
+#
+# Default: remove
+sp_not = remove # ignore/add/remove/force
+
+# Add or remove space between two '!' (not) unary operators.
+# If set to ignore, sp_not will be used.
+sp_not_not = remove # ignore/add/remove/force/not_defined
+
+# Add or remove space after the '~' (invert) unary operator.
+#
+# Default: remove
+sp_inv = remove # ignore/add/remove/force
+
+# Add or remove space after the '&' (address-of) unary operator. This does not
+# affect the spacing after a '&' that is part of a type.
+#
+# Default: remove
+sp_addr = remove # ignore/add/remove/force
+
+# Add or remove space around the '.' or '->' operators.
+#
+# Default: remove
+sp_member = remove # ignore/add/remove/force
+
+# Add or remove space after the '*' (dereference) unary operator. This does
+# not affect the spacing after a '*' that is part of a type.
+#
+# Default: remove
+sp_deref = remove # ignore/add/remove/force
+
+# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'.
+#
+# Default: remove
+sp_sign = remove # ignore/add/remove/force
+
+# Add or remove space between '++' and '--' the word to which it is being
+# applied, as in '(--x)' or 'y++;'.
+#
+# Default: remove
+sp_incdec = remove # ignore/add/remove/force
+
+# Add or remove space before a backslash-newline at the end of a line.
+#
+# Default: add
+sp_before_nl_cont = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;'
+# or '+(int) bar;'.
+sp_after_oc_scope = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after the colon in message specs,
+# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'.
+sp_after_oc_colon = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space before the colon in message specs,
+# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'.
+sp_before_oc_colon = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after the colon in immutable dictionary expression
+# 'NSDictionary *test = @{@"foo" :@"bar"};'.
+sp_after_oc_dict_colon = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space before the colon in immutable dictionary expression
+# 'NSDictionary *test = @{@"foo" :@"bar"};'.
+sp_before_oc_dict_colon = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after the colon in message specs,
+# i.e. '[object setValue:1];' vs. '[object setValue: 1];'.
+sp_after_send_oc_colon = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space before the colon in message specs,
+# i.e. '[object setValue:1];' vs. '[object setValue :1];'.
+sp_before_send_oc_colon = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after the (type) in message specs,
+# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'.
+sp_after_oc_type = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after the first (type) in message specs,
+# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'.
+sp_after_oc_return_type = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between '@selector' and '(',
+# i.e. '@selector(msgName)' vs. '@selector (msgName)'.
+# Also applies to '@protocol()' constructs.
+sp_after_oc_at_sel = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between '@selector(x)' and the following word,
+# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'.
+sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space inside '@selector' parentheses,
+# i.e. '@selector(foo)' vs. '@selector( foo )'.
+# Also applies to '@protocol()' constructs.
+sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space before a block pointer caret,
+# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'.
+sp_before_oc_block_caret = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after a block pointer caret,
+# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'.
+sp_after_oc_block_caret = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between the receiver and selector in a message,
+# as in '[receiver selector ...]'.
+sp_after_oc_msg_receiver = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space after '@property'.
+sp_after_oc_property = ignore # ignore/add/remove/force
+
+# (OC) Add or remove space between '@synchronized' and the open parenthesis,
+# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'.
+sp_after_oc_synchronized = ignore # ignore/add/remove/force
+
+# Add or remove space around the ':' in 'b ? t : f'.
+sp_cond_colon = force # ignore/add/remove/force
+
+# Add or remove space before the ':' in 'b ? t : f'.
+#
+# Overrides sp_cond_colon.
+sp_cond_colon_before = ignore # ignore/add/remove/force
+
+# Add or remove space after the ':' in 'b ? t : f'.
+#
+# Overrides sp_cond_colon.
+sp_cond_colon_after = ignore # ignore/add/remove/force
+
+# Add or remove space around the '?' in 'b ? t : f'.
+sp_cond_question = force # ignore/add/remove/force
+
+# Add or remove space before the '?' in 'b ? t : f'.
+#
+# Overrides sp_cond_question.
+sp_cond_question_before = ignore # ignore/add/remove/force
+
+# Add or remove space after the '?' in 'b ? t : f'.
+#
+# Overrides sp_cond_question.
+sp_cond_question_after = ignore # ignore/add/remove/force
+
+# In the abbreviated ternary form '(a ?: b)', add or remove space between '?'
+# and ':'.
+#
+# Overrides all other sp_cond_* options.
+sp_cond_ternary_short = remove # ignore/add/remove/force
+
+# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make
+# sense here.
+sp_case_label = force # ignore/add/remove/force
+
+# (D) Add or remove space around the D '..' operator.
+sp_range = ignore # ignore/add/remove/force
+
+# Add or remove space after ':' in a Java/C++11 range-based 'for',
+# as in 'for (Type var : expr)'.
+sp_after_for_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before ':' in a Java/C++11 range-based 'for',
+# as in 'for (Type var : expr)'.
+sp_before_for_colon = ignore # ignore/add/remove/force
+
+# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'.
+sp_extern_paren = ignore # ignore/add/remove/force
+
+# Add or remove space after the opening of a C++ comment,
+# i.e. '// A' vs. '//A'.
+sp_cmt_cpp_start = force # ignore/add/remove/force
+
+# Add or remove space in a C++ region marker comment, as in '// <here> BEGIN'.
+# A region marker is defined as a comment which is not preceded by other text
+# (i.e. the comment is the first non-whitespace on the line), and which starts
+# with either 'BEGIN' or 'END'.
+#
+# Overrides sp_cmt_cpp_start.
+sp_cmt_cpp_region = ignore # ignore/add/remove/force/not_defined
+
+# If true, space is added with sp_cmt_cpp_start will be added after doxygen
+# sequences like '///', '///<', '//!' and '//!<'.
+sp_cmt_cpp_doxygen = true # true/false
+
+# If true, space is added with sp_cmt_cpp_start will be added after Qt
+# translator or meta-data comments like '//:', '//=', and '//~'.
+sp_cmt_cpp_qttr = true # true/false
+
+# Add or remove space between #else or #endif and a trailing comment.
+sp_endif_cmt = force # ignore/add/remove/force
+
+# Add or remove space after 'new', 'delete' and 'delete[]'.
+sp_after_new = ignore # ignore/add/remove/force
+
+# Add or remove space between 'new' and '(' in 'new()'.
+sp_between_new_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between ')' and type in 'new(foo) BAR'.
+sp_after_newop_paren = ignore # ignore/add/remove/force
+
+# Add or remove space inside parenthesis of the new operator
+# as in 'new(foo) BAR'.
+sp_inside_newop_paren = ignore # ignore/add/remove/force
+
+# Add or remove space after the open parenthesis of the new operator,
+# as in 'new(foo) BAR'.
+#
+# Overrides sp_inside_newop_paren.
+sp_inside_newop_paren_open = ignore # ignore/add/remove/force
+
+# Add or remove space before the close parenthesis of the new operator,
+# as in 'new(foo) BAR'.
+#
+# Overrides sp_inside_newop_paren.
+sp_inside_newop_paren_close = ignore # ignore/add/remove/force
+
+# Add or remove space before a trailing comment.
+sp_before_tr_cmt = ignore # ignore/add/remove/force/not_defined
+
+# Number of spaces before a trailing comment.
+sp_num_before_tr_cmt = 0 # unsigned number
+
+# Add or remove space before an embedded comment.
+#
+# Default: force
+sp_before_emb_cmt = force # ignore/add/remove/force/not_defined
+
+# Number of spaces before an embedded comment.
+#
+# Default: 1
+sp_num_before_emb_cmt = 1 # unsigned number
+
+# Add or remove space after an embedded comment.
+#
+# Default: force
+sp_after_emb_cmt = force # ignore/add/remove/force/not_defined
+
+# Number of spaces after an embedded comment.
+#
+# Default: 1
+sp_num_after_emb_cmt = 1 # unsigned number
+
+# (Java) Add or remove space between an annotation and the open parenthesis.
+sp_annotation_paren = ignore # ignore/add/remove/force
+
+# If true, vbrace tokens are dropped to the previous token and skipped.
+sp_skip_vbrace_tokens = false # true/false
+
+# Add or remove space after 'noexcept'.
+sp_after_noexcept = ignore # ignore/add/remove/force
+
+# Add or remove space after '_'.
+sp_vala_after_translation = ignore # ignore/add/remove/force
+
+# If true, a <TAB> is inserted after #define.
+force_tab_after_define = false # true/false
+
+#
+# Indenting options
+#
+
+# The number of columns to indent per level. Usually 2, 3, 4, or 8.
+#
+# Default: 8
+indent_columns = 4 # unsigned number
+
+# Whether to ignore indent for the first continuation line. Subsequent
+# continuation lines will still be indented to match the first.
+indent_ignore_first_continue = false # true/false
+
+# The continuation indent. If non-zero, this overrides the indent of '(', '['
+# and '=' continuation indents. Negative values are OK; negative value is
+# absolute and not increased for each '(' or '[' level.
+#
+# For FreeBSD, this is set to 4.
+indent_continue = 8 # number
+
+# The continuation indent, only for class header line(s). If non-zero, this
+# overrides the indent of 'class' continuation indents.
+indent_continue_class_head = 0 # unsigned number
+
+# Whether to indent empty lines (i.e. lines which contain only spaces before
+# the newline character).
+indent_single_newlines = false # true/false
+
+# The continuation indent for func_*_param if they are true. If non-zero, this
+# overrides the indent.
+indent_param = 4 # unsigned number
+
+# How to use tabs when indenting code.
+#
+# 0: Spaces only
+# 1: Indent with tabs to brace level, align with spaces (default)
+# 2: Indent and align with tabs, using spaces when not on a tabstop
+#
+# Default: 1
+indent_with_tabs = 0 # unsigned number
+
+# Whether to indent comments that are not at a brace level with tabs on a
+# tabstop. Requires indent_with_tabs=2. If false, will use spaces.
+indent_cmt_with_tabs = false # true/false
+
+# Whether to indent strings broken by '\' so that they line up.
+indent_align_string = false # true/false
+
+# The number of spaces to indent multi-line XML strings.
+# Requires indent_align_string=true.
+indent_xml_string = 4 # unsigned number
+
+# Spaces to indent '{' from level.
+indent_brace = 0 # unsigned number
+
+# Whether braces are indented to the body level.
+indent_braces = false # true/false
+
+# Whether to disable indenting function braces if indent_braces=true.
+indent_braces_no_func = false # true/false
+
+# Whether to disable indenting class braces if indent_braces=true.
+indent_braces_no_class = false # true/false
+
+# Whether to disable indenting struct braces if indent_braces=true.
+indent_braces_no_struct = false # true/false
+
+# Whether to indent based on the size of the brace parent,
+# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.
+indent_brace_parent = false # true/false
+
+# Whether to indent based on the open parenthesis instead of the open brace
+# in '({\n'.
+indent_paren_open_brace = false # true/false
+
+# (C#) Whether to indent the brace of a C# delegate by another level.
+indent_cs_delegate_brace = false # true/false
+
+# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by
+# another level.
+indent_cs_delegate_body = false # true/false
+
+# Whether to indent the body of a 'namespace'.
+indent_namespace = false # true/false
+
+# Whether to indent only the first namespace, and not any nested namespaces.
+# Requires indent_namespace=true.
+indent_namespace_single_indent = false # true/false
+
+# The number of spaces to indent a namespace block.
+# If set to zero, use the value indent_columns
+indent_namespace_level = 0 # unsigned number
+
+# If the body of the namespace is longer than this number, it won't be
+# indented. Requires indent_namespace=true. 0 means no limit.
+indent_namespace_limit = 0 # unsigned number
+
+# Whether to indent only in inner namespaces (nested in other namespaces).
+# Requires indent_namespace=true.
+indent_namespace_inner_only = false # true/false
+
+# Whether the 'extern "C"' body is indented.
+indent_extern = false # true/false
+
+# Whether the 'class' body is indented.
+indent_class = false # true/false
+
+# Whether to ignore indent for the leading base class colon.
+indent_ignore_before_class_colon = false # true/false
+
+# Additional indent before the leading base class colon.
+# Negative values decrease indent down to the first column.
+# Requires a newline break before colon (see pos_class_colon
+# and nl_class_colon)
+indent_before_class_colon = 0 # number
+
+# Whether to indent the stuff after a leading base class colon.
+indent_class_colon = false # true/false
+
+# Whether to indent based on a class colon instead of the stuff after the
+# colon. Requires indent_class_colon=true.
+indent_class_on_colon = false # true/false
+
+# Whether to ignore indent for a leading class initializer colon.
+indent_ignore_before_constr_colon = false # true/false
+
+# Whether to indent the stuff after a leading class initializer colon.
+indent_constr_colon = false # true/false
+
+# Virtual indent from the ':' for member initializers.
+#
+# Default: 2
+indent_ctor_init_leading = 0 # unsigned number
+
+# Virtual indent from the ':' for following member initializers.
+#
+# Default: 2
+indent_ctor_init_following = 2 # unsigned number
+
+# Additional indent for constructor initializer list.
+# Negative values decrease indent down to the first column.
+indent_ctor_init = 0 # number
+
+# Whether to indent 'if' following 'else' as a new block under the 'else'.
+# If false, 'else\nif' is treated as 'else if' for indenting purposes.
+indent_else_if = false # true/false
+
+# Amount to indent variable declarations after a open brace.
+#
+# <0: Relative
+# >=0: Absolute
+indent_var_def_blk = 0 # number
+
+# Whether to indent continued variable declarations instead of aligning.
+indent_var_def_cont = true # true/false
+
+# How to indent continued shift expressions ('<<' and '>>').
+# Set align_left_shift=false when using this.
+# 0: Align shift operators instead of indenting them (default)
+# 1: Indent by one level
+# -1: Preserve original indentation
+indent_shift = 1 # number
+
+# Whether to force indentation of function definitions to start in column 1.
+indent_func_def_force_col1 = false # true/false
+
+# Whether to indent continued function call parameters one indent level,
+# rather than aligning parameters under the open parenthesis.
+indent_func_call_param = true # true/false
+
+# Whether to indent continued function definition parameters one indent level,
+# rather than aligning parameters under the open parenthesis.
+indent_func_def_param = true # true/false
+
+# for function definitions, only if indent_func_def_param is false
+# Allows to align params when appropriate and indent them when not
+# behave as if it was true if paren position is more than this value
+# if paren position is more than the option value
+indent_func_def_param_paren_pos_threshold = 0 # unsigned number
+
+# Whether to indent continued function call prototype one indent level,
+# rather than aligning parameters under the open parenthesis.
+indent_func_proto_param = true # true/false
+
+# Whether to indent continued function call declaration one indent level,
+# rather than aligning parameters under the open parenthesis.
+indent_func_class_param = true # true/false
+
+# Whether to indent continued class variable constructors one indent level,
+# rather than aligning parameters under the open parenthesis.
+indent_func_ctor_var_param = true # true/false
+
+# Whether to indent continued template parameter list one indent level,
+# rather than aligning parameters under the open parenthesis.
+indent_template_param = true # true/false
+
+# Double the indent for indent_func_xxx_param options.
+# Use both values of the options indent_columns and indent_param.
+indent_func_param_double = true # true/false
+
+# Indentation column for standalone 'const' qualifier on a function
+# prototype.
+indent_func_const = 0 # unsigned number
+
+# Indentation column for standalone 'throw' qualifier on a function
+# prototype.
+indent_func_throw = 0 # unsigned number
+
+# How to indent within a macro followed by a brace on the same line
+# This allows reducing the indent in macros that have (for example)
+# `do { ... } while (0)` blocks bracketing them.
+#
+# true: add an indent for the brace on the same line as the macro
+# false: do not add an indent for the brace on the same line as the macro
+#
+# Default: true
+indent_macro_brace = false # true/false
+
+# The number of spaces to indent a continued '->' or '.'.
+# Usually set to 0, 1, or indent_columns.
+indent_member = 0 # unsigned number
+
+# Whether lines broken at '.' or '->' should be indented by a single indent.
+# The indent_member option will not be effective if this is set to true.
+indent_member_single = true # true/false
+
+# Spaces to indent single line ('//') comments on lines before code.
+indent_single_line_comments_before = 0 # unsigned number
+
+# Spaces to indent single line ('//') comments on lines after code.
+indent_single_line_comments_after = 0 # unsigned number
+
+# When opening a paren for a control statement (if, for, while, etc), increase
+# the indent level by this value. Negative values decrease the indent level.
+indent_sparen_extra = 0 # number
+
+# Whether to indent trailing single line ('//') comments relative to the code
+# instead of trying to keep the same absolute column.
+indent_relative_single_line_comments = false # true/false
+
+# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns.
+indent_switch_case = 0 # unsigned number
+
+# Spaces to indent the body of a 'switch' before any 'case'.
+# Usually the same as indent_columns or indent_switch_case.
+indent_switch_body = 0 # unsigned number
+
+# Whether to ignore indent for '{' following 'case'.
+indent_ignore_case_brace = false # true/false
+
+# indent 'break' with 'case' from 'switch'.
+indent_switch_break_with_case = false # true/false
+
+# Whether to indent preprocessor statements inside of switch statements.
+#
+# Default: true
+indent_switch_pp = false # true/false
+
+# Spaces to shift the 'case' line, without affecting any other lines.
+# Usually 0.
+indent_case_shift = 0 # unsigned number
+
+# Spaces to indent '{' from 'case'. By default, the brace will appear under
+# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK.
+indent_case_brace = 0 # number
+
+# Whether to indent comments not found in first column.
+#
+# Default: true
+indent_comment = true # true/false
+
+# Whether to indent comments found in first column.
+indent_col1_comment = false # true/false
+
+# Whether to indent multi string literal in first column.
+indent_col1_multi_string_literal = false # true/false
+
+# Align comments on adjacent lines that are this many columns apart or less.
+#
+# Default: 3
+indent_comment_align_thresh = 3 # unsigned number
+
+# Whether to ignore indent for goto labels.
+indent_ignore_label = true # true/false
+
+# How to indent goto labels.
+#
+# >0: Absolute column where 1 is the leftmost column
+# <=0: Subtract from brace indent
+#
+# Default: 1
+indent_label = 1 # number
+
+# How to indent access specifiers that are followed by a
+# colon.
+#
+# >0: Absolute column where 1 is the leftmost column
+# <=0: Subtract from brace indent
+#
+# Default: 1
+indent_access_spec = 1 # number
+
+# Whether to indent the code after an access specifier by one level.
+# If true, this option forces 'indent_access_spec=0'.
+indent_access_spec_body = false # true/false
+
+# If an open parenthesis is followed by a newline, whether to indent the next
+# line so that it lines up after the open parenthesis (not recommended).
+indent_paren_nl = false # true/false
+
+# How to indent a close parenthesis after a newline.
+#
+# 0: Indent to body level (default)
+# 1: Align under the open parenthesis
+# 2: Indent to the brace level
+indent_paren_close = 1 # unsigned number
+
+# Whether to indent the open parenthesis of a function definition,
+# if the parenthesis is on its own line.
+indent_paren_after_func_def = false # true/false
+
+# Whether to indent the open parenthesis of a function declaration,
+# if the parenthesis is on its own line.
+indent_paren_after_func_decl = false # true/false
+
+# Whether to indent the open parenthesis of a function call,
+# if the parenthesis is on its own line.
+indent_paren_after_func_call = false # true/false
+
+# How to indent a comma when inside braces.
+# 0: Indent by one level (default)
+# 1: Align under the open brace
+# -1: Preserve original indentation
+indent_comma_brace = 0 # number
+
+# How to indent a comma when inside parentheses.
+# 0: Indent by one level (default)
+# 1: Align under the open parenthesis
+# -1: Preserve original indentation
+indent_comma_paren = 0 # number
+
+# How to indent a Boolean operator when inside parentheses.
+# 0: Indent by one level (default)
+# 1: Align under the open parenthesis
+# -1: Preserve original indentation
+indent_bool_paren = 0 # number
+
+# Whether to ignore the indentation of a Boolean operator when outside
+# parentheses.
+indent_ignore_bool = false # true/false
+
+# Whether to ignore the indentation of an arithmetic operator.
+indent_ignore_arith = false # true/false
+
+# Whether to indent a semicolon when inside a for parenthesis.
+# If true, aligns under the open for parenthesis.
+indent_semicolon_for_paren = false # true/false
+
+# Whether to ignore the indentation of a semicolon outside of a 'for'
+# statement.
+indent_ignore_semicolon = false # true/false
+
+# Whether to align the first expression to following ones
+# if indent_bool_paren=true.
+indent_first_bool_expr = false # true/false
+
+# Whether to align the first expression to following ones
+# if indent_semicolon_for_paren=true.
+indent_first_for_expr = false # true/false
+
+# If an open square is followed by a newline, whether to indent the next line
+# so that it lines up after the open square (not recommended).
+indent_square_nl = false # true/false
+
+# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies.
+indent_preserve_sql = false # true/false
+
+# Whether to ignore the indentation of an assignment operator.
+indent_ignore_assign = false # true/false
+
+# Whether to align continued statements at the '='. If false or if the '=' is
+# followed by a newline, the next line is indent one tab.
+#
+# Default: true
+indent_align_assign = false # true/false
+
+# If true, the indentation of the chunks after a '=' sequence will be set at
+# LHS token indentation column before '='.
+indent_off_after_assign = false # true/false
+
+# Whether to align continued statements at the '('. If false or the '(' is
+# followed by a newline, the next line indent is one tab.
+#
+# Default: true
+indent_align_paren = true # true/false
+
+# (OC) Whether to indent Objective-C code inside message selectors.
+indent_oc_inside_msg_sel = false # true/false
+
+# (OC) Whether to indent Objective-C blocks at brace level instead of usual
+# rules.
+indent_oc_block = false # true/false
+
+# (OC) Indent for Objective-C blocks in a message relative to the parameter
+# name.
+#
+# =0: Use indent_oc_block rules
+# >0: Use specified number of spaces to indent
+indent_oc_block_msg = 0 # unsigned number
+
+# (OC) Minimum indent for subsequent parameters
+indent_oc_msg_colon = 0 # unsigned number
+
+# (OC) Whether to prioritize aligning with initial colon (and stripping spaces
+# from lines, if necessary).
+#
+# Default: true
+indent_oc_msg_prioritize_first_colon = true # true/false
+
+# (OC) Whether to indent blocks the way that Xcode does by default
+# (from the keyword if the parameter is on its own line; otherwise, from the
+# previous indentation level). Requires indent_oc_block_msg=true.
+indent_oc_block_msg_xcode_style = false # true/false
+
+# (OC) Whether to indent blocks from where the brace is, relative to a
+# message keyword. Requires indent_oc_block_msg=true.
+indent_oc_block_msg_from_keyword = false # true/false
+
+# (OC) Whether to indent blocks from where the brace is, relative to a message
+# colon. Requires indent_oc_block_msg=true.
+indent_oc_block_msg_from_colon = false # true/false
+
+# (OC) Whether to indent blocks from where the block caret is.
+# Requires indent_oc_block_msg=true.
+indent_oc_block_msg_from_caret = false # true/false
+
+# (OC) Whether to indent blocks from where the brace caret is.
+# Requires indent_oc_block_msg=true.
+indent_oc_block_msg_from_brace = false # true/false
+
+# When indenting after virtual brace open and newline add further spaces to
+# reach this minimum indent.
+indent_min_vbrace_open = 4 # unsigned number
+
+# Whether to add further spaces after regular indent to reach next tabstop
+# when identing after virtual brace open and newline.
+indent_vbrace_open_on_tabstop = false # true/false
+
+# How to indent after a brace followed by another token (not a newline).
+# true: indent all contained lines to match the token
+# false: indent all contained lines to match the brace
+#
+# Default: true
+indent_token_after_brace = false # true/false
+
+# Whether to indent the body of a C++11 lambda.
+indent_cpp_lambda_body = false # true/false
+
+# How to indent compound literals that are being returned.
+# true: add both the indent from return & the compound literal open brace (ie:
+# 2 indent levels)
+# false: only indent 1 level, don't add the indent for the open brace, only add
+# the indent for the return.
+#
+# Default: true
+indent_compound_literal_return = true # true/false
+
+# (C#) Whether to indent a 'using' block if no braces are used.
+#
+# Default: true
+indent_using_block = true # true/false
+
+# How to indent the continuation of ternary operator.
+#
+# 0: Off (default)
+# 1: When the `if_false` is a continuation, indent it under `if_false`
+# 2: When the `:` is a continuation, indent it under `?`
+indent_ternary_operator = 0 # unsigned number
+
+# Whether to indent the statments inside ternary operator.
+indent_inside_ternary_operator = false # true/false
+
+# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column.
+indent_off_after_return = false # true/false
+
+# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column.
+indent_off_after_return_new = false # true/false
+
+# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token.
+indent_single_after_return = false # true/false
+
+# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they
+# have their own indentation).
+indent_ignore_asm_block = true # true/false
+
+# Don't indent the close parenthesis of a function definition,
+# if the parenthesis is on its own line.
+donot_indent_func_def_close_paren = false # true/false
+
+#
+# Newline adding and removing options
+#
+
+# Whether to collapse empty blocks between '{' and '}' except for functions.
+# Use nl_collapse_empty_body_functions to specify how empty function braces
+# should be formatted.
+nl_collapse_empty_body = true # true/false
+
+# Whether to collapse empty blocks between '{' and '}' for functions only.
+# If true, overrides nl_inside_empty_func.
+nl_collapse_empty_body_functions = false # true/false
+
+# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'.
+nl_assign_leave_one_liners = true # true/false
+
+# Don't split one-line braced statements inside a 'class xx { }' body.
+nl_class_leave_one_liners = true # true/false
+
+# Don't split one-line enums, as in 'enum foo { BAR = 15 };'
+nl_enum_leave_one_liners = true # true/false
+
+# Don't split one-line get or set functions.
+nl_getset_leave_one_liners = true # true/false
+
+# (C#) Don't split one-line property get or set functions.
+nl_cs_property_leave_one_liners = true # true/false
+
+# Don't split one-line function definitions, as in 'int foo() { return 0; }'.
+# might modify nl_func_type_name
+nl_func_leave_one_liners = true # true/false
+
+# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'.
+nl_cpp_lambda_leave_one_liners = true # true/false
+
+# Don't split one-line if/else statements, as in 'if(...) b++;'.
+nl_if_leave_one_liners = false # true/false
+
+# Don't split one-line while statements, as in 'while(...) b++;'.
+nl_while_leave_one_liners = false # true/false
+
+# Don't split one-line do statements, as in 'do { b++; } while(...);'.
+nl_do_leave_one_liners = false # true/false
+
+# Don't split one-line for statements, as in 'for(...) b++;'.
+nl_for_leave_one_liners = false # true/false
+
+# (OC) Don't split one-line Objective-C messages.
+nl_oc_msg_leave_one_liner = true # true/false
+
+# (OC) Add or remove newline between method declaration and '{'.
+nl_oc_mdef_brace = ignore # ignore/add/remove/force
+
+# (OC) Add or remove newline between Objective-C block signature and '{'.
+nl_oc_block_brace = ignore # ignore/add/remove/force
+
+# (OC) Add or remove blank line before '@interface' statement.
+nl_oc_before_interface = ignore # ignore/add/remove/force
+
+# (OC) Add or remove blank line before '@implementation' statement.
+nl_oc_before_implementation = ignore # ignore/add/remove/force
+
+# (OC) Add or remove blank line before '@end' statement.
+nl_oc_before_end = ignore # ignore/add/remove/force
+
+# (OC) Add or remove newline between '@interface' and '{'.
+nl_oc_interface_brace = ignore # ignore/add/remove/force
+
+# (OC) Add or remove newline between '@implementation' and '{'.
+nl_oc_implementation_brace = ignore # ignore/add/remove/force
+
+# Add or remove newlines at the start of the file.
+nl_start_of_file = remove # ignore/add/remove/force
+
+# The minimum number of newlines at the start of the file (only used if
+# nl_start_of_file is 'add' or 'force').
+nl_start_of_file_min = 0 # unsigned number
+
+# Add or remove newline at the end of the file.
+nl_end_of_file = force # ignore/add/remove/force
+
+# The minimum number of newlines at the end of the file (only used if
+# nl_end_of_file is 'add' or 'force').
+nl_end_of_file_min = 1 # unsigned number
+
+# Add or remove newline between '=' and '{'.
+nl_assign_brace = ignore # ignore/add/remove/force
+
+# (D) Add or remove newline between '=' and '['.
+nl_assign_square = ignore # ignore/add/remove/force
+
+# Add or remove newline between '[]' and '{'.
+nl_tsquare_brace = ignore # ignore/add/remove/force
+
+# (D) Add or remove newline after '= ['. Will also affect the newline before
+# the ']'.
+nl_after_square_assign = ignore # ignore/add/remove/force
+
+# Add or remove newline between a function call's ')' and '{', as in
+# 'list_for_each(item, &list) { }'.
+nl_fcall_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'enum' and '{'.
+nl_enum_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'enum' and 'class'.
+nl_enum_class = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'enum class' and the identifier.
+nl_enum_class_identifier = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'enum class' type and ':'.
+nl_enum_identifier_colon = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'enum class identifier :' and type.
+nl_enum_colon_type = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'struct and '{'.
+nl_struct_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'union' and '{'.
+nl_union_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'if' and '{'.
+nl_if_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'else'.
+nl_brace_else = remove # ignore/add/remove/force
+
+# Add or remove newline between 'else if' and '{'. If set to ignore,
+# nl_if_brace is used instead.
+nl_elseif_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'else' and '{'.
+nl_else_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'else' and 'if'.
+nl_else_if = remove # ignore/add/remove/force
+
+# Add or remove newline before '{' opening brace
+nl_before_opening_brace_func_class_def = force # ignore/add/remove/force
+
+# Add or remove newline before 'if'/'else if' closing parenthesis.
+nl_before_if_closing_paren = remove # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'finally'.
+nl_brace_finally = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'finally' and '{'.
+nl_finally_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'try' and '{'.
+nl_try_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between get/set and '{'.
+nl_getset_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'for' and '{'.
+nl_for_brace = remove # ignore/add/remove/force
+
+# Add or remove newline before the '{' of a 'catch' statement, as in
+# 'catch (decl) <here> {'.
+nl_catch_brace = ignore # ignore/add/remove/force
+
+# (OC) Add or remove newline before the '{' of a '@catch' statement, as in
+# '@catch (decl) <here> {'. If set to ignore, nl_catch_brace is used.
+nl_oc_catch_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'catch'.
+nl_brace_catch = ignore # ignore/add/remove/force
+
+# (OC) Add or remove newline between '}' and '@catch'. If set to ignore,
+# nl_brace_catch is used.
+nl_oc_brace_catch = ignore # ignore/add/remove/force
+
+# Add or remove newline between '}' and ']'.
+nl_brace_square = ignore # ignore/add/remove/force
+
+# Add or remove newline between '}' and ')' in a function invocation.
+nl_brace_fparen = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'while' and '{'.
+nl_while_brace = remove # ignore/add/remove/force
+
+# (D) Add or remove newline between 'scope (x)' and '{'.
+nl_scope_brace = ignore # ignore/add/remove/force
+
+# (D) Add or remove newline between 'unittest' and '{'.
+nl_unittest_brace = ignore # ignore/add/remove/force
+
+# (D) Add or remove newline between 'version (x)' and '{'.
+nl_version_brace = ignore # ignore/add/remove/force
+
+# (C#) Add or remove newline between 'using' and '{'.
+nl_using_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between two open or close braces. Due to general
+# newline/brace handling, REMOVE may not work.
+nl_brace_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'do' and '{'.
+nl_do_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'while' of 'do' statement.
+nl_brace_while = remove # ignore/add/remove/force
+
+# Add or remove newline between 'switch' and '{'.
+nl_switch_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'synchronized' and '{'.
+nl_synchronized_brace = ignore # ignore/add/remove/force
+
+# Add a newline between ')' and '{' if the ')' is on a different line than the
+# if/for/etc.
+#
+# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and
+# nl_catch_brace.
+nl_multi_line_cond = false # true/false
+
+# Add a newline after '(' if an if/for/while/switch condition spans multiple
+# lines
+nl_multi_line_sparen_open = ignore # ignore/add/remove/force
+
+# Add a newline before ')' if an if/for/while/switch condition spans multiple
+# lines. Overrides nl_before_if_closing_paren if both are specified.
+nl_multi_line_sparen_close = ignore # ignore/add/remove/force
+
+# Force a newline in a define after the macro name for multi-line defines.
+nl_multi_line_define = false # true/false
+
+# Whether to add a newline before 'case', and a blank line before a 'case'
+# statement that follows a ';' or '}'.
+nl_before_case = false # true/false
+
+# Whether to add a newline after a 'case' statement.
+nl_after_case = true # true/false
+
+# Add or remove newline between a case ':' and '{'.
+#
+# Overrides nl_after_case.
+nl_case_colon_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between ')' and 'throw'.
+nl_before_throw = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'namespace' and '{'.
+nl_namespace_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template class.
+nl_template_class = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template class declaration.
+#
+# Overrides nl_template_class.
+nl_template_class_decl = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<>' of a specialized class declaration.
+#
+# Overrides nl_template_class_decl.
+nl_template_class_decl_special = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template class definition.
+#
+# Overrides nl_template_class.
+nl_template_class_def = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<>' of a specialized class definition.
+#
+# Overrides nl_template_class_def.
+nl_template_class_def_special = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template function.
+nl_template_func = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template function
+# declaration.
+#
+# Overrides nl_template_func.
+nl_template_func_decl = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<>' of a specialized function
+# declaration.
+#
+# Overrides nl_template_func_decl.
+nl_template_func_decl_special = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template function
+# definition.
+#
+# Overrides nl_template_func.
+nl_template_func_def = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<>' of a specialized function
+# definition.
+#
+# Overrides nl_template_func_def.
+nl_template_func_def_special = ignore # ignore/add/remove/force
+
+# Add or remove newline after 'template<...>' of a template variable.
+nl_template_var = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'template<...>' and 'using' of a templated
+# type alias.
+nl_template_using = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'class' and '{'.
+nl_class_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline before or after (depending on pos_class_comma,
+# may not be IGNORE) each',' in the base class list.
+nl_class_init_args = ignore # ignore/add/remove/force
+
+# Add or remove newline after each ',' in the constructor member
+# initialization. Related to nl_constr_colon, pos_constr_colon and
+# pos_constr_comma.
+nl_constr_init_args = ignore # ignore/add/remove/force
+
+# Add or remove newline before first element, after comma, and after last
+# element, in 'enum'.
+nl_enum_own_lines = ignore # ignore/add/remove/force
+
+# Add or remove newline between return type and function name in a function
+# definition.
+# might be modified by nl_func_leave_one_liners
+nl_func_type_name = force # ignore/add/remove/force
+
+# Add or remove newline between return type and function name inside a class
+# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name
+# is used instead.
+nl_func_type_name_class = ignore # ignore/add/remove/force
+
+# Add or remove newline between class specification and '::'
+# in 'void A::f() { }'. Only appears in separate member implementation (does
+# not appear with in-line implementation).
+nl_func_class_scope = ignore # ignore/add/remove/force
+
+# Add or remove newline between function scope and name, as in
+# 'void A :: <here> f() { }'.
+nl_func_scope_name = ignore # ignore/add/remove/force
+
+# Add or remove newline between return type and function name in a prototype.
+nl_func_proto_type_name = remove # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '(' in the
+# declaration.
+nl_func_paren = remove # ignore/add/remove/force
+
+# Overrides nl_func_paren for functions with no parameters.
+nl_func_paren_empty = ignore # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '(' in the
+# definition.
+nl_func_def_paren = remove # ignore/add/remove/force
+
+# Overrides nl_func_def_paren for functions with no parameters.
+nl_func_def_paren_empty = ignore # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '(' in the
+# call.
+nl_func_call_paren = remove # ignore/add/remove/force
+
+# Overrides nl_func_call_paren for functions with no parameters.
+nl_func_call_paren_empty = ignore # ignore/add/remove/force
+
+# Add or remove newline after '(' in a function declaration.
+nl_func_decl_start = ignore # ignore/add/remove/force
+
+# Add or remove newline after '(' in a function definition.
+nl_func_def_start = ignore # ignore/add/remove/force
+
+# Overrides nl_func_decl_start when there is only one parameter.
+nl_func_decl_start_single = ignore # ignore/add/remove/force
+
+# Overrides nl_func_def_start when there is only one parameter.
+nl_func_def_start_single = ignore # ignore/add/remove/force
+
+# Whether to add a newline after '(' in a function declaration if '(' and ')'
+# are in different lines. If false, nl_func_decl_start is used instead.
+nl_func_decl_start_multi_line = false # true/false
+
+# Whether to add a newline after '(' in a function definition if '(' and ')'
+# are in different lines. If false, nl_func_def_start is used instead.
+nl_func_def_start_multi_line = false # true/false
+
+# Add or remove newline after each ',' in a function declaration.
+nl_func_decl_args = ignore # ignore/add/remove/force
+
+# Add or remove newline after each ',' in a function definition.
+nl_func_def_args = ignore # ignore/add/remove/force
+
+# Add or remove newline after each ',' in a function call.
+nl_func_call_args = ignore # ignore/add/remove/force
+
+# Whether to add a newline after each ',' in a function declaration if '('
+# and ')' are in different lines. If false, nl_func_decl_args is used instead.
+nl_func_decl_args_multi_line = false # true/false
+
+# Whether to add a newline after each ',' in a function definition if '('
+# and ')' are in different lines. If false, nl_func_def_args is used instead.
+nl_func_def_args_multi_line = false # true/false
+
+# Add or remove newline before the ')' in a function declaration.
+nl_func_decl_end = remove # ignore/add/remove/force
+
+# Add or remove newline before the ')' in a function definition.
+nl_func_def_end = remove # ignore/add/remove/force
+
+# Overrides nl_func_decl_end when there is only one parameter.
+nl_func_decl_end_single = ignore # ignore/add/remove/force
+
+# Overrides nl_func_def_end when there is only one parameter.
+nl_func_def_end_single = ignore # ignore/add/remove/force
+
+# Whether to add a newline before ')' in a function declaration if '(' and ')'
+# are in different lines. If false, nl_func_decl_end is used instead.
+nl_func_decl_end_multi_line = false # true/false
+
+# Whether to add a newline before ')' in a function definition if '(' and ')'
+# are in different lines. If false, nl_func_def_end is used instead.
+nl_func_def_end_multi_line = false # true/false
+
+# Add or remove newline between '()' in a function declaration.
+nl_func_decl_empty = remove # ignore/add/remove/force
+
+# Add or remove newline between '()' in a function definition.
+nl_func_def_empty = remove # ignore/add/remove/force
+
+# Add or remove newline between '()' in a function call.
+nl_func_call_empty = remove # ignore/add/remove/force
+
+# Whether to add a newline after '(' in a function call,
+# has preference over nl_func_call_start_multi_line.
+nl_func_call_start = ignore # ignore/add/remove/force
+
+# Whether to add a newline before ')' in a function call.
+nl_func_call_end = remove # ignore/add/remove/force
+
+# Whether to add a newline after '(' in a function call if '(' and ')' are in
+# different lines.
+nl_func_call_start_multi_line = false # true/false
+
+# Whether to add a newline after each ',' in a function call if '(' and ')'
+# are in different lines.
+nl_func_call_args_multi_line = false # true/false
+
+# Whether to add a newline before ')' in a function call if '(' and ')' are in
+# different lines.
+nl_func_call_end_multi_line = false # true/false
+
+# Whether to respect nl_func_call_XXX option incase of closure args.
+nl_func_call_args_multi_line_ignore_closures = true # true/false
+
+# Whether to add a newline after '<' of a template parameter list.
+nl_template_start = false # true/false
+
+# Whether to add a newline after each ',' in a template parameter list.
+nl_template_args = false # true/false
+
+# Whether to add a newline before '>' of a template parameter list.
+nl_template_end = false # true/false
+
+# (OC) Whether to put each Objective-C message parameter on a separate line.
+# See nl_oc_msg_leave_one_liner.
+nl_oc_msg_args = false # true/false
+
+# (OC) Minimum number of Objective-C message parameters before applying nl_oc_msg_args.
+nl_oc_msg_args_min_params = 0 # unsigned number
+
+# (OC) Max code width of Objective-C message before applying nl_oc_msg_args.
+nl_oc_msg_args_max_code_width = 0 # unsigned number
+
+# Add or remove newline between function signature and '{'.
+nl_fdef_brace = force # ignore/add/remove/force
+
+# Add or remove newline between function signature and '{',
+# if signature ends with ')'. Overrides nl_fdef_brace.
+nl_fdef_brace_cond = force # ignore/add/remove/force
+
+# Add or remove newline between C++11 lambda signature and '{'.
+nl_cpp_ldef_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'return' and the return expression.
+nl_return_expr = remove # ignore/add/remove/force
+
+# Add or remove newline between 'throw' and the throw expression.
+nl_throw_expr = ignore # ignore/add/remove/force/not_defined
+
+# Whether to add a newline after semicolons, except in 'for' statements.
+nl_after_semicolon = false # true/false
+
+# (Java) Add or remove newline between the ')' and '{{' of the double brace
+# initializer.
+nl_paren_dbrace_open = ignore # ignore/add/remove/force
+
+# Whether to add a newline after the type in an unnamed temporary
+# direct-list-initialization.
+nl_type_brace_init_lst = ignore # ignore/add/remove/force
+
+# Whether to add a newline after the open brace in an unnamed temporary
+# direct-list-initialization.
+nl_type_brace_init_lst_open = ignore # ignore/add/remove/force
+
+# Whether to add a newline before the close brace in an unnamed temporary
+# direct-list-initialization.
+nl_type_brace_init_lst_close = ignore # ignore/add/remove/force
+
+# Whether to add a newline before '{'.
+nl_before_brace_open = false # true/false
+
+# Whether to add a newline after '{'. This also adds a newline before the
+# matching '}'.
+nl_after_brace_open = true # true/false
+
+# Whether to add a newline between the open brace and a trailing single-line
+# comment. Requires nl_after_brace_open=true.
+nl_after_brace_open_cmt = false # true/false
+
+# Whether to add a newline after a virtual brace open with a non-empty body.
+# These occur in un-braced if/while/do/for statement bodies.
+nl_after_vbrace_open = false # true/false
+
+# Whether to add a newline after a virtual brace open with an empty body.
+# These occur in un-braced if/while/do/for statement bodies.
+nl_after_vbrace_open_empty = false # true/false
+
+# Whether to add a newline after '}'. Does not apply if followed by a
+# necessary ';'.
+nl_after_brace_close = true # true/false
+
+# Whether to add a newline after a virtual brace close,
+# as in 'if (foo) a++; <here> return;'.
+nl_after_vbrace_close = false # true/false
+
+# Add or remove newline between the close brace and identifier,
+# as in 'struct { int a; } <here> b;'. Affects enumerations, unions and
+# structures. If set to ignore, uses nl_after_brace_close.
+nl_brace_struct_var = remove # ignore/add/remove/force
+
+# Whether to alter newlines in '#define' macros.
+nl_define_macro = false # true/false
+
+# Whether to alter newlines between consecutive parenthesis closes. The number
+# of closing parentheses in a line will depend on respective open parenthesis
+# lines.
+nl_squeeze_paren_close = true # true/false
+
+# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and
+# '#endif'. Does not affect top-level #ifdefs.
+nl_squeeze_ifdef = true # true/false
+
+# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well.
+nl_squeeze_ifdef_top_level = false # true/false
+
+# Add or remove blank line before 'if'.
+nl_before_if = ignore # ignore/add/remove/force
+
+# Add or remove blank line after 'if' statement. Add/Force work only if the
+# next token is not a closing brace.
+nl_after_if = ignore # ignore/add/remove/force
+
+# Add or remove blank line before 'for'.
+nl_before_for = ignore # ignore/add/remove/force
+
+# Add or remove blank line after 'for' statement.
+nl_after_for = ignore # ignore/add/remove/force
+
+# Add or remove blank line before 'while'.
+nl_before_while = ignore # ignore/add/remove/force
+
+# Add or remove blank line after 'while' statement.
+nl_after_while = ignore # ignore/add/remove/force
+
+# Add or remove blank line before 'switch'.
+nl_before_switch = ignore # ignore/add/remove/force
+
+# Add or remove blank line after 'switch' statement.
+nl_after_switch = ignore # ignore/add/remove/force
+
+# Add or remove blank line before 'synchronized'.
+nl_before_synchronized = ignore # ignore/add/remove/force
+
+# Add or remove blank line after 'synchronized' statement.
+nl_after_synchronized = ignore # ignore/add/remove/force
+
+# Add or remove blank line before 'do'.
+nl_before_do = ignore # ignore/add/remove/force
+
+# Add or remove blank line after 'do/while' statement.
+nl_after_do = ignore # ignore/add/remove/force
+
+# Ignore nl_before_{if,for,switch,do,synchronized} if the control
+# statement is immediately after a case statement.
+# if nl_before_{if,for,switch,do} is set to remove, this option
+# does nothing.
+nl_before_ignore_after_case = false # true/false
+
+# Whether to put a blank line before 'return' statements, unless after an open
+# brace.
+nl_before_return = false # true/false
+
+# Whether to put a blank line after 'return' statements, unless followed by a
+# close brace.
+nl_after_return = false # true/false
+
+# Whether to put a blank line before a member '.' or '->' operators.
+nl_before_member = ignore # ignore/add/remove/force
+
+# (Java) Whether to put a blank line after a member '.' or '->' operators.
+nl_after_member = ignore # ignore/add/remove/force
+
+# Whether to double-space commented-entries in 'struct'/'union'/'enum'.
+nl_ds_struct_enum_cmt = false # true/false
+
+# Whether to force a newline before '}' of a 'struct'/'union'/'enum'.
+# (Lower priority than eat_blanks_before_close_brace.)
+nl_ds_struct_enum_close_brace = false # true/false
+
+# Add or remove newline before or after (depending on pos_class_colon) a class
+# colon, as in 'class Foo <here> : <or here> public Bar'.
+nl_class_colon = ignore # ignore/add/remove/force
+
+# Add or remove newline around a class constructor colon. The exact position
+# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma.
+nl_constr_colon = ignore # ignore/add/remove/force
+
+# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }'
+# into a single line. If true, prevents other brace newline rules from turning
+# such code into four lines.
+nl_namespace_two_to_one_liner = false # true/false
+
+# Whether to remove a newline in simple unbraced if statements, turning them
+# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'.
+nl_create_if_one_liner = false # true/false
+
+# Whether to remove a newline in simple unbraced for statements, turning them
+# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'.
+nl_create_for_one_liner = false # true/false
+
+# Whether to remove a newline in simple unbraced while statements, turning
+# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'.
+nl_create_while_one_liner = false # true/false
+
+# Whether to collapse a function definition whose body (not counting braces)
+# is only one line so that the entire definition (prototype, braces, body) is
+# a single line.
+nl_create_func_def_one_liner = false # true/false
+
+# Whether to collapse a function definition whose body (not counting braces)
+# is only one line so that the entire definition (prototype, braces, body) is
+# a single line.
+nl_create_list_one_liner = false # true/false
+
+# Whether to split one-line simple unbraced if statements into two lines by
+# adding a newline, as in 'if(b) <here> i++;'.
+nl_split_if_one_liner = false # true/false
+
+# Whether to split one-line simple unbraced for statements into two lines by
+# adding a newline, as in 'for (...) <here> stmt;'.
+nl_split_for_one_liner = false # true/false
+
+# Whether to split one-line simple unbraced while statements into two lines by
+# adding a newline, as in 'while (expr) <here> stmt;'.
+nl_split_while_one_liner = false # true/false
+
+# Don't add a newline before a cpp-comment in a parameter list of a function
+# call.
+donot_add_nl_before_cpp_comment = false # true/false
+
+#
+# Blank line options
+#
+
+# The maximum number of consecutive newlines (3 = 2 blank lines).
+nl_max = 2 # unsigned number
+
+# The maximum number of consecutive newlines in a function.
+nl_max_blank_in_func = 2 # unsigned number
+
+# The number of newlines inside an empty function body.
+# This option overrides eat_blanks_after_open_brace and
+# eat_blanks_before_close_brace, but is ignored when
+# nl_collapse_empty_body=true
+nl_inside_empty_func = 0 # unsigned number
+
+# The number of newlines before a function prototype.
+nl_before_func_body_proto = 0 # unsigned number
+
+# The number of newlines before a multi-line function definition.
+nl_before_func_body_def = 0 # unsigned number
+
+# The number of newlines before a class constructor/destructor prototype.
+nl_before_func_class_proto = 0 # unsigned number
+
+# The number of newlines before a class constructor/destructor definition.
+nl_before_func_class_def = 0 # unsigned number
+
+# The number of newlines after a function prototype.
+nl_after_func_proto = 0 # unsigned number
+
+# The number of newlines after a function prototype, if not followed by
+# another function prototype.
+nl_after_func_proto_group = 0 # unsigned number
+
+# The number of newlines after a class constructor/destructor prototype.
+nl_after_func_class_proto = 0 # unsigned number
+
+# The number of newlines after a class constructor/destructor prototype,
+# if not followed by another constructor/destructor prototype.
+nl_after_func_class_proto_group = 0 # unsigned number
+
+# Whether one-line method definitions inside a class body should be treated
+# as if they were prototypes for the purposes of adding newlines.
+#
+# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def
+# and nl_before_func_class_def for one-liners.
+nl_class_leave_one_liner_groups = false # true/false
+
+# The number of newlines after '}' of a multi-line function body.
+nl_after_func_body = 2 # unsigned number
+
+# The number of newlines after '}' of a multi-line function body in a class
+# declaration. Also affects class constructors/destructors.
+#
+# Overrides nl_after_func_body.
+nl_after_func_body_class = 2 # unsigned number
+
+# The number of newlines after '}' of a single line function body. Also
+# affects class constructors/destructors.
+#
+# Overrides nl_after_func_body and nl_after_func_body_class.
+nl_after_func_body_one_liner = 2 # unsigned number
+
+# The number of newlines before a block of typedefs. If nl_after_access_spec
+# is non-zero, that option takes precedence.
+#
+# 0: No change (default).
+nl_typedef_blk_start = 1 # unsigned number
+
+# The number of newlines after a block of typedefs.
+#
+# 0: No change (default).
+nl_typedef_blk_end = 1 # unsigned number
+
+# The maximum number of consecutive newlines within a block of typedefs.
+#
+# 0: No change (default).
+nl_typedef_blk_in = 0 # unsigned number
+
+# The minimum number of blank lines after a block of variable definitions
+# at the top of a function body. If any preprocessor directives appear
+# between the opening brace of the function and the variable block, then
+# it is considered as not at the top of the function.Newlines are added
+# before trailing preprocessor directives, if any exist.
+#
+# 0: No change (default).
+nl_var_def_blk_end_func_top = 1 # unsigned number
+
+# The number of newlines before a block of variable definitions not at the top
+# of a function body. If nl_after_access_spec is non-zero, that option takes
+# precedence.
+#
+# 0: No change (default).
+nl_var_def_blk_start = 0 # unsigned number
+
+# The number of newlines after a block of variable definitions not at the top
+# of a function body.
+#
+# 0: No change (default).
+nl_var_def_blk_end = 1 # unsigned number
+
+# The maximum number of consecutive newlines within a block of variable
+# definitions.
+#
+# 0: No change (default).
+nl_var_def_blk_in = 0 # unsigned number
+
+# The minimum number of newlines before a multi-line comment.
+# Doesn't apply if after a brace open or another multi-line comment.
+nl_before_block_comment = 0 # unsigned number
+
+# The minimum number of newlines before a single-line C comment.
+# Doesn't apply if after a brace open or other single-line C comments.
+nl_before_c_comment = 0 # unsigned number
+
+# The minimum number of newlines before a CPP comment.
+# Doesn't apply if after a brace open or other CPP comments.
+nl_before_cpp_comment = 0 # unsigned number
+
+# Whether to force a newline after a multi-line comment.
+nl_after_multiline_comment = false # true/false
+
+# Whether to force a newline after a label's colon.
+nl_after_label_colon = true # true/false
+
+# The number of newlines before a struct definition.
+nl_before_struct = 1 # unsigned number
+
+# The number of newlines after '}' or ';' of a struct/enum/union definition.
+nl_after_struct = 1 # unsigned number
+
+# The number of newlines before a class definition.
+nl_before_class = 0 # unsigned number
+
+# The number of newlines after '}' or ';' of a class definition.
+nl_after_class = 0 # unsigned number
+
+# The number of newlines before a namespace.
+nl_before_namespace = 0 # unsigned number
+
+# The number of newlines after '{' of a namespace. This also adds newlines
+# before the matching '}'.
+#
+# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if
+# applicable, otherwise no change.
+#
+# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace.
+nl_inside_namespace = 0 # unsigned number
+
+# The number of newlines after '}' of a namespace.
+nl_after_namespace = 0 # unsigned number
+
+# The number of newlines before an access specifier label. This also includes
+# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count
+# if after a brace open.
+#
+# 0: No change (default).
+nl_before_access_spec = 0 # unsigned number
+
+# The number of newlines after an access specifier label. This also includes
+# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count
+# if after a brace open.
+#
+# 0: No change (default).
+#
+# Overrides nl_typedef_blk_start and nl_var_def_blk_start.
+nl_after_access_spec = 0 # unsigned number
+
+# The number of newlines between a function definition and the function
+# comment, as in '// comment\n <here> void foo() {...}'.
+#
+# 0: No change (default).
+nl_comment_func_def = 0 # unsigned number
+
+# The number of newlines after a try-catch-finally block that isn't followed
+# by a brace close.
+#
+# 0: No change (default).
+nl_after_try_catch_finally = 0 # unsigned number
+
+# (C#) The number of newlines before and after a property, indexer or event
+# declaration.
+#
+# 0: No change (default).
+nl_around_cs_property = 0 # unsigned number
+
+# (C#) The number of newlines between the get/set/add/remove handlers.
+#
+# 0: No change (default).
+nl_between_get_set = 0 # unsigned number
+
+# (C#) Add or remove newline between property and the '{'.
+nl_property_brace = ignore # ignore/add/remove/force
+
+# Whether to remove blank lines after '{'.
+eat_blanks_after_open_brace = false # true/false
+
+# Whether to remove blank lines before '}'.
+eat_blanks_before_close_brace = false # true/false
+
+# How aggressively to remove extra newlines not in preprocessor.
+#
+# 0: No change (default)
+# 1: Remove most newlines not handled by other config
+# 2: Remove all newlines and reformat completely by config
+nl_remove_extra_newlines = 0 # unsigned number
+
+# (Java) Add or remove newline after an annotation statement. Only affects
+# annotations that are after a newline.
+nl_after_annotation = ignore # ignore/add/remove/force
+
+# (Java) Add or remove newline between two annotations.
+nl_between_annotation = ignore # ignore/add/remove/force
+
+# The number of newlines before a whole-file #ifdef.
+#
+# 0: No change (default).
+nl_before_whole_file_ifdef = 2 # unsigned number
+
+# The number of newlines after a whole-file #ifdef.
+#
+# 0: No change (default).
+nl_after_whole_file_ifdef = 0 # unsigned number
+
+# The number of newlines before a whole-file #endif.
+#
+# 0: No change (default).
+nl_before_whole_file_endif = 2 # unsigned number
+
+# The number of newlines after a whole-file #endif.
+#
+# 0: No change (default).
+nl_after_whole_file_endif = 2 # unsigned number
+
+#
+# Positioning options
+#
+
+# The position of arithmetic operators in wrapped expressions.
+pos_arith = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of assignment in wrapped expressions. Do not affect '='
+# followed by '{'.
+pos_assign = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of Boolean operators in wrapped expressions.
+pos_bool = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of comparison operators in wrapped expressions.
+pos_compare = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of conditional operators, as in the '?' and ':' of
+# 'expr ? stmt : stmt', in wrapped expressions.
+pos_conditional = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of the comma in wrapped expressions.
+pos_comma = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of the comma in enum entries.
+pos_enum_comma = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of the comma in the base class list if there is more than one
+# line. Affects nl_class_init_args.
+pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of the comma in the constructor initialization list.
+# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon.
+pos_constr_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of trailing/leading class colon, between class and base class
+# list. Affects nl_class_colon.
+pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of colons between constructor and member initialization.
+# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma.
+pos_constr_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+# The position of shift operators in wrapped expressions.
+pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
+
+#
+# Line splitting options
+#
+
+# Try to limit code width to N columns.
+code_width = 256 # unsigned number
+
+# Whether to fully split long 'for' statements at semi-colons.
+ls_for_split_full = true # true/false
+
+# Whether to fully split long function prototypes/calls at commas.
+# The option ls_code_width has priority over the option ls_func_split_full.
+ls_func_split_full = false # true/false
+
+# Whether to split lines as close to code_width as possible and ignore some
+# groupings.
+# The option ls_code_width has priority over the option ls_func_split_full.
+ls_code_width = false # true/false
+
+#
+# Code alignment options (not left column spaces/tabs)
+#
+
+# Whether to keep non-indenting tabs.
+align_keep_tabs = false # true/false
+
+# Whether to use tabs for aligning.
+align_with_tabs = false # true/false
+
+# Whether to bump out to the next tab when aligning.
+align_on_tabstop = true # true/false
+
+# Whether to right-align numbers.
+align_number_right = true # true/false
+
+# Whether to keep whitespace not required for alignment.
+align_keep_extra_space = false # true/false
+
+# Whether to align variable definitions in prototypes and functions.
+align_func_params = false # true/false
+
+# The span for aligning parameter definitions in function on parameter name.
+#
+# 0: Don't align (default).
+align_func_params_span = 0 # unsigned number
+
+# The threshold for aligning function parameter definitions.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_func_params_thresh = 0 # number
+
+# The gap for aligning function parameter definitions.
+align_func_params_gap = 1 # unsigned number
+
+# The span for aligning constructor value.
+#
+# 0: Don't align (default).
+align_constr_value_span = 0 # unsigned number
+
+# The threshold for aligning constructor value.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_constr_value_thresh = 0 # number
+
+# The gap for aligning constructor value.
+align_constr_value_gap = 0 # unsigned number
+
+# Whether to align parameters in single-line functions that have the same
+# name. The function names must already be aligned with each other.
+align_same_func_call_params = false # true/false
+
+# The span for aligning function-call parameters for single line functions.
+#
+# 0: Don't align (default).
+align_same_func_call_params_span = 1 # unsigned number
+
+# The threshold for aligning function-call parameters for single line
+# functions.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_same_func_call_params_thresh = 0 # number
+
+# The span for aligning variable definitions.
+#
+# 0: Don't align (default).
+align_var_def_span = 0 # unsigned number
+
+# How to consider (or treat) the '*' in the alignment of variable definitions.
+#
+# 0: Part of the type 'void * foo;' (default)
+# 1: Part of the variable 'void *foo;'
+# 2: Dangling 'void *foo;'
+# Dangling: the '*' will not be taken into account when aligning.
+align_var_def_star_style = 1 # unsigned number
+
+# How to consider (or treat) the '&' in the alignment of variable definitions.
+#
+# 0: Part of the type 'long & foo;' (default)
+# 1: Part of the variable 'long &foo;'
+# 2: Dangling 'long &foo;'
+# Dangling: the '&' will not be taken into account when aligning.
+align_var_def_amp_style = 1 # unsigned number
+
+# The threshold for aligning variable definitions.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_var_def_thresh = 0 # number
+
+# The gap for aligning variable definitions.
+align_var_def_gap = 0 # unsigned number
+
+# Whether to align the colon in struct bit fields.
+align_var_def_colon = false # true/false
+
+# The gap for aligning the colon in struct bit fields.
+align_var_def_colon_gap = 0 # unsigned number
+
+# Whether to align any attribute after the variable name.
+align_var_def_attribute = false # true/false
+
+# Whether to align inline struct/enum/union variable definitions.
+align_var_def_inline = true # true/false
+
+# The span for aligning on '=' in assignments.
+#
+# 0: Don't align (default).
+align_assign_span = 0 # unsigned number
+
+# The span for aligning on '{' in braced init list.
+#
+# 0: Don't align (default).
+align_braced_init_list_span = 0 # unsigned number
+
+# The span for aligning on '=' in function prototype modifier.
+#
+# 0: Don't align (default).
+align_assign_func_proto_span = 0 # unsigned number
+
+# The threshold for aligning on '=' in assignments.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_assign_thresh = 0 # number
+
+# Whether to align on the left most assignment when multiple
+# definitions are found on the same line.
+# Depends on 'align_assign_span' and 'align_assign_thresh' settings.
+align_assign_on_multi_var_defs = false # true/false
+
+# The threshold for aligning on '{' in braced init list.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_braced_init_list_thresh = 0 # number
+
+# How to apply align_assign_span to function declaration "assignments", i.e.
+# 'virtual void foo() = 0' or '~foo() = {default|delete}'.
+#
+# 0: Align with other assignments (default)
+# 1: Align with each other, ignoring regular assignments
+# 2: Don't align
+align_assign_decl_func = 0 # unsigned number
+
+# The span for aligning on '=' in enums.
+#
+# 0: Don't align (default).
+align_enum_equ_span = 0 # unsigned number
+
+# The threshold for aligning on '=' in enums.
+# Use a negative number for absolute thresholds.
+#
+# 0: no limit (default).
+align_enum_equ_thresh = 0 # number
+
+# The span for aligning class member definitions.
+#
+# 0: Don't align (default).
+align_var_class_span = 0 # unsigned number
+
+# The threshold for aligning class member definitions.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_var_class_thresh = 0 # number
+
+# The gap for aligning class member definitions.
+align_var_class_gap = 0 # unsigned number
+
+# The span for aligning struct/union member definitions.
+#
+# 0: Don't align (default).
+align_var_struct_span = 0 # unsigned number
+
+# The threshold for aligning struct/union member definitions.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_var_struct_thresh = 0 # number
+
+# The gap for aligning struct/union member definitions.
+align_var_struct_gap = 0 # unsigned number
+
+# The span for aligning struct initializer values.
+#
+# 0: Don't align (default).
+align_struct_init_span = 0 # unsigned number
+
+# The span for aligning single-line typedefs.
+#
+# 0: Don't align (default).
+align_typedef_span = 0 # unsigned number
+
+# The minimum space between the type and the synonym of a typedef.
+align_typedef_gap = 1 # unsigned number
+
+# How to align typedef'd functions with other typedefs.
+#
+# 0: Don't mix them at all (default)
+# 1: Align the open parenthesis with the types
+# 2: Align the function type name with the other type names
+align_typedef_func = 0 # unsigned number
+
+# How to consider (or treat) the '*' in the alignment of typedefs.
+#
+# 0: Part of the typedef type, 'typedef int * pint;' (default)
+# 1: Part of type name: 'typedef int *pint;'
+# 2: Dangling: 'typedef int *pint;'
+# Dangling: the '*' will not be taken into account when aligning.
+align_typedef_star_style = 1 # unsigned number
+
+# How to consider (or treat) the '&' in the alignment of typedefs.
+#
+# 0: Part of the typedef type, 'typedef int & intref;' (default)
+# 1: Part of type name: 'typedef int &intref;'
+# 2: Dangling: 'typedef int &intref;'
+# Dangling: the '&' will not be taken into account when aligning.
+align_typedef_amp_style = 1 # unsigned number
+
+# The span for aligning comments that end lines.
+#
+# 0: Don't align (default).
+align_right_cmt_span = 0 # unsigned number
+
+# Minimum number of columns between preceding text and a trailing comment in
+# order for the comment to qualify for being aligned. Must be non-zero to have
+# an effect.
+align_right_cmt_gap = 0 # unsigned number
+
+# If aligning comments, whether to mix with comments after '}' and #endif with
+# less than three spaces before the comment.
+align_right_cmt_mix = false # true/false
+
+# Whether to only align trailing comments that are at the same brace level.
+align_right_cmt_same_level = false # true/false
+
+# Minimum column at which to align trailing comments. Comments which are
+# aligned beyond this column, but which can be aligned in a lesser column,
+# may be "pulled in".
+#
+# 0: Ignore (default).
+align_right_cmt_at_col = 0 # unsigned number
+
+# The span for aligning function prototypes.
+#
+# 0: Don't align (default).
+align_func_proto_span = 0 # unsigned number
+
+# How to consider (or treat) the '*' in the alignment of function prototypes.
+#
+# 0: Part of the type 'void * foo();' (default)
+# 1: Part of the function 'void *foo();'
+# 2: Dangling 'void *foo();'
+# Dangling: the '*' will not be taken into account when aligning.
+align_func_proto_star_style = 0 # unsigned number
+
+# How to consider (or treat) the '&' in the alignment of function prototypes.
+#
+# 0: Part of the type 'long & foo();' (default)
+# 1: Part of the function 'long &foo();'
+# 2: Dangling 'long &foo();'
+# Dangling: the '&' will not be taken into account when aligning.
+align_func_proto_amp_style = 0 # unsigned number
+
+# The threshold for aligning function prototypes.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_func_proto_thresh = 0 # number
+
+# Minimum gap between the return type and the function name.
+align_func_proto_gap = 1 # unsigned number
+
+# Whether to align function prototypes on the 'operator' keyword instead of
+# what follows.
+align_on_operator = false # true/false
+
+# Whether to mix aligning prototype and variable declarations. If true,
+# align_var_def_XXX options are used instead of align_func_proto_XXX options.
+align_mix_var_proto = false # true/false
+
+# Whether to align single-line functions with function prototypes.
+# Uses align_func_proto_span.
+align_single_line_func = false # true/false
+
+# Whether to align the open brace of single-line functions.
+# Requires align_single_line_func=true. Uses align_func_proto_span.
+align_single_line_brace = false # true/false
+
+# Gap for align_single_line_brace.
+align_single_line_brace_gap = 1 # unsigned number
+
+# (OC) The span for aligning Objective-C message specifications.
+#
+# 0: Don't align (default).
+align_oc_msg_spec_span = 0 # unsigned number
+
+# Whether to align macros wrapped with a backslash and a newline. This will
+# not work right if the macro contains a multi-line comment.
+align_nl_cont = false # true/false
+
+# Whether to align macro functions and variables together.
+align_pp_define_together = false # true/false
+
+# The span for aligning on '#define' bodies.
+#
+# =0: Don't align (default)
+# >0: Number of lines (including comments) between blocks
+align_pp_define_span = 0 # unsigned number
+
+# The minimum space between label and value of a preprocessor define.
+align_pp_define_gap = 1 # unsigned number
+
+# Whether to align lines that start with '<<' with previous '<<'.
+#
+# Default: true
+align_left_shift = false # true/false
+
+# Whether to align comma-separated statements following '<<' (as used to
+# initialize Eigen matrices).
+align_eigen_comma_init = false # true/false
+
+# Whether to align text after 'asm volatile ()' colons.
+align_asm_colon = false # true/false
+
+# (OC) Span for aligning parameters in an Objective-C message call
+# on the ':'.
+#
+# 0: Don't align.
+align_oc_msg_colon_span = 0 # unsigned number
+
+# (OC) Whether to always align with the first parameter, even if it is too
+# short.
+align_oc_msg_colon_first = false # true/false
+
+# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration
+# on the ':'.
+align_oc_decl_colon = false # true/false
+
+# (OC) Whether to not align parameters in an Objectve-C message call if first
+# colon is not on next line of the message call (the same way Xcode does
+# aligment)
+align_oc_msg_colon_xcode_like = false # true/false
+
+#
+# Comment modification options
+#
+
+# Try to wrap comments at N columns.
+cmt_width = 256 # unsigned number
+
+# How to reflow comments.
+#
+# 0: No reflowing (apart from the line wrapping due to cmt_width) (default)
+# 1: No touching at all
+# 2: Full reflow
+cmt_reflow_mode = 1 # unsigned number
+
+# Path to a file that contains regular expressions describing patterns for
+# which the end of one line and the beginning of the next will be folded into
+# the same sentence or paragraph during full comment reflow. The regular
+# expressions are described using ECMAScript syntax. The syntax for this
+# specification is as follows, where "..." indicates the custom regular
+# expression and "n" indicates the nth end_of_prev_line_regex and
+# beg_of_next_line_regex regular expression pair:
+#
+# end_of_prev_line_regex[1] = "...$"
+# beg_of_next_line_regex[1] = "^..."
+# end_of_prev_line_regex[2] = "...$"
+# beg_of_next_line_regex[2] = "^..."
+# .
+# .
+# .
+# end_of_prev_line_regex[n] = "...$"
+# beg_of_next_line_regex[n] = "^..."
+#
+# Note that use of this option overrides the default reflow fold regular
+# expressions, which are internally defined as follows:
+#
+# end_of_prev_line_regex[1] = "[\w,\]\)]$"
+# beg_of_next_line_regex[1] = "^[\w,\[\(]"
+# end_of_prev_line_regex[2] = "\.$"
+# beg_of_next_line_regex[2] = "^[A-Z]"
+cmt_reflow_fold_regex_file = "" # string
+
+# Whether to indent wrapped lines to the start of the encompassing paragraph
+# during full comment reflow (cmt_reflow_mode = 2). Overrides the value
+# specified by cmt_sp_after_star_cont.
+#
+# Note that cmt_align_doxygen_javadoc_tags overrides this option for
+# paragraphs associated with javadoc tags
+cmt_reflow_indent_to_paragraph_start = false # true/false
+
+# Whether to convert all tabs to spaces in comments. If false, tabs in
+# comments are left alone, unless used for indenting.
+cmt_convert_tab_to_spaces = false # true/false
+
+# Whether to apply changes to multi-line comments, including cmt_width,
+# keyword substitution and leading chars.
+#
+# Default: true
+cmt_indent_multi = false # true/false
+
+# Whether to align doxygen javadoc-style tags ('@param', '@return', etc.)
+# and corresponding fields such that groups of consecutive block tags,
+# parameter names, and descriptions align with one another. Overrides that
+# which is specified by the cmt_sp_after_star_cont. If cmt_width > 0, it may
+# be necessary to enable cmt_indent_multi and set cmt_reflow_mode = 2
+# in order to achieve the desired alignment for line-wrapping.
+cmt_align_doxygen_javadoc_tags = false # true/false
+
+# The number of spaces to insert after the star and before doxygen
+# javadoc-style tags (@param, @return, etc). Requires enabling
+# cmt_align_doxygen_javadoc_tags. Overrides that which is specified by the
+# cmt_sp_after_star_cont.
+#
+# Default: 1
+cmt_sp_before_doxygen_javadoc_tags = 1 # unsigned number
+
+# Whether to change trailing, single-line c-comments into cpp-comments.
+cmt_trailing_single_line_c_to_cpp = false # true/false
+
+# Whether to group c-comments that look like they are in a block.
+cmt_c_group = false # true/false
+
+# Whether to put an empty '/*' on the first line of the combined c-comment.
+cmt_c_nl_start = false # true/false
+
+# Whether to add a newline before the closing '*/' of the combined c-comment.
+cmt_c_nl_end = false # true/false
+
+# Whether to change cpp-comments into c-comments.
+cmt_cpp_to_c = false # true/false
+
+# Whether to group cpp-comments that look like they are in a block. Only
+# meaningful if cmt_cpp_to_c=true.
+cmt_cpp_group = false # true/false
+
+# Whether to put an empty '/*' on the first line of the combined cpp-comment
+# when converting to a c-comment.
+#
+# Requires cmt_cpp_to_c=true and cmt_cpp_group=true.
+cmt_cpp_nl_start = false # true/false
+
+# Whether to add a newline before the closing '*/' of the combined cpp-comment
+# when converting to a c-comment.
+#
+# Requires cmt_cpp_to_c=true and cmt_cpp_group=true.
+cmt_cpp_nl_end = false # true/false
+
+# Whether to put a star on subsequent comment lines.
+cmt_star_cont = false # true/false
+
+# The number of spaces to insert at the start of subsequent comment lines.
+cmt_sp_before_star_cont = 0 # unsigned number
+
+# The number of spaces to insert after the star on subsequent comment lines.
+cmt_sp_after_star_cont = 3 # unsigned number
+
+# For multi-line comments with a '*' lead, remove leading spaces if the first
+# and last lines of the comment are the same length.
+#
+# Default: true
+cmt_multi_check_last = false # true/false
+
+# For multi-line comments with a '*' lead, remove leading spaces if the first
+# and last lines of the comment are the same length AND if the length is
+# bigger as the first_len minimum.
+#
+# Default: 4
+cmt_multi_first_len_minimum = 4 # unsigned number
+
+# Path to a file that contains text to insert at the beginning of a file if
+# the file doesn't start with a C/C++ comment. If the inserted text contains
+# '$(filename)', that will be replaced with the current file's name.
+cmt_insert_file_header = "" # string
+
+# Path to a file that contains text to insert at the end of a file if the
+# file doesn't end with a C/C++ comment. If the inserted text contains
+# '$(filename)', that will be replaced with the current file's name.
+cmt_insert_file_footer = "" # string
+
+# Path to a file that contains text to insert before a function definition if
+# the function isn't preceded by a C/C++ comment. If the inserted text
+# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be
+# replaced with, respectively, the name of the function, the javadoc '@param'
+# and '@return' stuff, or the name of the class to which the member function
+# belongs.
+cmt_insert_func_header = "" # string
+
+# Path to a file that contains text to insert before a class if the class
+# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)',
+# that will be replaced with the class name.
+cmt_insert_class_header = "" # string
+
+# Path to a file that contains text to insert before an Objective-C message
+# specification, if the method isn't preceded by a C/C++ comment. If the
+# inserted text contains '$(message)' or '$(javaparam)', these will be
+# replaced with, respectively, the name of the function, or the javadoc
+# '@param' and '@return' stuff.
+cmt_insert_oc_msg_header = "" # string
+
+# Whether a comment should be inserted if a preprocessor is encountered when
+# stepping backwards from a function name.
+#
+# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and
+# cmt_insert_class_header.
+cmt_insert_before_preproc = false # true/false
+
+# Whether a comment should be inserted if a function is declared inline to a
+# class definition.
+#
+# Applies to cmt_insert_func_header.
+#
+# Default: true
+cmt_insert_before_inlines = false # true/false
+
+# Whether a comment should be inserted if the function is a class constructor
+# or destructor.
+#
+# Applies to cmt_insert_func_header.
+cmt_insert_before_ctor_dtor = false # true/false
+
+#
+# Code modifying options (non-whitespace)
+#
+
+# Add or remove braces on a single-line 'do' statement.
+mod_full_brace_do = force # ignore/add/remove/force
+
+# Add or remove braces on a single-line 'for' statement.
+mod_full_brace_for = force # ignore/add/remove/force
+
+# (Pawn) Add or remove braces on a single-line function definition.
+mod_full_brace_function = force # ignore/add/remove/force
+
+# Add or remove braces on a single-line 'if' statement. Braces will not be
+# removed if the braced statement contains an 'else'.
+mod_full_brace_if = force # ignore/add/remove/force
+
+# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either
+# have, or do not have, braces. Overrides mod_full_brace_if.
+#
+# 0: Don't override mod_full_brace_if
+# 1: Add braces to all blocks if any block needs braces and remove braces if
+# they can be removed from all blocks
+# 2: Add braces to all blocks if any block already has braces, regardless of
+# whether it needs them
+# 3: Add braces to all blocks if any block needs braces and remove braces if
+# they can be removed from all blocks, except if all blocks have braces
+# despite none needing them
+mod_full_brace_if_chain = 0 # unsigned number
+
+# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain.
+# If true, mod_full_brace_if_chain will only remove braces from an 'if' that
+# does not have an 'else if' or 'else'.
+mod_full_brace_if_chain_only = true # true/false
+
+# Add or remove braces on single-line 'while' statement.
+mod_full_brace_while = force # ignore/add/remove/force
+
+# Add or remove braces on single-line 'using ()' statement.
+mod_full_brace_using = ignore # ignore/add/remove/force
+
+# Don't remove braces around statements that span N newlines
+mod_full_brace_nl = 0 # unsigned number
+
+# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks
+# which span multiple lines.
+#
+# Affects:
+# mod_full_brace_for
+# mod_full_brace_if
+# mod_full_brace_if_chain
+# mod_full_brace_if_chain_only
+# mod_full_brace_while
+# mod_full_brace_using
+#
+# Does not affect:
+# mod_full_brace_do
+# mod_full_brace_function
+mod_full_brace_nl_block_rem_mlcond = false # true/false
+
+# Add or remove unnecessary parenthesis on 'return' statement.
+mod_paren_on_return = remove # ignore/add/remove/force
+
+# Add or remove unnecessary parentheses on 'throw' statement.
+mod_paren_on_throw = ignore # ignore/add/remove/force/not_defined
+
+# (Pawn) Whether to change optional semicolons to real semicolons.
+mod_pawn_semicolon = false # true/false
+
+# Whether to fully parenthesize Boolean expressions in 'while' and 'if'
+# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'.
+mod_full_paren_if_bool = true # true/false
+
+# Whether to fully parenthesize Boolean expressions after '='
+# statement, as in 'x = a && b > c;' => 'x = (a && (b > c));'.
+mod_full_paren_assign_bool = false # true/false
+
+# Whether to fully parenthesize Boolean expressions after '='
+# statement, as in 'return a && b > c;' => 'return (a && (b > c));'.
+mod_full_paren_return_bool = false # true/false
+
+# Whether to remove superfluous semicolons.
+mod_remove_extra_semicolon = true # true/false
+
+# Whether to remove duplicate include.
+mod_remove_duplicate_include = true # true/false
+
+# If a function body exceeds the specified number of newlines and doesn't have
+# a comment after the close brace, a comment will be added.
+mod_add_long_function_closebrace_comment = 0 # unsigned number
+
+# If a namespace body exceeds the specified number of newlines and doesn't
+# have a comment after the close brace, a comment will be added.
+mod_add_long_namespace_closebrace_comment = 0 # unsigned number
+
+# If a class body exceeds the specified number of newlines and doesn't have a
+# comment after the close brace, a comment will be added.
+mod_add_long_class_closebrace_comment = 0 # unsigned number
+
+# If a switch body exceeds the specified number of newlines and doesn't have a
+# comment after the close brace, a comment will be added.
+mod_add_long_switch_closebrace_comment = 0 # unsigned number
+
+# If an #ifdef body exceeds the specified number of newlines and doesn't have
+# a comment after the #endif, a comment will be added.
+mod_add_long_ifdef_endif_comment = 0 # unsigned number
+
+# If an #ifdef or #else body exceeds the specified number of newlines and
+# doesn't have a comment after the #else, a comment will be added.
+mod_add_long_ifdef_else_comment = 0 # unsigned number
+
+# Whether to take care of the case by the mod_sort_xx options.
+mod_sort_case_sensitive = false # true/false
+
+# Whether to sort consecutive single-line 'import' statements.
+mod_sort_import = false # true/false
+
+# (C#) Whether to sort consecutive single-line 'using' statements.
+mod_sort_using = false # true/false
+
+# Whether to sort consecutive single-line '#include' statements (C/C++) and
+# '#import' statements (Objective-C). Be aware that this has the potential to
+# break your code if your includes/imports have ordering dependencies.
+mod_sort_include = true # true/false
+
+# Whether to prioritize '#include' and '#import' statements that contain
+# filename without extension when sorting is enabled.
+mod_sort_incl_import_prioritize_filename = false # true/false
+
+# Whether to prioritize '#include' and '#import' statements that does not
+# contain extensions when sorting is enabled.
+mod_sort_incl_import_prioritize_extensionless = false # true/false
+
+# Whether to prioritize '#include' and '#import' statements that contain
+# angle over quotes when sorting is enabled.
+mod_sort_incl_import_prioritize_angle_over_quotes = true # true/false
+
+# Whether to ignore file extension in '#include' and '#import' statements
+# for sorting comparison.
+mod_sort_incl_import_ignore_extension = true # true/false
+
+# Whether to group '#include' and '#import' statements when sorting is enabled.
+mod_sort_incl_import_grouping_enabled = false # true/false
+
+# Whether to move a 'break' that appears after a fully braced 'case' before
+# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'.
+mod_move_case_break = true # true/false
+
+# Whether to move a 'return' that appears after a fully braced 'case' before
+# the close brace, as in 'case X: { ... } return;' => 'case X: { ... return; }'.
+mod_move_case_return = true # true/false
+
+# Add or remove braces around a fully braced case statement. Will only remove
+# braces if there are no variable declarations in the block.
+mod_case_brace = remove # ignore/add/remove/force
+
+# Whether to remove a void 'return;' that appears as the last statement in a
+# function.
+mod_remove_empty_return = true # true/false
+
+# Add or remove the comma after the last value of an enumeration.
+mod_enum_last_comma = remove # ignore/add/remove/force
+
+# Syntax to use for infinite loops.
+#
+# 0: Leave syntax alone (default)
+# 1: Rewrite as `for(;;)`
+# 2: Rewrite as `while(true)`
+# 3: Rewrite as `do`...`while(true);`
+# 4: Rewrite as `while(1)`
+# 5: Rewrite as `do`...`while(1);`
+#
+# Infinite loops that do not already match one of these syntaxes are ignored.
+# Other options that affect loop formatting will be applied after transforming
+# the syntax.
+mod_infinite_loop = 0 # unsigned number
+
+# Add or remove the 'int' keyword in 'int short'.
+mod_int_short = remove # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'short int'.
+mod_short_int = remove # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'int long'.
+mod_int_long = remove # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'long int'.
+mod_long_int = remove # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'int signed'.
+mod_int_signed = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'signed int'.
+mod_signed_int = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'int unsigned'.
+mod_int_unsigned = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove the 'int' keyword in 'unsigned int'.
+mod_unsigned_int = ignore # ignore/add/remove/force/not_defined
+
+# If there is a situation where mod_int_* and mod_*_int would result in
+# multiple int keywords, whether to keep the rightmost int (the default) or the
+# leftmost int.
+mod_int_prefer_int_on_left = false # true/false
+
+# (OC) Whether to organize the properties. If true, properties will be
+# rearranged according to the mod_sort_oc_property_*_weight factors.
+mod_sort_oc_properties = false # true/false
+
+# (OC) Weight of a class property modifier.
+mod_sort_oc_property_class_weight = 0 # number
+
+# (OC) Weight of 'atomic' and 'nonatomic'.
+mod_sort_oc_property_thread_safe_weight = 0 # number
+
+# (OC) Weight of 'readwrite' when organizing properties.
+mod_sort_oc_property_readwrite_weight = 0 # number
+
+# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign',
+# 'weak', 'strong') when organizing properties.
+mod_sort_oc_property_reference_weight = 0 # number
+
+# (OC) Weight of getter type ('getter=') when organizing properties.
+mod_sort_oc_property_getter_weight = 0 # number
+
+# (OC) Weight of setter type ('setter=') when organizing properties.
+mod_sort_oc_property_setter_weight = 0 # number
+
+# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified',
+# 'null_resettable') when organizing properties.
+mod_sort_oc_property_nullability_weight = 0 # number
+
+#
+# Preprocessor options
+#
+
+# How to use tabs when indenting preprocessor code.
+#
+# -1: Use 'indent_with_tabs' setting (default)
+# 0: Spaces only
+# 1: Indent with tabs to brace level, align with spaces
+# 2: Indent and align with tabs, using spaces when not on a tabstop
+#
+# Default: -1
+pp_indent_with_tabs = 0 # number
+
+# Add or remove indentation of preprocessor directives inside #if blocks
+# at brace level 0 (file-level).
+pp_indent = ignore # ignore/add/remove/force
+
+# Whether to indent #if/#else/#endif at the brace level. If false, these are
+# indented from column 1.
+pp_indent_at_level = false # true/false
+
+# Whether to indent #if/#else/#endif at the parenthesis level if the brace
+# level is 0. If false, these are indented from column 1.
+pp_indent_at_level0 = false # true/false
+
+# Specifies the number of columns to indent preprocessors per level
+# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies
+# the number of columns to indent preprocessors per level
+# at brace level > 0 (function-level).
+#
+# Default: 1
+pp_indent_count = 0 # unsigned number
+
+# Add or remove space after # based on pp level of #if blocks.
+pp_space_after = ignore # ignore/add/remove/force/not_defined
+
+# Sets the number of spaces per level added with pp_space.
+pp_space_count = 0 # unsigned number
+
+# The indent for '#region' and '#endregion' in C# and '#pragma region' in
+# C/C++. Negative values decrease indent down to the first column.
+pp_indent_region = 0 # number
+
+# Whether to indent the code between #region and #endregion.
+pp_region_indent_code = false # true/false
+
+# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when
+# not at file-level. Negative values decrease indent down to the first column.
+#
+# =0: Indent preprocessors using output_tab_size
+# >0: Column at which all preprocessors will be indented
+pp_indent_if = 0 # number
+
+# Whether to indent the code between #if, #else and #endif.
+pp_if_indent_code = false # true/false
+
+# Whether to indent the body of an #if that encompasses all the code in the file.
+pp_indent_in_guard = false # true/false
+
+# Whether to indent '#define' at the brace level. If false, these are
+# indented from column 1.
+pp_define_at_level = false # true/false
+
+# Whether to indent '#include' at the brace level.
+pp_include_at_level = false # true/false
+
+# Whether to ignore the '#define' body while formatting.
+pp_ignore_define_body = true # true/false
+
+# An offset value that controls the indentation of the body of a multiline #define.
+# 'body' refers to all the lines of a multiline #define except the first line.
+# Requires 'pp_ignore_define_body = false'.
+#
+# <0: Absolute column: the body indentation starts off at the specified column
+# (ex. -3 ==> the body is indented starting from column 3)
+# >=0: Relative to the column of the '#' of '#define'
+# (ex. 3 ==> the body is indented starting 3 columns at the right of '#')
+#
+# Default: 8
+pp_multiline_define_body_indent = 8 # number
+
+# Whether to indent case statements between #if, #else, and #endif.
+# Only applies to the indent of the preprocesser that the case statements
+# directly inside of.
+#
+# Default: true
+pp_indent_case = false # true/false
+
+# Whether to indent whole function definitions between #if, #else, and #endif.
+# Only applies to the indent of the preprocesser that the function definition
+# is directly inside of.
+#
+# Default: true
+pp_indent_func_def = false # true/false
+
+# Whether to indent extern C blocks between #if, #else, and #endif.
+# Only applies to the indent of the preprocesser that the extern block is
+# directly inside of.
+#
+# Default: true
+pp_indent_extern = false # true/false
+
+# How to indent braces directly inside #if, #else, and #endif.
+# Requires pp_if_indent_code=true and only applies to the indent of the
+# preprocesser that the braces are directly inside of.
+# 0: No extra indent
+# 1: Indent by one level
+# -1: Preserve original indentation
+#
+# Default: 1
+pp_indent_brace = 0 # number
+
+# Whether to print warning messages for unbalanced #if and #else blocks.
+# This will print a message in the following cases:
+# - if an #ifdef block ends on a different indent level than
+# where it started from. Example:
+#
+# #ifdef TEST
+# int i;
+# {
+# int j;
+# #endif
+#
+# - an #elif/#else block ends on a different indent level than
+# the corresponding #ifdef block. Example:
+#
+# #ifdef TEST
+# int i;
+# #else
+# }
+# int j;
+# #endif
+pp_warn_unbalanced_if = true # true/false
+
+#
+# Sort includes options
+#
+
+# The regex for include category with priority 0.
+include_category_0 = "" # string
+
+# The regex for include category with priority 1.
+include_category_1 = "" # string
+
+# The regex for include category with priority 2.
+include_category_2 = "" # string
+
+#
+# Use or Do not Use options
+#
+
+# true: indent_func_call_param will be used (default)
+# false: indent_func_call_param will NOT be used
+#
+# Default: true
+use_indent_func_call_param = true # true/false
+
+# The value of the indentation for a continuation line is calculated
+# differently if the statement is:
+# - a declaration: your case with QString fileName ...
+# - an assignment: your case with pSettings = new QSettings( ...
+#
+# At the second case the indentation value might be used twice:
+# - at the assignment
+# - at the function call (if present)
+#
+# To prevent the double use of the indentation value, use this option with the
+# value 'true'.
+#
+# true: indent_continue will be used only once
+# false: indent_continue will be used every time (default)
+use_indent_continue_only_once = true # true/false
+
+# The value might be used twice:
+# - at the assignment
+# - at the opening brace
+#
+# To prevent the double use of the indentation value, use this option with the
+# value 'true'.
+#
+# true: indentation will be used only once
+# false: indentation will be used every time (default)
+indent_cpp_lambda_only_once = true # true/false
+
+# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the
+# historic behavior, but is probably not the desired behavior, so this is off
+# by default.
+use_sp_after_angle_always = false # true/false
+
+# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially,
+# this tries to format these so that they match Qt's normalized form (i.e. the
+# result of QMetaObject::normalizedSignature), which can slightly improve the
+# performance of the QObject::connect call, rather than how they would
+# otherwise be formatted.
+#
+# See options_for_QT.cpp for details.
+#
+# Default: true
+use_options_overriding_for_qt_macros = false # true/false
+
+# If true: the form feed character is removed from the list
+# of whitespace characters.
+# See https://en.cppreference.com/w/cpp/string/byte/isspace
+use_form_feed_no_more_as_whitespace_character = false # true/false
+
+#
+# Warn levels - 1: error, 2: warning (default), 3: note
+#
+
+# (C#) Warning is given if doing tab-to-\t replacement and we have found one
+# in a C# verbatim string literal.
+#
+# Default: 2
+warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number
+
+# Limit the number of loops.
+# Used by uncrustify.cpp to exit from infinite loop.
+# 0: no limit.
+debug_max_number_of_loops = 0 # number
+
+# Set the number of the line to protocol;
+# Used in the function prot_the_line if the 2. parameter is zero.
+# 0: nothing protocol.
+debug_line_number_to_protocol = 0 # number
+
+# Set the number of second(s) before terminating formatting the current file,
+# 0: no timeout.
+# only for linux
+debug_timeout = 0 # number
+
+# Set the number of characters to be printed if the text is too long,
+# 0: do not truncate.
+debug_truncate = 0 # unsigned number
+
+# sort (or not) the tracking info.
+#
+# Default: true
+debug_sort_the_tracks = true # true/false
+
+# decode (or not) the flags as a new line.
+# only if the -p option is set.
+debug_decode_the_flags = false # true/false
+
+# insert the number of the line at the beginning of each line
+set_numbering_for_html_output = false # true/false
+
+# Meaning of the settings:
+# Ignore - do not do any changes
+# Add - makes sure there is 1 or more space/brace/newline/etc
+# Force - makes sure there is exactly 1 space/brace/newline/etc,
+# behaves like Add in some contexts
+# Remove - removes space/brace/newline/etc
+#
+#
+# - Token(s) can be treated as specific type(s) with the 'set' option:
+# `set tokenType tokenString [tokenString...]`
+#
+# Example:
+# `set BOOL __AND__ __OR__`
+#
+# tokenTypes are defined in src/token_enum.h, use them without the
+# 'CT_' prefix: 'CT_BOOL' => 'BOOL'
+#
+#
+# - Token(s) can be treated as type(s) with the 'type' option.
+# `type tokenString [tokenString...]`
+#
+# Example:
+# `type int c_uint_8 Rectangle`
+#
+# This can also be achieved with `set TYPE int c_uint_8 Rectangle`
+#
+#
+# To embed whitespace in tokenStrings use the '\' escape character, or quote
+# the tokenStrings. These quotes are supported: "'`
+#
+#
+# - Support for the auto detection of languages through the file ending can be
+# added using the 'file_ext' command.
+# `file_ext langType langString [langString..]`
+#
+# Example:
+# `file_ext CPP .ch .cxx .cpp.in`
+#
+# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use
+# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP'
+#
+#
+# - Custom macro-based indentation can be set up using 'macro-open',
+# 'macro-else' and 'macro-close'.
+# `(macro-open | macro-else | macro-close) tokenString`
+#
+# Example:
+# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP`
+# `macro-open BEGIN_MESSAGE_MAP`
+# `macro-close END_MESSAGE_MAP`
+#
+#
+# option(s) with 'not default' value: 232
+#