yanglint FEATURE schema mount support (#1901)

* modify existing context if provided

In ly_ctx_new_yldata(), check if *ctx is NULL.  If so, proceed as before
with allocating a new context.  Otherwise, load modules into the existing
context.

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

* add parent-reference xpath helper to schema-mount plugin

This new function produces a list of schema nodes from the expanded
parent-reference xpath expressions.

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

* add context lookup to schema-mount plugin

This function allocates a new context for a particular instance of the
yangmnt:mount-point extension.

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

* yanglint: schema-mount extension callback

Add a commandline argument "-x" that accepts a file containing extension
data, used to create a context for the schema-mount extension.  This file
has the same format as what is provided to the "-Y" option.  A callback
function for the schema-mount extenion is registered if the new option
is specified.

This allows validating instance data for models that include a schema
mount point.

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

* yanglint: print mounted schema trees

Display "mp" flag for mount point and print mounted tree.  Also display
the / and @ opts in node names.

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

* add unit test for tree mount-point flag

Test is courtesy of aPiecek <piecek@cesnet.cz>.

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

* add example data files for validation with schema-mount

From tools/lint/examples directory:

% ../../../build/yanglint \
    -f json -t config -p ../../../models -p . \
    -Y ./sm-context-main.xml -x ./sm-context-extension.xml sm-data.xml

% ../../../build/yanglint \
    -f tree -p ../../../models -p . \
    -Y ./sm-context-main.xml -x ./sm-context-extension.xml sm-main.yang

Signed-off-by: Eric Kinzie <ekinzie@labn.net>

Signed-off-by: Eric Kinzie <ekinzie@labn.net>
Co-authored-by: Eric Kinzie <ekinzie@labn.net>
diff --git a/tests/utests/schema/test_printer_tree.c b/tests/utests/schema/test_printer_tree.c
index 5627702..850216a 100644
--- a/tests/utests/schema/test_printer_tree.c
+++ b/tests/utests/schema/test_printer_tree.c
@@ -1841,6 +1841,54 @@
     TEST_LOCAL_TEARDOWN;
 }
 
+static void
+mount_point(void **state)
+{
+    TEST_LOCAL_SETUP;
+
+    orig =
+        "module a29 {\n"
+        "  yang-version 1.1;\n"
+        "  namespace \"x:y\";\n"
+        "  prefix x;\n"
+        "  import ietf-yang-schema-mount {\n"
+             "prefix yangmnt;\n"
+        "  }\n"
+        "  list my-list {\n"
+        "    key name;\n"
+        "    leaf name {\n"
+        "      type string;\n"
+        "    }\n"
+        "    yangmnt:mount-point \"mnt-root\";\n"
+        "  }\n"
+        "  container my-cont {\n"
+        "    yangmnt:mount-point \"mnt-root\";\n"
+        "  }\n"
+        "}\n";
+
+    expect =
+        "module: a29\n"
+        "  +--mp my-list* [name]\n"
+        "  |  +--rw name    string\n"
+        "  +--mp my-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);
+
+    /* 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;
+}
+
 int
 main(void)
 {
@@ -1873,6 +1921,7 @@
         UTEST(print_compiled_node),
         UTEST(print_parsed_submodule),
         UTEST(yang_data),
+        UTEST(mount_point),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);