yanglint FEATURE extdata cmd for interactive mode
Implementation of --ext-data parameter for interactive yanglint mode.
diff --git a/tools/lint/CMakeLists.txt b/tools/lint/CMakeLists.txt
index 20edd97..0fd2fad 100644
--- a/tools/lint/CMakeLists.txt
+++ b/tools/lint/CMakeLists.txt
@@ -17,6 +17,7 @@
cmd_load.c
cmd_print.c
cmd_searchpath.c
+ cmd_extdata.c
common.c
)
if(YANGLINT_INTERACTIVE)
diff --git a/tools/lint/cmd.c b/tools/lint/cmd.c
index 93994c7..885b689 100644
--- a/tools/lint/cmd.c
+++ b/tools/lint/cmd.c
@@ -90,6 +90,18 @@
#endif
void
+cmd_free(void)
+{
+ uint16_t i;
+
+ for (i = 0; commands[i].name; i++) {
+ if (commands[i].free_func) {
+ commands[i].free_func();
+ }
+ }
+}
+
+void
cmd_verb_help(void)
{
printf("Usage: verb (error | warning | verbose | debug)\n");
@@ -239,22 +251,23 @@
/* Also keep enum COMMAND_INDEX updated. */
COMMAND commands[] = {
- {"help", cmd_help, cmd_help_help, "Display commands description", "h"},
- {"add", cmd_add, cmd_add_help, "Add a new module from a specific file", "DF:hi"},
- {"load", cmd_load, cmd_load_help, "Load a new schema from the searchdirs", "F:hi"},
- {"print", cmd_print, cmd_print_help, "Print a module", "f:hL:o:P:q"},
- {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data", "d:ef:F:hmo:O:R:r:nt:x:"},
- {"list", cmd_list, cmd_list_help, "List all the loaded modules", "f:h"},
- {"feature", cmd_feature, cmd_feature_help, "Print all features of module(s) with their state", "haf"},
- {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for schemas", "ch"},
- {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded modules", "iyh"},
- {"verb", cmd_verb, cmd_verb_help, "Change verbosity", "h"},
+ {"help", cmd_help, cmd_help_help, NULL, "Display commands description", "h"},
+ {"add", cmd_add, cmd_add_help, NULL, "Add a new module from a specific file", "DF:hi"},
+ {"load", cmd_load, cmd_load_help, NULL, "Load a new schema from the searchdirs", "F:hi"},
+ {"print", cmd_print, cmd_print_help, NULL, "Print a module", "f:hL:o:P:q"},
+ {"data", cmd_data, cmd_data_help, NULL, "Load, validate and optionally print instance data", "d:ef:F:hmo:O:R:r:nt:x:"},
+ {"list", cmd_list, cmd_list_help, NULL, "List all the loaded modules", "f:h"},
+ {"feature", cmd_feature, cmd_feature_help, NULL, "Print all features of module(s) with their state", "haf"},
+ {"searchpath", cmd_searchpath, cmd_searchpath_help, NULL, "Print/set the search path(s) for schemas", "ch"},
+ {"extdata", cmd_extdata, cmd_extdata_help, cmd_extdata_free, "Set the specific data required by an extension", "ch"},
+ {"clear", cmd_clear, cmd_clear_help, NULL, "Clear the context - remove all the loaded modules", "iyh"},
+ {"verb", cmd_verb, cmd_verb_help, NULL, "Change verbosity", "h"},
#ifndef NDEBUG
- {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups", "h"},
+ {"debug", cmd_debug, cmd_debug_help, NULL, "Display specific debug message groups", "h"},
#endif
- {"quit", cmd_quit, NULL, "Quit the program", "h"},
+ {"quit", cmd_quit, NULL, NULL, "Quit the program", "h"},
/* synonyms for previous commands */
- {"?", cmd_help, NULL, "Display commands description", "h"},
- {"exit", cmd_quit, NULL, "Quit the program", "h"},
- {NULL, NULL, NULL, NULL, NULL}
+ {"?", cmd_help, NULL, NULL, "Display commands description", "h"},
+ {"exit", cmd_quit, NULL, NULL, "Quit the program", "h"},
+ {NULL, NULL, NULL, NULL, NULL, NULL}
};
diff --git a/tools/lint/cmd.h b/tools/lint/cmd.h
index 071348f..5898cfe 100644
--- a/tools/lint/cmd.h
+++ b/tools/lint/cmd.h
@@ -26,6 +26,7 @@
void (*func)(struct ly_ctx **ctx, const char *); /* Function to call to do the command. */
void (*help_func)(void); /* Display command help. */
+ void (*free_func)(void); /* Freeing global variables allocated by the command. */
char *helpstring; /* Documentation for this function. */
char *optstring; /* Option characters used in function getopt_long. */
} COMMAND;
@@ -47,6 +48,7 @@
CMD_LIST,
CMD_FEATURE,
CMD_SEARCHPATH,
+ CMD_EXTDATA,
CMD_CLEAR,
CMD_VERB,
#ifndef NDEBUG
@@ -54,6 +56,11 @@
#endif
};
+/**
+ * @brief For each cmd, call the COMMAND.free_func in the variable 'commands'.
+ */
+void cmd_free(void);
+
/* cmd_add.c */
void cmd_add(struct ly_ctx **ctx, const char *cmdline);
void cmd_add_help(void);
@@ -86,4 +93,9 @@
void cmd_searchpath(struct ly_ctx **ctx, const char *cmdline);
void cmd_searchpath_help(void);
+/* cmd_extdata.c */
+void cmd_extdata(struct ly_ctx **ctx, const char *cmdline);
+void cmd_extdata_help(void);
+void cmd_extdata_free(void);
+
#endif /* COMMANDS_H_ */
diff --git a/tools/lint/cmd_clear.c b/tools/lint/cmd_clear.c
index cbc5e10..44b5307 100644
--- a/tools/lint/cmd_clear.c
+++ b/tools/lint/cmd_clear.c
@@ -91,6 +91,9 @@
goto cleanup;
}
+ /* Global variables in commands are also deleted. */
+ cmd_free();
+
ly_ctx_destroy(*ctx);
*ctx = ctx_new;
diff --git a/tools/lint/cmd_extdata.c b/tools/lint/cmd_extdata.c
new file mode 100644
index 0000000..a3b1cfd
--- /dev/null
+++ b/tools/lint/cmd_extdata.c
@@ -0,0 +1,101 @@
+/**
+ * @file cmd_extdata.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'extdata' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+
+char *filename;
+
+void
+cmd_extdata_free(void)
+{
+ free(filename);
+ filename = NULL;
+}
+
+void
+cmd_extdata_help(void)
+{
+ printf("Usage: extdata [--clear] [<extdata-file-path>]\n"
+ " File containing the specific data required by an extension. Required by\n"
+ " the schema-mount extension, for example, when the operational data are\n"
+ " expected in the file. File format is guessed.\n");
+}
+
+void
+cmd_extdata(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;
+ int8_t file_count = 0;
+
+ if (parse_cmdline(cmdline, &argc, &argv)) {
+ goto cleanup;
+ }
+
+ while ((opt = getopt_long(argc, argv, commands[CMD_EXTDATA].optstring, options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ ly_ctx_set_ext_data_clb(*ctx, NULL, NULL);
+ free(filename);
+ filename = NULL;
+ cleared = 1;
+ break;
+ case 'h':
+ cmd_extdata_help();
+ goto cleanup;
+ default:
+ YLMSG_E("Unknown option.\n");
+ goto cleanup;
+ }
+ }
+
+ file_count = argc - 1 - cleared;
+
+ if (!cleared && (file_count == 0)) {
+ /* no argument - print the current file */
+ printf("%s\n", filename ? filename : "No file set.");
+ } else if (file_count == 1) {
+ /* set callback providing run-time extension instance data */
+ free(filename);
+ filename = strdup(argv[optind]);
+ if (!filename) {
+ YLMSG_E("Memory allocation error.\n");
+ goto cleanup;
+ }
+ ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, filename);
+ } else if (!cleared) {
+ YLMSG_E("Only one file must be entered.\n");
+ }
+
+cleanup:
+ free_cmdline(argv);
+}
diff --git a/tools/lint/common.c b/tools/lint/common.c
index 1ba4a1d..f2ff464 100644
--- a/tools/lint/common.c
+++ b/tools/lint/common.c
@@ -27,6 +27,7 @@
#include "compat.h"
#include "libyang.h"
+#include "plugins_exts.h"
int
parse_schema_path(const char *path, char **dir, char **module)
@@ -937,3 +938,19 @@
free(module_name);
return parent_node;
}
+
+LY_ERR
+ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *data = NULL;
+
+ ctx = ext->module->ctx;
+ if (user_data) {
+ lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
diff --git a/tools/lint/common.h b/tools/lint/common.h
index 6f117bc..7e98691 100644
--- a/tools/lint/common.h
+++ b/tools/lint/common.h
@@ -276,4 +276,15 @@
*/
const struct lysc_node *find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
+/**
+ * @brief General callback providing run-time extension instance data.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] ext_data Provided extension instance data.
+ * @param[out] ext_data_free Whether the extension instance should free @p ext_data or not.
+ * @return LY_ERR value.
+ */
+LY_ERR ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free);
+
#endif /* COMMON_H_ */
diff --git a/tools/lint/completion.c b/tools/lint/completion.c
index 2eab01c..db2704d 100644
--- a/tools/lint/completion.c
+++ b/tools/lint/completion.c
@@ -413,6 +413,7 @@
{CMD_PRINT, "-o", linenoisePathCompletion, NULL},
{CMD_PRINT, NULL, NULL, get_model_completion},
{CMD_SEARCHPATH, NULL, linenoisePathCompletion, NULL},
+ {CMD_EXTDATA, NULL, linenoisePathCompletion, NULL},
{CMD_DATA, "-t", NULL, get_data_type_arg},
{CMD_DATA, "-O", linenoisePathCompletion, NULL},
{CMD_DATA, "-R", linenoisePathCompletion, NULL},
diff --git a/tools/lint/main.c b/tools/lint/main.c
index 9f0d027..b3d985a 100644
--- a/tools/lint/main.c
+++ b/tools/lint/main.c
@@ -95,6 +95,9 @@
free(cmdline);
}
+ /* Global variables in commands are freed. */
+ cmd_free();
+
store_config();
ly_ctx_destroy(ctx);
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 184ab57..2c0ae12 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -25,7 +25,6 @@
#include <sys/stat.h>
#include "libyang.h"
-#include "plugins_exts.h"
#include "common.h"
#include "out.h"
@@ -363,22 +362,6 @@
}
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;
diff --git a/tools/lint/tests/interactive/extdata.test b/tools/lint/tests/interactive/extdata.test
new file mode 100644
index 0000000..bef06a7
--- /dev/null
+++ b/tools/lint/tests/interactive/extdata.test
@@ -0,0 +1,45 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+set ddir "$::env(TESTS_DIR)/data"
+
+test extdata_set_clear {Set and clear extdata file} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "extdata" "No file set"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "extdata" "$ddir/modsm_ctx_ext.xml"
+ ly_cmd "extdata -c"
+ ly_cmd "extdata" "No file set"
+}}
+
+test extdata_clear_cmd {Clear extdata file by 'clear' command} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "clear"
+ ly_cmd "extdata" "No file set"
+}}
+
+test extdata_one_only {Only one file for extdata} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd_err "extdata $ddir/modsm_ctx_ext.xml $ddir/modsm_ctx_ext.xml" "Only one file must be entered"
+}}
+
+test extdata_schema_mount_tree {Print tree output of a model with Schema Mount} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd "searchpath $mdir"
+ ly_cmd "load modsm"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "print -f tree modsm" "--mp root.*--rw lfl/"
+}}
+
+test ext_data_schema_mount_xml {Validating and printing mounted data} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd "searchpath $mdir"
+ ly_cmd "load modsm"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "data -f xml -t config $ddir/modsm.xml" "</lfl>"
+}}
+
+cleanupTests