summaryrefslogtreecommitdiffstats
path: root/tools/lint
diff options
context:
space:
mode:
Diffstat (limited to 'tools/lint')
-rw-r--r--tools/lint/CMakeLists.txt48
-rw-r--r--tools/lint/cmd.c275
-rw-r--r--tools/lint/cmd.h353
-rw-r--r--tools/lint/cmd_add.c186
-rw-r--r--tools/lint/cmd_clear.c139
-rw-r--r--tools/lint/cmd_data.c694
-rw-r--r--tools/lint/cmd_debug.c134
-rw-r--r--tools/lint/cmd_extdata.c115
-rw-r--r--tools/lint/cmd_feature.c122
-rw-r--r--tools/lint/cmd_help.c107
-rw-r--r--tools/lint/cmd_list.c145
-rw-r--r--tools/lint/cmd_load.c129
-rw-r--r--tools/lint/cmd_print.c339
-rw-r--r--tools/lint/cmd_searchpath.c64
-rw-r--r--tools/lint/cmd_verb.c114
-rw-r--r--tools/lint/common.c775
-rw-r--r--tools/lint/common.h199
-rw-r--r--tools/lint/completion.c208
-rw-r--r--tools/lint/configuration.c20
-rw-r--r--tools/lint/examples/README.md75
-rw-r--r--tools/lint/main.c52
-rw-r--r--tools/lint/main_ni.c790
-rw-r--r--tools/lint/tests/expect/common.exp68
-rwxr-xr-xtools/lint/tests/expect/completion.exp60
-rwxr-xr-xtools/lint/tests/expect/feature.exp20
-rwxr-xr-xtools/lint/tests/expect/list.exp20
-rwxr-xr-xtools/lint/tests/shunit2/feature.sh19
-rwxr-xr-xtools/lint/tests/shunit2/list.sh28
-rw-r--r--tools/lint/yl_opt.c344
-rw-r--r--tools/lint/yl_opt.h237
-rw-r--r--tools/lint/yl_schema_features.c212
-rw-r--r--tools/lint/yl_schema_features.h84
32 files changed, 3693 insertions, 2482 deletions
diff --git a/tools/lint/CMakeLists.txt b/tools/lint/CMakeLists.txt
index 32cdcbf..14f8b76 100644
--- a/tools/lint/CMakeLists.txt
+++ b/tools/lint/CMakeLists.txt
@@ -17,6 +17,12 @@ set(lintsrc
cmd_load.c
cmd_print.c
cmd_searchpath.c
+ cmd_extdata.c
+ cmd_help.c
+ cmd_verb.c
+ cmd_debug.c
+ yl_opt.c
+ yl_schema_features.c
common.c
)
if(YANGLINT_INTERACTIVE)
@@ -46,45 +52,3 @@ if(WIN32)
target_include_directories(yanglint PRIVATE ${GETOPT_INCLUDE_DIR})
target_link_libraries(yanglint ${GETOPT_LIBRARY})
endif()
-
-#
-# tests
-#
-function(add_yanglint_test)
- cmake_parse_arguments(ADDTEST "" "NAME;VIA;SCRIPT" "" ${ARGN})
- set(TEST_NAME yanglint_${ADDTEST_NAME})
-
- if(${ADDTEST_VIA} STREQUAL "bash")
- set(WRAPPER /usr/bin/env bash)
- elseif(${ADDTEST_VIA} STREQUAL "expect")
- set(WRAPPER ${PATH_EXPECT})
- else()
- message(FATAL_ERROR "build: unexpected wrapper '${ADDTEST_VIA}'")
- endif()
-
- add_test(NAME ${TEST_NAME} COMMAND ${WRAPPER} ${CMAKE_CURRENT_SOURCE_DIR}/tests/${ADDTEST_SCRIPT})
- set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANGLINT=${PROJECT_BINARY_DIR}/yanglint")
- set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANG_MODULES_DIR=${PROJECT_SOURCE_DIR}/tests/modules/yang")
- set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
-endfunction(add_yanglint_test)
-
-if(ENABLE_TESTS)
- # tests of non-interactive mode using shunit2
- find_program(PATH_SHUNIT NAMES shunit2)
- if(NOT PATH_SHUNIT)
- message(WARNING "'shunit2' not found! The yanglint(1) non-interactive tests will not be available.")
- else()
- add_yanglint_test(NAME ni_list VIA bash SCRIPT shunit2/list.sh)
- add_yanglint_test(NAME ni_feature VIA bash SCRIPT shunit2/feature.sh)
- endif()
-
- # tests of interactive mode using expect
- find_program(PATH_EXPECT NAMES expect)
- if(NOT PATH_EXPECT)
- message(WARNING "'expect' not found! The yanglint(1) interactive tests will not be available.")
- elseif(YANGLINT_INTERACTIVE)
- add_yanglint_test(NAME in_list VIA expect SCRIPT expect/list.exp)
- add_yanglint_test(NAME in_feature VIA expect SCRIPT expect/feature.exp)
- add_yanglint_test(NAME in_completion VIA expect SCRIPT expect/completion.exp)
- endif()
-endif()
diff --git a/tools/lint/cmd.c b/tools/lint/cmd.c
index 10e7446..344900d 100644
--- a/tools/lint/cmd.c
+++ b/tools/lint/cmd.c
@@ -2,9 +2,10 @@
* @file cmd.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool general commands
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -30,230 +31,80 @@
COMMAND commands[];
extern int done;
-#ifndef NDEBUG
-
-void
-cmd_debug_help(void)
-{
- printf("Usage: debug (dict | xpath)+\n");
-}
-
void
-cmd_debug(struct ly_ctx **UNUSED(ctx), const char *cmdline)
+cmd_free(void)
{
- int argc = 0;
- char **argv = NULL;
- int opt, opt_index;
- struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
- uint32_t dbg_groups = 0;
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
- }
+ uint16_t i;
- while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
- switch (opt) {
- case 'h':
- cmd_debug_help();
- goto cleanup;
- default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ for (i = 0; commands[i].name; i++) {
+ if (commands[i].free_func) {
+ commands[i].free_func();
}
}
- if (argc == optind) {
- /* no argument */
- cmd_debug_help();
- goto cleanup;
- }
-
- for (int i = 0; i < argc - optind; i++) {
- if (!strcasecmp("dict", argv[optind + i])) {
- dbg_groups |= LY_LDGDICT;
- } else if (!strcasecmp("xpath", argv[optind + i])) {
- dbg_groups |= LY_LDGXPATH;
- } else {
- YLMSG_E("Unknown debug group \"%s\"\n", argv[optind + 1]);
- goto cleanup;
- }
- }
-
- ly_log_dbg_groups(dbg_groups);
-
-cleanup:
- free_cmdline(argv);
}
-#endif
-
-void
-cmd_verb_help(void)
-{
- printf("Usage: verb (error | warning | verbose | debug)\n");
-}
-
-void
-cmd_verb(struct ly_ctx **UNUSED(ctx), const char *cmdline)
-{
- int argc = 0;
- char **argv = NULL;
- int opt, opt_index;
- struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
- }
-
- while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
- switch (opt) {
- case 'h':
- cmd_verb_help();
- goto cleanup;
- default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
- }
- }
-
- if (argc - optind > 1) {
- YLMSG_E("Only a single verbosity level can be set.\n");
- cmd_verb_help();
- goto cleanup;
- } else if (argc == optind) {
- /* no argument - print current value */
- LY_LOG_LEVEL level = ly_log_level(LY_LLERR);
-
- ly_log_level(level);
- printf("Current verbosity level: ");
- if (level == LY_LLERR) {
- printf("error\n");
- } else if (level == LY_LLWRN) {
- printf("warning\n");
- } else if (level == LY_LLVRB) {
- printf("verbose\n");
- } else if (level == LY_LLDBG) {
- printf("debug\n");
- }
- goto cleanup;
- }
-
- if (!strcasecmp("error", argv[optind]) || !strcmp("0", argv[optind])) {
- ly_log_level(LY_LLERR);
- } else if (!strcasecmp("warning", argv[optind]) || !strcmp("1", argv[optind])) {
- ly_log_level(LY_LLWRN);
- } else if (!strcasecmp("verbose", argv[optind]) || !strcmp("2", argv[optind])) {
- ly_log_level(LY_LLVRB);
- } else if (!strcasecmp("debug", argv[optind]) || !strcmp("3", argv[optind])) {
- ly_log_level(LY_LLDBG);
- } else {
- YLMSG_E("Unknown verbosity \"%s\"\n", argv[optind]);
- goto cleanup;
- }
-
-cleanup:
- free_cmdline(argv);
-}
-
-void
-cmd_quit(struct ly_ctx **UNUSED(ctx), const char *UNUSED(cmdline))
+int
+cmd_quit_exec(struct ly_ctx **UNUSED(ctx), struct yl_opt *UNUSED(yo), const char *UNUSED(posv))
{
done = 1;
- return;
-}
-
-void
-cmd_help_help(void)
-{
- printf("Usage: help [cmd ...]\n");
-}
-
-void
-cmd_help(struct ly_ctx **UNUSED(ctx), const char *cmdline)
-{
- int argc = 0;
- char **argv = NULL;
- int opt, opt_index;
- struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
- }
-
- while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
- switch (opt) {
- case 'h':
- cmd_help_help();
- goto cleanup;
- default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
- }
- }
-
- if (argc == optind) {
-generic_help:
- printf("Available commands:\n");
- for (uint16_t i = 0; commands[i].name; i++) {
- if (commands[i].helpstring != NULL) {
- printf(" %-15s %s\n", commands[i].name, commands[i].helpstring);
- }
- }
- } else {
- /* print specific help for the selected command(s) */
-
- for (int c = 0; c < argc - optind; ++c) {
- int8_t match = 0;
-
- /* get the command of the specified name */
- for (uint16_t i = 0; commands[i].name; i++) {
- if (strcmp(argv[optind + c], commands[i].name) == 0) {
- match = 1;
- if (commands[i].help_func != NULL) {
- commands[i].help_func();
- } else {
- printf("%s: %s\n", argv[optind + c], commands[i].helpstring);
- }
- break;
- }
- }
- if (!match) {
- /* if unknown command specified, print the list of commands */
- printf("Unknown command \'%s\'\n", argv[optind + c]);
- goto generic_help;
- }
- }
- }
-
-cleanup:
- free_cmdline(argv);
+ return 0;
}
+/* Also keep enum COMMAND_INDEX updated. */
COMMAND commands[] = {
- {"help", cmd_help, cmd_help_help, "Display commands description"},
- {"add", cmd_add, cmd_add_help, "Add a new module from a specific file"},
- {"load", cmd_load, cmd_load_help, "Load a new schema from the searchdirs"},
- {"print", cmd_print, cmd_print_help, "Print a module"},
- {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
- {"list", cmd_list, cmd_list_help, "List all the loaded modules"},
- {"feature", cmd_feature, cmd_feature_help, "Print all features of module(s) with their state"},
- {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for schemas"},
- {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded modules"},
- {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
+ {
+ "help", cmd_help_opt, NULL, cmd_help_exec, NULL, cmd_help_help, NULL,
+ "Display commands description", "h"
+ },
+ {
+ "add", cmd_add_opt, cmd_add_dep, cmd_add_exec, NULL, cmd_add_help, NULL,
+ "Add a new module from a specific file", "DF:hiX"
+ },
+ {
+ "load", cmd_load_opt, cmd_load_dep, cmd_load_exec, NULL, cmd_load_help, NULL,
+ "Load a new schema from the searchdirs", "F:hiX"
+ },
+ {
+ "print", cmd_print_opt, cmd_print_dep, cmd_print_exec, NULL, cmd_print_help, NULL,
+ "Print a module", "f:hL:o:P:q"
+ },
+ {
+ "data", cmd_data_opt, cmd_data_dep, cmd_data_store, cmd_data_process, cmd_data_help, NULL,
+ "Load, validate and optionally print instance data", "d:ef:F:hmo:O:R:r:nt:x:"
+ },
+ {
+ "list", cmd_list_opt, cmd_list_dep, cmd_list_exec, NULL, cmd_list_help, NULL,
+ "List all the loaded modules", "f:h"
+ },
+ {
+ "feature", cmd_feature_opt, cmd_feature_dep, cmd_feature_exec, cmd_feature_fin, cmd_feature_help, NULL,
+ "Print all features of module(s) with their state", "haf"
+ },
+ {
+ "searchpath", cmd_searchpath_opt, NULL, cmd_searchpath_exec, NULL, cmd_searchpath_help, NULL,
+ "Print/set the search path(s) for schemas", "ch"
+ },
+ {
+ "extdata", cmd_extdata_opt, cmd_extdata_dep, cmd_extdata_exec, NULL, cmd_extdata_help, cmd_extdata_free,
+ "Set the specific data required by an extension", "ch"
+ },
+ {
+ "clear", cmd_clear_opt, cmd_clear_dep, cmd_clear_exec, NULL, cmd_clear_help, NULL,
+ "Clear the context - remove all the loaded modules", "iyhY:"
+ },
+ {
+ "verb", cmd_verb_opt, cmd_verb_dep, cmd_verb_exec, NULL, cmd_verb_help, NULL,
+ "Change verbosity", "h"
+ },
#ifndef NDEBUG
- {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
+ {
+ "debug", cmd_debug_opt, cmd_debug_dep, cmd_debug_store, cmd_debug_setlog, cmd_debug_help, NULL,
+ "Display specific debug message groups", "h"
+ },
#endif
- {"quit", cmd_quit, NULL, "Quit the program"},
+ {"quit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
/* synonyms for previous commands */
- {"?", cmd_help, NULL, "Display commands description"},
- {"exit", cmd_quit, NULL, "Quit the program"},
- {NULL, NULL, NULL, NULL}
+ {"?", NULL, NULL, cmd_help_exec, NULL, NULL, NULL, "Display commands description", "h"},
+ {"exit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};
diff --git a/tools/lint/cmd.h b/tools/lint/cmd.h
index 9f6f88d..bd2f2f2 100644
--- a/tools/lint/cmd.h
+++ b/tools/lint/cmd.h
@@ -2,9 +2,10 @@
* @file cmd.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool commands header
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -18,15 +19,44 @@
#include "libyang.h"
+struct yl_opt;
+
/**
* @brief command information
+ *
+ * Callback functions are in the order they should be called.
+ * First, the 'opt_func' should be called, which parses arguments from the command line and sets flags or pointers in
+ * the struct yl_opt. This type of function is for interactive mode and is optional.
+ * Then the 'dep_func' callback can check the struct yl_opt settings. Other items that depend on them can also be
+ * set. There is also an possibility for controlling the number of positional arguments and its implications.
+ * The most important callback is 'exec_func' where the command itself is executed. This function can even replace the
+ * entire libyang context. The function parameters are mainly found in the yl_opt structure. Optionally, the function
+ * can be called with a positional argument obtained from the command line. Some 'exec_func' are adapted to be called
+ * from non-interactive yanglint mode.
+ * The 'fun_func' complements the 'exec_func'. In some cases, the command execution must be divided into two stages.
+ * For example, the 'exec_func' is used to fill some items in the yl_opt structure from the positional
+ * arguments and then the 'fin_func' is used to perform the final action.
*/
typedef struct {
- char *name; /* User printable name of the function. */
+ /* User printable name of the function. */
+ char *name;
- void (*func)(struct ly_ctx **ctx, const char *); /* Function to call to do the command. */
- void (*help_func)(void); /* Display command help. */
- char *helpstring; /* Documentation for this function. */
+ /* Convert command line options to the data struct yl_opt. */
+ int (*opt_func)(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+ /* Additionally set dependent items and perform error checking. */
+ int (*dep_func)(struct yl_opt *yo, int posc);
+ /* Execute command. */
+ int (*exec_func)(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+ /* Finish execution of command. */
+ int (*fin_func)(struct ly_ctx *ctx, struct yl_opt *yo);
+ /* Display command help. */
+ void (*help_func)(void);
+ /* Freeing global variables allocated by the command. */
+ void (*free_func)(void);
+ /* Documentation for this function. */
+ char *helpstring;
+ /* Option characters used in function getopt_long. */
+ char *optstring;
} COMMAND;
/**
@@ -34,36 +64,333 @@ typedef struct {
*/
extern COMMAND commands[];
+/**
+ * @brief Index for global variable ::commands.
+ */
+enum COMMAND_INDEX {
+ CMD_HELP = 0,
+ CMD_ADD,
+ CMD_LOAD,
+ CMD_PRINT,
+ CMD_DATA,
+ CMD_LIST,
+ CMD_FEATURE,
+ CMD_SEARCHPATH,
+ CMD_EXTDATA,
+ CMD_CLEAR,
+ CMD_VERB,
+#ifndef NDEBUG
+ CMD_DEBUG,
+#endif
+};
+
+/**
+ * @brief For each cmd, call the COMMAND.free_func in the variable 'commands'.
+ */
+void cmd_free(void);
+
/* cmd_add.c */
-void cmd_add(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @brief Parse the arguments of an interactive command.
+ *
+ * @param[out] yo Context for yanglint.
+ * @param[in] cmdline String containing command line arguments.
+ * @param[out] posv Pointer to argv to a section of positional arguments.
+ * @param[out] posc Number of positional arguments.
+ * @return 0 on success.
+ */
+int cmd_add_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @brief Check the options and set dependent items in @p yo.
+ *
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posc number of positional arguments.
+ * @return 0 on success.
+ */
+int cmd_add_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Parse and compile a new module using filepath.
+ *
+ * @param[in,out] ctx Context for libyang.
+ * @param[in,out] yo Context for yanglint.
+ * @param[in] posv Path to the file where the new module is located.
+ * @return 0 on success.
+ */
+int cmd_add_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_add_help(void);
/* cmd_clear.c */
-void cmd_clear(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_clear_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Clear libyang context.
+ *
+ * @param[in,out] ctx context for libyang that will be replaced with an empty one.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv not used.
+ * @return 0 on success.
+ */
+int cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_clear_help(void);
/* cmd_data.c */
-void cmd_data(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_data_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Store data file for later processing.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Path to the file where the data is located.
+ * @return 0 on success.
+ */
+int cmd_data_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+
+/**
+ * @brief Parse, validate and optionally print data instances.
+ *
+ * @param[in] ctx Context for libyang.
+ * @param[in] yo Context of yanglint. All necessary parameters should already be set.
+ * @return 0 on success.
+ */
+int cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_data_help(void);
/* cmd_list.c */
-void cmd_list(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_list_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_list_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Print the list of modules in the current context.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Not used.
+ * @return 0 on success.
+ */
+int cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_list_help(void);
/* cmd_feature.c */
-void cmd_feature(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_feature_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_feature_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Print the features the modules.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the module which features are to be printed.
+ * @return 0 on success.
+ */
+int cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+
+/**
+ * @brief Printing of features ends.
+ *
+ * @param[in] ctx context for libyang. Not used.
+ * @param[in] yo context for yanglint.
+ * @return 0 on success.
+ */
+int cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_feature_help(void);
/* cmd_load.c */
-void cmd_load(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_load_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_load_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Parse and compile a new module using module name.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the module to be loaded into the context.
+ * @return 0 on success.
+ */
+int cmd_load_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_load_help(void);
/* cmd_print.c */
-void cmd_print(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_print_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Print a schema module.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the module to be printed. Can be NULL in the case of printing a node.
+ * @return 0 on success.
+ */
+int cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_print_help(void);
/* cmd_searchpath.c */
-void cmd_searchpath(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_searchpath_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @brief Set the paths of directories where to search schema modules.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Path to the directory. Can be NULL in the case of printing a current searchdirs.
+ * @return 0 on success.
+ */
+int cmd_searchpath_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_searchpath_help(void);
+/* cmd_extdata.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_extdata_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_extdata_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Set path to the file required by the extension.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Path to the directory. Can be NULL in the case of printing a current path.
+ * @return 0 on success.
+ */
+int cmd_extdata_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+void cmd_extdata_help(void);
+void cmd_extdata_free(void);
+
+/* cmd_help.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_help_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @brief Print help.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the command which help message is to be printed. Can be NULL.
+ * @return 0 on success.
+ */
+int cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+void cmd_help_help(void);
+
+/* cmd_verb.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_verb_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_verb_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Set the verbose level.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the verbose level to be set.
+ * @return 0 on success.
+ */
+int cmd_verb_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+void cmd_verb_help(void);
+
+/* cmd_debug.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_debug_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_debug_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Store the type of debug messages for later processing.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the debug type to be set.
+ * @return 0 on success.
+ */
+int cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+
+/**
+ * @brief Set debug logging.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint. All necessary parameters should already be set.
+ * @return 0 on success.
+ */
+int cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo);
+void cmd_debug_help(void);
+
#endif /* COMMANDS_H_ */
diff --git a/tools/lint/cmd_add.c b/tools/lint/cmd_add.c
index bbfdfd6..9f10711 100644
--- a/tools/lint/cmd_add.c
+++ b/tools/lint/cmd_add.c
@@ -2,9 +2,10 @@
* @file cmd_add.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'add' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,8 +18,8 @@
#include "cmd.h"
+#include <assert.h>
#include <getopt.h>
-#include <libgen.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,6 +27,8 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
void
cmd_add_help(void)
@@ -44,133 +47,164 @@ cmd_add_help(void)
" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" <schema> module also implemented. If specified a second time,\n"
- " all the modules are set implemented.\n");
+ " all the modules are set implemented.\n"
+ " -X, --extended-leafref\n"
+ " Allow usage of deref() XPath function within leafref.\n");
}
-void
-cmd_add(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_add_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"disable-searchdir", no_argument, NULL, 'D'},
{"features", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"make-implemented", no_argument, NULL, 'i'},
+ {"extended-leafref", no_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
- uint16_t options_ctx = 0;
- const char *all_features[] = {"*", NULL};
- struct ly_set fset = {0};
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "D:F:hi", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_ADD].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'D': /* --disable--search */
- if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
- YLMSG_W("The -D option specified too many times.\n");
- }
- if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
- options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
- options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
- } else {
- options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
+ YLMSG_W("The -D option specified too many times.");
}
+ yo_opt_update_disable_searchdir(yo);
break;
case 'F': /* --features */
- if (parse_features(optarg, &fset)) {
- goto cleanup;
+ if (parse_features(optarg, &yo->schema_features)) {
+ return 1;
}
break;
case 'h':
cmd_add_help();
- goto cleanup;
+ return 1;
case 'i': /* --make-implemented */
- if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
- options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
- options_ctx |= LY_CTX_ALL_IMPLEMENTED;
- } else {
- options_ctx |= LY_CTX_REF_IMPLEMENTED;
- }
+ yo_opt_update_make_implemented(yo);
+ break;
+
+ case 'X': /* --extended-leafref */
+ yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (argc == optind) {
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_add_dep(struct yl_opt *yo, int posc)
+{
+ if (yo->interactive && !posc) {
/* no argument */
cmd_add_help();
- goto cleanup;
+ return 1;
}
-
- if (!fset.count) {
+ if (!yo->schema_features.count) {
/* no features, enable all of them */
- options_ctx |= LY_CTX_ENABLE_IMP_FEATURES;
+ yo->ctx_options |= LY_CTX_ENABLE_IMP_FEATURES;
}
- if (options_ctx) {
- ly_ctx_set_options(*ctx, options_ctx);
- }
+ return 0;
+}
- for (int i = 0; i < argc - optind; i++) {
- /* process the schema module files */
- LY_ERR ret;
- uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
- char *dir, *module;
- const char **features = NULL;
- struct ly_in *in = NULL;
+static int
+store_parsed_module(const char *filepath, struct lys_module *mod, struct yl_opt *yo)
+{
+ assert(!yo->interactive);
- if (parse_schema_path(argv[optind + i], &dir, &module)) {
- goto cleanup;
+ if (yo->schema_out_format || yo->feature_param_format) {
+ if (ly_set_add(&yo->schema_modules, (void *)mod, 1, NULL)) {
+ YLMSG_E("Storing parsed schema module (%s) for print failed.", filepath);
+ return 1;
}
+ }
- /* add temporarily also the path of the module itself */
- if (ly_ctx_set_searchdir(*ctx, dirname(dir)) == LY_EEXIST) {
- path_unset = 0;
- }
+ return 0;
+}
- /* get features list for this module */
- if (!fset.count) {
- features = all_features;
- } else {
- get_features(&fset, module, &features);
- }
+int
+cmd_add_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ const char *all_features[] = {"*", NULL};
+ LY_ERR ret;
+ uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
+ char *dir, *modname = NULL;
+ const char **features = NULL;
+ struct ly_in *in = NULL;
+ struct lys_module *mod;
+
+ assert(posv);
- /* temporary cleanup */
- free(dir);
- free(module);
+ if (yo->ctx_options) {
+ ly_ctx_set_options(*ctx, yo->ctx_options);
+ }
+
+ if (parse_schema_path(posv, &dir, &modname)) {
+ return 1;
+ }
- /* prepare input handler */
- ret = ly_in_new_filepath(argv[optind + i], 0, &in);
- if (ret) {
+ if (!yo->interactive) {
+ /* The module should already be parsed if yang_lib_file was set. */
+ if (yo->yang_lib_file && (mod = ly_ctx_get_module_implemented(*ctx, modname))) {
+ ret = store_parsed_module(posv, mod, yo);
goto cleanup;
}
+ /* parse module */
+ }
+
+ /* add temporarily also the path of the module itself */
+ if (ly_ctx_set_searchdir(*ctx, dir) == LY_EEXIST) {
+ path_unset = 0;
+ }
+
+ /* get features list for this module */
+ if (!yo->schema_features.count) {
+ features = all_features;
+ } else {
+ get_features(&yo->schema_features, modname, &features);
+ }
+
+ /* prepare input handler */
+ ret = ly_in_new_filepath(posv, 0, &in);
+ if (ret) {
+ goto cleanup;
+ }
- /* parse the file */
- ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, NULL);
- ly_in_free(in, 1);
- ly_ctx_unset_searchdir_last(*ctx, path_unset);
+ /* parse the file */
+ ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, &mod);
+ ly_in_free(in, 1);
+ ly_ctx_unset_searchdir_last(*ctx, path_unset);
+ if (ret) {
+ goto cleanup;
+ }
- if (ret) {
- /* libyang printed the error messages */
+ if (!yo->interactive) {
+ if ((ret = store_parsed_module(posv, mod, yo))) {
goto cleanup;
}
}
cleanup:
- if (options_ctx) {
- ly_ctx_unset_options(*ctx, options_ctx);
- }
- ly_set_erase(&fset, free_features);
- free_cmdline(argv);
+ free(dir);
+ free(modname);
+
+ return ret;
}
diff --git a/tools/lint/cmd_clear.c b/tools/lint/cmd_clear.c
index 5eed6ff..4a869af 100644
--- a/tools/lint/cmd_clear.c
+++ b/tools/lint/cmd_clear.c
@@ -2,9 +2,10 @@
* @file cmd_clear.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'clear' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_clear_help(void)
@@ -32,68 +34,139 @@ cmd_clear_help(void)
" Replace the current context with an empty one, searchpaths\n"
" are not kept.\n\n"
" -i, --make-implemented\n"
- " Make the imported modules \"referenced\" from any loaded\n"
- " module also implemented. If specified a second time, all the\n"
- " modules are set implemented.\n"
+ " When loading a module into the context, the imported 'referenced'\n"
+ " modules will also be implemented. If specified a second time,\n"
+ " all the modules will be implemented.\n"
" -y, --yang-library\n"
" Load and implement internal \"ietf-yang-library\" YANG module.\n"
" Note that this module includes definitions of mandatory state\n"
- " data that can result in unexpected data validation errors.\n");
-#if 0
- " If <yang-library-data> path specified, load the modules\n"
- " according to the provided yang library data.\n"
-#endif
+ " data that can result in unexpected data validation errors.\n"
+ " -Y FILE, --yang-library-file=FILE\n"
+ " Parse FILE with \"ietf-yang-library\" data and use them to\n"
+ " create an exact YANG schema context. Searchpaths defined so far\n"
+ " are used, but then deleted.\n");
}
-void
-cmd_clear(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
- {"make-implemented", no_argument, NULL, 'i'},
- {"yang-library", no_argument, NULL, 'y'},
+ {"make-implemented", no_argument, NULL, 'i'},
+ {"yang-library", no_argument, NULL, 'y'},
+ {"yang-library-file", no_argument, NULL, 'Y'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
- uint16_t options_ctx = LY_CTX_NO_YANGLIBRARY;
- struct ly_ctx *ctx_new;
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ yo->ctx_options = LY_CTX_NO_YANGLIBRARY;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "iyh", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_CLEAR].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'i':
- if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
- options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
- options_ctx |= LY_CTX_ALL_IMPLEMENTED;
+ if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
+ yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
+ yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
} else {
- options_ctx |= LY_CTX_REF_IMPLEMENTED;
+ yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
}
break;
case 'y':
- options_ctx &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ break;
+ case 'Y':
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->yang_lib_file = optarg;
+ if (!yo->yang_lib_file) {
+ YLMSG_E("Memory allocation error.");
+ return 1;
+ }
break;
case 'h':
cmd_clear_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return rc;
+}
+
+int
+cmd_clear_dep(struct yl_opt *yo, int posc)
+{
+ (void) yo;
+
+ if (posc) {
+ YLMSG_E("No positional arguments are allowed.");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Convert searchpaths into single string.
+ *
+ * @param[in] ctx Context with searchpaths.
+ * @param[out] searchpaths Collection of paths in the single string. Paths are delimited by colon ":"
+ * (on Windows, used semicolon ";" instead).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+searchpaths_to_str(const struct ly_ctx *ctx, char **searchpaths)
+{
+ uint32_t i;
+ int rc = 0;
+ const char * const *dirs = ly_ctx_get_searchdirs(ctx);
+
+ for (i = 0; dirs[i]; ++i) {
+ rc = searchpath_strcat(searchpaths, dirs[i]);
+ if (!rc) {
+ break;
}
}
- if (ly_ctx_new(NULL, options_ctx, &ctx_new)) {
- YLMSG_W("Failed to create context.\n");
- goto cleanup;
+ return rc;
+}
+
+int
+cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) posv;
+ struct ly_ctx *ctx_new = NULL;
+
+ if (yo->yang_lib_file) {
+ if (searchpaths_to_str(*ctx, &yo->searchpaths)) {
+ YLMSG_E("Storing searchpaths failed.");
+ return 1;
+ }
+ if (ly_ctx_new_ylpath(yo->searchpaths, yo->yang_lib_file, LYD_UNKNOWN, yo->ctx_options, &ctx_new)) {
+ YLMSG_E("Unable to create libyang context with yang-library data.");
+ return 1;
+ }
+ } else {
+ if (ly_ctx_new(NULL, yo->ctx_options, &ctx_new)) {
+ YLMSG_W("Failed to create context.");
+ return 1;
+ }
}
+ /* Global variables in commands are also deleted. */
+ cmd_free();
+
ly_ctx_destroy(*ctx);
*ctx = ctx_new;
-cleanup:
- free_cmdline(argv);
+ return 0;
}
diff --git a/tools/lint/cmd_data.c b/tools/lint/cmd_data.c
index 25449f5..44fb237 100644
--- a/tools/lint/cmd_data.c
+++ b/tools/lint/cmd_data.c
@@ -2,9 +2,10 @@
* @file cmd_data.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'data' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,6 +18,7 @@
#include "cmd.h"
+#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
@@ -27,18 +29,23 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
-void
-cmd_data_help(void)
+static void
+cmd_data_help_header(void)
{
printf("Usage: data [-emn] [-t TYPE]\n"
" [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
" data [-n] -t (rpc | notif | reply) [-O FILE]\n"
" [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
" data [-en] [-t TYPE] [-F FORMAT] -x XPATH [-o OUTFILE] <data1> ...\n"
- " Parse, validate and optionally print data instances\n\n"
+ " Parse, validate and optionally print data instances\n");
+}
- " -t TYPE, --type=TYPE\n"
+static void
+cmd_data_help_type(void)
+{
+ printf(" -t TYPE, --type=TYPE\n"
" Specify data tree type in the input data file(s):\n"
" data - Complete datastore with status data (default type).\n"
" config - Configuration datastore (without status data).\n"
@@ -47,39 +54,45 @@ cmd_data_help(void)
" edit - Content of the NETCONF <edit-config> operation.\n"
" rpc - Content of the NETCONF <rpc> message, defined as YANG's\n"
" RPC/Action input statement.\n"
+ " nc-rpc - Similar to 'rpc' but expect and check also the NETCONF\n"
+ " envelopes <rpc> or <action>.\n"
" reply - Reply to the RPC/Action. Note that the reply data are\n"
" expected inside a container representing the original\n"
" RPC/Action. This is necessary to identify appropriate\n"
" data definitions in the schema module.\n"
+ " nc-reply - Similar to 'reply' but expect and check also the NETCONF\n"
+ " envelope <rpc-reply> with output data nodes as direct\n"
+ " descendants. The original RPC/action invocation is expected\n"
+ " in a separate parameter '-R' and is parsed as 'nc-rpc'.\n"
" notif - Notification instance (content of the <notification>\n"
- " element without <eventTime>).\n\n"
-
- " -e, --present Validate only with the schema modules whose data actually\n"
- " exist in the provided input data files. Takes effect only\n"
- " with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
- " mandatory nodes from modules which data are not present in the\n"
- " provided input data files.\n"
- " -m, --merge Merge input data files into a single tree and validate at\n"
- " once.The option has effect only for 'data' and 'config' TYPEs.\n"
- " In case of using -x option, the data are always merged.\n"
- " -n, --not-strict\n"
- " Do not require strict data parsing (silently skip unknown data),\n"
- " has no effect for schemas.\n\n"
- " -O FILE, --operational=FILE\n"
- " Provide optional data to extend validation of the 'rpc',\n"
- " 'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
- " the :running configuration datastore and state data\n"
- " (operational datastore) referenced from the RPC/Notification.\n\n"
+ " element without <eventTime>).\n"
+ " nc-notif - Similar to 'notif' but expect and check also the NETCONF\n"
+ " envelope <notification> with element <eventTime> and its\n"
+ " sibling as the actual notification.\n");
+}
- " -f FORMAT, --format=FORMAT\n"
+static void
+cmd_data_help_format(void)
+{
+ printf(" -f FORMAT, --format=FORMAT\n"
" Print the data in one of the following formats:\n"
" xml, json, lyb\n"
- " Note that the LYB format requires the -o option specified.\n"
- " -F FORMAT, --in-format=FORMAT\n"
+ " Note that the LYB format requires the -o option specified.\n");
+}
+
+static void
+cmd_data_help_in_format(void)
+{
+ printf(" -F FORMAT, --in-format=FORMAT\n"
" Load the data in one of the following formats:\n"
" xml, json, lyb\n"
- " If input format not specified, it is detected from the file extension.\n"
- " -d MODE, --default=MODE\n"
+ " If input format not specified, it is detected from the file extension.\n");
+}
+
+static void
+cmd_data_help_default(void)
+{
+ printf(" -d MODE, --default=MODE\n"
" Print data with default values, according to the MODE\n"
" (to print attributes, ietf-netconf-with-defaults model\n"
" must be loaded):\n"
@@ -87,24 +100,58 @@ cmd_data_help(void)
" all-tagged - Add missing default nodes and mark all the default\n"
" nodes with the attribute.\n"
" trim - Remove all nodes with a default value.\n"
- " implicit-tagged - Add missing nodes and mark them with the attribute.\n"
- " -o OUTFILE, --output=OUTFILE\n"
- " Write the output to OUTFILE instead of stdout.\n\n"
+ " implicit-tagged - Add missing nodes and mark them with the attribute.\n");
+}
- " -x XPATH, --xpath=XPATH\n"
- " Evaluate XPATH expression and print the nodes satysfying the.\n"
+static void
+cmd_data_help_xpath(void)
+{
+ printf(" -x XPATH, --xpath=XPATH\n"
+ " Evaluate XPATH expression and print the nodes satisfying the\n"
" expression. The output format is specific and the option cannot\n"
" be combined with the -f and -d options. Also all the data\n"
" inputs are merged into a single data tree where the expression\n"
- " is evaluated, so the -m option is always set implicitly.\n\n");
-
+ " is evaluated, so the -m option is always set implicitly.\n");
}
void
-cmd_data(struct ly_ctx **ctx, const char *cmdline)
+cmd_data_help(void)
+{
+ cmd_data_help_header();
+ printf("\n");
+ cmd_data_help_type();
+ printf(" -e, --present Validate only with the schema modules whose data actually\n"
+ " exist in the provided input data files. Takes effect only\n"
+ " with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
+ " mandatory nodes from modules which data are not present in the\n"
+ " provided input data files.\n"
+ " -m, --merge Merge input data files into a single tree and validate at\n"
+ " once.The option has effect only for 'data' and 'config' TYPEs.\n"
+ " In case of using -x option, the data are always merged.\n"
+ " -n, --not-strict\n"
+ " Do not require strict data parsing (silently skip unknown data),\n"
+ " has no effect for schemas.\n"
+ " -O FILE, --operational=FILE\n"
+ " Provide optional data to extend validation of the 'rpc',\n"
+ " 'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
+ " the operational datastore referenced from the operation.\n"
+ " In case of a nested notification or action, its parent\n"
+ " existence is also checked in these operational data.\n"
+ " -R FILE, --reply-rpc=FILE\n"
+ " Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
+ " is supposed to contain the source 'nc-rpc' operation of the reply.\n");
+ cmd_data_help_format();
+ cmd_data_help_in_format();
+ printf(" -o OUTFILE, --output=OUTFILE\n"
+ " Write the output to OUTFILE instead of stdout.\n");
+ cmd_data_help_xpath();
+ printf("\n");
+}
+
+int
+cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"defaults", required_argument, NULL, 'd'},
@@ -115,214 +162,525 @@ cmd_data(struct ly_ctx **ctx, const char *cmdline)
{"merge", no_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
{"operational", required_argument, NULL, 'O'},
+ {"reply-rpc", required_argument, NULL, 'R'},
{"not-strict", no_argument, NULL, 'n'},
{"type", required_argument, NULL, 't'},
{"xpath", required_argument, NULL, 'x'},
{NULL, 0, NULL, 0}
};
- uint8_t data_merge = 0;
- uint32_t options_print = 0;
- uint32_t options_parse = YL_DEFAULT_DATA_PARSE_OPTIONS;
- uint32_t options_validate = 0;
- enum lyd_type data_type = 0;
uint8_t data_type_set = 0;
- LYD_FORMAT outformat = LYD_UNKNOWN;
- LYD_FORMAT informat = LYD_UNKNOWN;
- struct ly_out *out = NULL;
- struct cmdline_file *operational = NULL;
- struct ly_set inputs = {0};
- struct ly_set xpaths = {0};
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+
+ yo->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
+ yo->data_validate_options = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "d:ef:F:hmo:O:r:nt:x:", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_DATA].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'd': /* --default */
- if (!strcasecmp(optarg, "all")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
- } else if (!strcasecmp(optarg, "all-tagged")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
- } else if (!strcasecmp(optarg, "trim")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
- } else if (!strcasecmp(optarg, "implicit-tagged")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
- } else {
- YLMSG_E("Unknown default mode %s\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yo_opt_update_data_default(optarg, yo)) {
+ YLMSG_E("Unknown default mode %s.", optarg);
+ cmd_data_help_default();
+ return 1;
}
break;
case 'f': /* --format */
- if (!strcasecmp(optarg, "xml")) {
- outformat = LYD_XML;
- } else if (!strcasecmp(optarg, "json")) {
- outformat = LYD_JSON;
- } else if (!strcasecmp(optarg, "lyb")) {
- outformat = LYD_LYB;
- } else {
- YLMSG_E("Unknown output format %s\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yl_opt_update_data_out_format(optarg, yo)) {
+ cmd_data_help_format();
+ return 1;
}
break;
case 'F': /* --in-format */
- if (!strcasecmp(optarg, "xml")) {
- informat = LYD_XML;
- } else if (!strcasecmp(optarg, "json")) {
- informat = LYD_JSON;
- } else if (!strcasecmp(optarg, "lyb")) {
- informat = LYD_LYB;
- } else {
- YLMSG_E("Unknown input format %s\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yo_opt_update_data_in_format(optarg, yo)) {
+ YLMSG_E("Unknown input format %s.", optarg);
+ cmd_data_help_in_format();
+ return 1;
}
break;
case 'o': /* --output */
- if (out) {
- YLMSG_E("Only a single output can be specified.\n");
- goto cleanup;
+ if (yo->out) {
+ YLMSG_E("Only a single output can be specified.");
+ return 1;
} else {
- if (ly_out_new_filepath(optarg, &out)) {
- YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
- goto cleanup;
+ if (ly_out_new_filepath(optarg, &yo->out)) {
+ YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
+ return 1;
}
}
break;
- case 'O': { /* --operational */
- struct ly_in *in;
- LYD_FORMAT f;
-
- if (operational) {
- YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
- goto cleanup;
+ case 'O': /* --operational */
+ if (yo->data_operational.path) {
+ YLMSG_E("The operational datastore (-O) cannot be set multiple times.");
+ return 1;
}
- if (get_input(optarg, NULL, &f, &in)) {
- goto cleanup;
+ yo->data_operational.path = optarg;
+ break;
+ case 'R': /* --reply-rpc */
+ if (yo->reply_rpc.path) {
+ YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.");
+ return 1;
}
- operational = fill_cmdline_file(NULL, in, optarg, f);
+ yo->reply_rpc.path = optarg;
break;
- } /* case 'O' */
-
case 'e': /* --present */
- options_validate |= LYD_VALIDATE_PRESENT;
+ yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;
case 'm': /* --merge */
- data_merge = 1;
+ yo->data_merge = 1;
break;
case 'n': /* --not-strict */
- options_parse &= ~LYD_PARSE_STRICT;
+ yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
case 't': /* --type */
if (data_type_set) {
- YLMSG_E("The data type (-t) cannot be set multiple times.\n");
- goto cleanup;
+ YLMSG_E("The data type (-t) cannot be set multiple times.");
+ return 1;
}
- if (!strcasecmp(optarg, "config")) {
- options_parse |= LYD_PARSE_NO_STATE;
- options_validate |= LYD_VALIDATE_NO_STATE;
- } else if (!strcasecmp(optarg, "get")) {
- options_parse |= LYD_PARSE_ONLY;
- } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config") || !strcasecmp(optarg, "edit")) {
- options_parse |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
- } else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
- data_type = LYD_TYPE_RPC_YANG;
- } else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
- data_type = LYD_TYPE_REPLY_YANG;
- } else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
- data_type = LYD_TYPE_NOTIF_YANG;
- } else if (!strcasecmp(optarg, "data")) {
- /* default option */
- } else {
- YLMSG_E("Unknown data tree type %s.\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yl_opt_update_data_type(optarg, yo)) {
+ YLMSG_E("Unknown data tree type %s.", optarg);
+ cmd_data_help_type();
+ return 1;
}
data_type_set = 1;
break;
case 'x': /* --xpath */
- if (ly_set_add(&xpaths, optarg, 0, NULL)) {
- YLMSG_E("Storing XPath \"%s\" failed.\n", optarg);
- goto cleanup;
+ if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
+ YLMSG_E("Storing XPath \"%s\" failed.", optarg);
+ return 1;
}
break;
case 'h': /* --help */
cmd_data_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (optind == argc) {
- YLMSG_E("Missing the data file to process.\n");
- goto cleanup;
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return rc;
+}
+
+int
+cmd_data_dep(struct yl_opt *yo, int posc)
+{
+ if (yo->interactive && !posc) {
+ YLMSG_E("Missing the data file to process.");
+ return 1;
}
- if (data_merge) {
- if (data_type || (options_parse & LYD_PARSE_ONLY)) {
+ if (yo->data_merge) {
+ if (yo->data_type || (yo->data_parse_options & LYD_PARSE_ONLY)) {
/* switch off the option, incompatible input data type */
- data_merge = 0;
+ YLMSG_W("The --merge option has effect only for 'data' and 'config' TYPEs.");
+ yo->data_merge = 0;
} else {
/* postpone validation after the merge of all the input data */
- options_parse |= LYD_PARSE_ONLY;
+ yo->data_parse_options |= LYD_PARSE_ONLY;
}
- } else if (xpaths.count) {
- data_merge = 1;
+ } else if (yo->data_xpath.count) {
+ yo->data_merge = 1;
}
- if (xpaths.count && outformat) {
- YLMSG_E("The --format option cannot be combined with --xpath option.\n");
- cmd_data_help();
- goto cleanup;
+ if (yo->data_xpath.count && (yo->schema_out_format || yo->data_out_format)) {
+ YLMSG_E("The --format option cannot be combined with --xpath option.");
+ if (yo->interactive) {
+ cmd_data_help_xpath();
+ }
+ return 1;
+ }
+ if (yo->data_xpath.count && (yo->data_print_options & LYD_PRINT_WD_MASK)) {
+ YLMSG_E("The --default option cannot be combined with --xpath option.");
+ if (yo->interactive) {
+ cmd_data_help_xpath();
+ }
+ return 1;
+ }
+
+ if (yo->data_operational.path && !yo->data_type) {
+ YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.");
+ yo->data_operational.path = NULL;
+ }
+
+ if (yo->reply_rpc.path && (yo->data_type != LYD_TYPE_REPLY_NETCONF)) {
+ YLMSG_W("Source RPC is needed only for NETCONF Reply input data type.");
+ yo->data_operational.path = NULL;
+ } else if (!yo->reply_rpc.path && (yo->data_type == LYD_TYPE_REPLY_NETCONF)) {
+ YLMSG_E("Missing source RPC (-R) for NETCONF Reply input data type.");
+ return 1;
+ }
+
+ if (!yo->out && (yo->data_out_format == LYD_LYB)) {
+ YLMSG_E("The LYB format requires the -o option specified.");
+ return 1;
+ }
+
+ /* default output stream */
+ if (!yo->out) {
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Unable to set stdout as output.");
+ return 1;
+ }
+ yo->out_stdout = 1;
}
- if (operational && !data_type) {
- YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notifications input data types.\n");
- free_cmdline_file(operational);
- operational = NULL;
+ /* process the operational and/or reply RPC content if any */
+ if (yo->data_operational.path) {
+ if (get_input(yo->data_operational.path, NULL, &yo->data_operational.format, &yo->data_operational.in)) {
+ return -1;
+ }
+ }
+ if (yo->reply_rpc.path) {
+ if (get_input(yo->reply_rpc.path, NULL, &yo->reply_rpc.format, &yo->reply_rpc.in)) {
+ return -1;
+ }
}
+ return 0;
+}
+
+int
+cmd_data_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) ctx;
+ struct ly_in *in;
+ LYD_FORMAT format_data;
+
+ assert(posv);
+
+ format_data = yo->data_in_format;
+
/* process input data files provided as standalone command line arguments */
- for (int i = 0; i < argc - optind; i++) {
- struct ly_in *in;
+ if (get_input(posv, NULL, &format_data, &in)) {
+ return 1;
+ }
+
+ if (!fill_cmdline_file(&yo->data_inputs, in, posv, format_data)) {
+ ly_in_free(in, 1);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Evaluate xpath adn print result.
+ *
+ * @param[in] tree Data tree.
+ * @param[in] xpath Xpath to evaluate.
+ * @return 0 on success.
+ */
+static int
+evaluate_xpath(const struct lyd_node *tree, const char *xpath)
+{
+ struct ly_set *set = NULL;
+
+ if (lyd_find_xpath(tree, xpath, &set)) {
+ return -1;
+ }
+
+ /* print result */
+ printf("XPath \"%s\" evaluation result:\n", xpath);
+ if (!set->count) {
+ printf("\tEmpty\n");
+ } else {
+ for (uint32_t u = 0; u < set->count; ++u) {
+ struct lyd_node *node = (struct lyd_node *)set->objs[u];
+
+ printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
+ if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ printf(" (value: \"%s\")\n", lyd_get_value(node));
+ } else if (node->schema->nodetype == LYS_LIST) {
+ printf(" (");
+ for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
+ printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
+ key->schema->name, lyd_get_value(key));
+ }
+ printf(")\n");
+ } else {
+ printf("\n");
+ }
+ }
+ }
+
+ ly_set_free(set, NULL);
+ return 0;
+}
+
+/**
+ * @brief Checking that a parent data node exists in the datastore for the nested-notification and action.
+ *
+ * @param[in] op Operation to check.
+ * @param[in] oper_tree Data from datastore.
+ * @param[in] operational_f Operational datastore file information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+check_operation_parent(struct lyd_node *op, struct lyd_node *oper_tree, struct cmdline_file *operational_f)
+{
+ LY_ERR ret;
+ struct ly_set *set = NULL;
+ char *path = NULL;
+
+ if (!op || !lyd_parent(op)) {
+ /* The function is defined only for nested-notification and action. */
+ return LY_SUCCESS;
+ }
+
+ if (!operational_f || (operational_f && !operational_f->in)) {
+ YLMSG_E("The --operational parameter needed to validate operation \"%s\" is missing.", LYD_NAME(op));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
+ if (!path) {
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+
+ if (!oper_tree) {
+ YLMSG_W("Operational datastore is empty or contains unknown data.");
+ YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.", LYD_NAME(op), path);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
+ goto cleanup;
+ }
+ if (!set->count) {
+ YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.", LYD_NAME(op), path);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ free(path);
+
+ return ret;
+}
+
+/**
+ * @brief Process the input data files - parse, validate and print according to provided options.
+ *
+ * @param[in] ctx libyang context with schema.
+ * @param[in] type The type of data in the input files.
+ * @param[in] merge Flag if the data should be merged before validation.
+ * @param[in] out_format Data format for printing.
+ * @param[in] out The output handler for printing.
+ * @param[in] parse_options Parser options.
+ * @param[in] validate_options Validation options.
+ * @param[in] print_options Printer options.
+ * @param[in] operational Optional operational datastore file information for the case of an extended validation of
+ * operation(s).
+ * @param[in] reply_rpc Source RPC operation file information for parsing NETCONF rpc-reply.
+ * @param[in] inputs Set of file informations of input data files.
+ * @param[in] xpaths The set of XPaths to be evaluated on the processed data tree, basic information about the resulting set
+ * is printed. Alternative to data printing.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+process_data(struct ly_ctx *ctx, enum lyd_type type, uint8_t merge, LYD_FORMAT out_format,
+ struct ly_out *out, uint32_t parse_options, uint32_t validate_options, uint32_t print_options,
+ struct cmdline_file *operational, struct cmdline_file *reply_rpc, struct ly_set *inputs,
+ struct ly_set *xpaths)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
+ const char *xpath;
+ struct ly_set *set = NULL;
- if (get_input(argv[optind + i], NULL, &informat, &in)) {
+ /* additional operational datastore */
+ if (operational && operational->in) {
+ ret = lyd_parse_data(ctx, NULL, operational->in, operational->format, LYD_PARSE_ONLY, 0, &oper_tree);
+ if (ret) {
+ YLMSG_E("Failed to parse operational datastore file \"%s\".", operational->path);
goto cleanup;
}
+ }
+
+ for (uint32_t u = 0; u < inputs->count; ++u) {
+ struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
+
+ switch (type) {
+ case LYD_TYPE_DATA_YANG:
+ ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, parse_options, validate_options, &tree);
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_NOTIF_YANG:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &tree, &op);
+ break;
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &envp, &op);
+
+ /* adjust pointers */
+ for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ /* parse source RPC operation */
+ assert(reply_rpc && reply_rpc->in);
+ ret = lyd_parse_op(ctx, NULL, reply_rpc->in, reply_rpc->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
+ if (ret) {
+ YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".", reply_rpc->path);
+ goto cleanup;
+ }
+
+ /* adjust pointers */
+ for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
+
+ /* free input */
+ lyd_free_siblings(lyd_child(op));
+
+ /* we do not care */
+ lyd_free_all(envp);
+ envp = NULL;
- if (!fill_cmdline_file(&inputs, in, argv[optind + i], informat)) {
- ly_in_free(in, 1);
+ ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, type, &envp, NULL);
+ break;
+ default:
+ YLMSG_E("Internal error (%s:%d).", __FILE__, __LINE__);
+ goto cleanup;
+ }
+
+ if (ret) {
+ YLMSG_E("Failed to parse input data file \"%s\".", input_f->path);
goto cleanup;
}
+
+ if (merge) {
+ /* merge the data so far parsed for later validation and print */
+ if (!merged_tree) {
+ merged_tree = tree;
+ } else {
+ ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
+ if (ret) {
+ YLMSG_E("Merging %s with previous data failed.", input_f->path);
+ goto cleanup;
+ }
+ }
+ tree = NULL;
+ } else if (out_format) {
+ /* print */
+ switch (type) {
+ case LYD_TYPE_DATA_YANG:
+ lyd_print_all(out, tree, out_format, print_options);
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_NOTIF_YANG:
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ lyd_print_tree(out, tree, out_format, print_options);
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ /* just the output */
+ lyd_print_tree(out, lyd_child(tree), out_format, print_options);
+ break;
+ default:
+ assert(0);
+ }
+ } else {
+ /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
+ switch (type) {
+ case LYD_TYPE_DATA_YANG:
+ /* already validated */
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_RPC_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_REPLY_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ case LYD_TYPE_NOTIF_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
+ break;
+ default:
+ assert(0);
+ }
+ if (ret) {
+ if (operational->path) {
+ YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".",
+ input_f->path, operational->path);
+ } else {
+ YLMSG_E("Failed to validate input data file \"%s\".", input_f->path);
+ }
+ goto cleanup;
+ }
+
+ if ((ret = check_operation_parent(op, oper_tree, operational))) {
+ goto cleanup;
+ }
+ }
+
+ /* next iter */
+ lyd_free_all(tree);
+ tree = NULL;
+ lyd_free_all(envp);
+ envp = NULL;
}
- /* default output stream */
- if (!out) {
- if (ly_out_new_file(stdout, &out)) {
- YLMSG_E("Unable to set stdout as output.\n");
+ if (merge) {
+ /* validate the merged result */
+ ret = lyd_validate_all(&merged_tree, ctx, validate_options, NULL);
+ if (ret) {
+ YLMSG_E("Merged data are not valid.");
goto cleanup;
}
+
+ if (out_format) {
+ /* and print it */
+ lyd_print_all(out, merged_tree, out_format, print_options);
+ }
+
+ for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
+ xpath = (const char *)xpaths->objs[u];
+ ly_set_free(set, NULL);
+ ret = lys_find_xpath(ctx, NULL, xpath, LYS_FIND_NO_MATCH_ERROR, &set);
+ if (ret || !set->count) {
+ ret = (ret == LY_SUCCESS) ? LY_EINVAL : ret;
+ YLMSG_E("The requested xpath failed.");
+ goto cleanup;
+ }
+ if (evaluate_xpath(merged_tree, xpath)) {
+ goto cleanup;
+ }
+ }
}
+cleanup:
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+ lyd_free_all(merged_tree);
+ lyd_free_all(oper_tree);
+ ly_set_free(set, NULL);
+ return ret;
+}
+
+int
+cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
+{
/* parse, validate and print data */
- if (process_data(*ctx, data_type, data_merge, outformat, out, options_parse, options_validate, options_print,
- operational, NULL, &inputs, &xpaths)) {
- goto cleanup;
+ if (process_data(ctx, yo->data_type, yo->data_merge, yo->data_out_format, yo->out, yo->data_parse_options,
+ yo->data_validate_options, yo->data_print_options, &yo->data_operational, &yo->reply_rpc,
+ &yo->data_inputs, &yo->data_xpath)) {
+ return 1;
}
-cleanup:
- ly_out_free(out, NULL, 0);
- ly_set_erase(&inputs, free_cmdline_file);
- ly_set_erase(&xpaths, NULL);
- free_cmdline_file(operational);
- free_cmdline(argv);
+ return 0;
}
diff --git a/tools/lint/cmd_debug.c b/tools/lint/cmd_debug.c
new file mode 100644
index 0000000..3661bfa
--- /dev/null
+++ b/tools/lint/cmd_debug.c
@@ -0,0 +1,134 @@
+/**
+ * @file cmd_debug.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'verb' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2023-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef NDEBUG
+
+#include "cmd.h"
+
+#include <assert.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+struct debug_groups {
+ char *name;
+ uint32_t flag;
+} const dg [] = {
+ {"dict", LY_LDGDICT},
+ {"xpath", LY_LDGXPATH},
+ {"dep-sets", LY_LDGDEPSETS},
+};
+#define DG_LENGTH (sizeof dg / sizeof *dg)
+
+void
+cmd_debug_help(void)
+{
+ uint32_t i;
+
+ printf("Usage: debug (");
+ for (i = 0; i < DG_LENGTH; i++) {
+ if ((i + 1) == DG_LENGTH) {
+ printf("%s", dg[i].name);
+ } else {
+ printf("%s | ", dg[i].name);
+ }
+ }
+ printf(")+\n");
+}
+
+int
+cmd_debug_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_DEBUG].optstring, options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ cmd_debug_help();
+ return 1;
+ default:
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_debug_dep(struct yl_opt *yo, int posc)
+{
+ (void) yo;
+
+ if (yo->interactive && !posc) {
+ /* no argument */
+ cmd_debug_help();
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) ctx;
+ uint32_t i;
+ ly_bool set;
+
+ assert(posv);
+
+ set = 0;
+ for (i = 0; i < DG_LENGTH; i++) {
+ if (!strcasecmp(posv, dg[i].name)) {
+ yo->dbg_groups |= dg[i].flag;
+ set = 1;
+ break;
+ }
+ }
+
+ if (!set) {
+ YLMSG_E("Unknown debug group \"%s\".", posv);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+ (void) ctx;
+ return ly_log_dbg_groups(yo->dbg_groups);
+}
+
+#endif
diff --git a/tools/lint/cmd_extdata.c b/tools/lint/cmd_extdata.c
new file mode 100644
index 0000000..fc7ac7b
--- /dev/null
+++ b/tools/lint/cmd_extdata.c
@@ -0,0 +1,115 @@
+/**
+ * @file cmd_extdata.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'extdata' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+char *filename;
+
+void
+cmd_extdata_free(void)
+{
+ free(filename);
+ filename = NULL;
+}
+
+void
+cmd_extdata_help(void)
+{
+ printf("Usage: extdata [--clear] [<extdata-file-path>]\n"
+ " File containing the specific data required by an extension. Required by\n"
+ " the schema-mount extension, for example, when the operational data are\n"
+ " expected in the file. File format is guessed.\n");
+}
+
+int
+cmd_extdata_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"clear", no_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_EXTDATA].optstring, options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ yo->extdata_unset = 1;
+ free(filename);
+ filename = NULL;
+ break;
+ case 'h':
+ cmd_extdata_help();
+ return 1;
+ default:
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_extdata_dep(struct yl_opt *yo, int posc)
+{
+ if (!yo->extdata_unset && (posc > 1)) {
+ YLMSG_E("Only one file must be entered.");
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_extdata_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ if (yo->extdata_unset) {
+ ly_ctx_set_ext_data_clb(*ctx, NULL, NULL);
+ } else if (!yo->extdata_unset && !posv) {
+ /* no argument - print the current file */
+ printf("%s\n", filename ? filename : "No file set.");
+ } else if (posv) {
+ /* set callback providing run-time extension instance data */
+ free(filename);
+ filename = strdup(posv);
+ if (!filename) {
+ YLMSG_E("Memory allocation error.");
+ return 1;
+ }
+ ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, filename);
+ }
+
+ return 0;
+}
diff --git a/tools/lint/cmd_feature.c b/tools/lint/cmd_feature.c
index 6b332ab..96d55c1 100644
--- a/tools/lint/cmd_feature.c
+++ b/tools/lint/cmd_feature.c
@@ -1,9 +1,10 @@
/**
* @file cmd_feature.c
* @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'feature' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -23,6 +24,8 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
void
cmd_feature_help(void)
@@ -37,17 +40,11 @@ cmd_feature_help(void)
" Print features of all implemented modules.\n");
}
-void
-cmd_feature(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_feature_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
- char *features_output = NULL;
- int opt, opt_index, i;
- ly_bool generate_features = 0, print_all = 0;
- struct ly_set set = {0};
- const struct lys_module *mod;
- struct ly_out *out = NULL;
+ int rc = 0, argc = 0;
+ int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{"all", no_argument, NULL, 'a'},
@@ -55,81 +52,80 @@ cmd_feature(struct ly_ctx **ctx, const char *cmdline)
{NULL, 0, NULL, 0}
};
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "haf", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_FEATURE].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'h':
cmd_feature_help();
- goto cleanup;
+ return 1;
case 'a':
- print_all = 1;
+ yo->feature_print_all = 1;
break;
case 'f':
- generate_features = 1;
+ yo->feature_param_format = 1;
break;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (ly_out_new_file(stdout, &out)) {
- YLMSG_E("Unable to print to the standard output.\n");
- goto cleanup;
- }
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
- if (print_all) {
- if (print_all_features(out, *ctx, generate_features, &features_output)) {
- YLMSG_E("Printing all features failed.\n");
- goto cleanup;
- }
- if (generate_features) {
- printf("%s\n", features_output);
- }
- goto cleanup;
- }
+ return 0;
+}
- if (argc == optind) {
- YLMSG_E("Missing modules to print.\n");
- goto cleanup;
+int
+cmd_feature_dep(struct yl_opt *yo, int posc)
+{
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Unable to print to the standard output.");
+ return 1;
}
+ yo->out_stdout = 1;
- for (i = 0; i < argc - optind; i++) {
- /* always erase the set, so the previous module's features don't carry over to the next module's features */
- ly_set_erase(&set, NULL);
+ if (yo->interactive && !yo->feature_print_all && !posc) {
+ YLMSG_E("Missing modules to print.");
+ return 1;
+ }
- mod = ly_ctx_get_module_latest(*ctx, argv[optind + i]);
- if (!mod) {
- YLMSG_E("Module \"%s\" not found.\n", argv[optind + i]);
- goto cleanup;
- }
+ return 0;
+}
- /* collect features of the module */
- if (collect_features(mod, &set)) {
- goto cleanup;
- }
+int
+cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ const struct lys_module *mod;
- if (generate_features) {
- if (generate_features_output(mod, &set, &features_output)) {
- goto cleanup;
- }
- /* don't print features and their state of each module if generating features parameter */
- continue;
- }
+ if (yo->feature_print_all) {
+ print_all_features(yo->out, *ctx, yo->feature_param_format);
+ return 0;
+ }
- print_features(out, mod, &set);
+ mod = ly_ctx_get_module_latest(*ctx, posv);
+ if (!mod) {
+ YLMSG_E("Module \"%s\" not found.", posv);
+ return 1;
}
- if (generate_features) {
- printf("%s\n", features_output);
+ if (yo->feature_param_format) {
+ print_feature_param(yo->out, mod);
+ } else {
+ print_features(yo->out, mod);
}
-cleanup:
- free_cmdline(argv);
- ly_out_free(out, NULL, 0);
- ly_set_erase(&set, NULL);
- free(features_output);
+ return 0;
+}
+
+int
+cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+ (void) ctx;
+
+ ly_print(yo->out, "\n");
+ return 0;
}
diff --git a/tools/lint/cmd_help.c b/tools/lint/cmd_help.c
new file mode 100644
index 0000000..a1ee3f6
--- /dev/null
+++ b/tools/lint/cmd_help.c
@@ -0,0 +1,107 @@
+/**
+ * @file cmd_help.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'help' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2023-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+void
+cmd_help_help(void)
+{
+ printf("Usage: help [cmd ...]\n");
+}
+
+int
+cmd_help_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_HELP].optstring, options, &opt_index)) != -1) {
+ if (opt == 'h') {
+ cmd_help_help();
+ return 1;
+ } else {
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return rc;
+}
+
+static void
+print_generic_help(void)
+{
+ printf("Available commands:\n");
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (commands[i].helpstring != NULL) {
+ printf(" %-15s %s\n", commands[i].name, commands[i].helpstring);
+ }
+ }
+}
+
+int
+cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void)ctx, (void)yo;
+
+ if (!posv) {
+ print_generic_help();
+ } else {
+ /* print specific help for the selected command(s) */
+
+ int8_t match = 0;
+
+ /* get the command of the specified name */
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (strcmp(posv, commands[i].name) == 0) {
+ match = 1;
+ if (commands[i].help_func != NULL) {
+ commands[i].help_func();
+ } else {
+ printf("%s: %s\n", posv, commands[i].helpstring);
+ }
+ break;
+ }
+ }
+ if (!match) {
+ /* if unknown command specified, print the list of commands */
+ printf("Unknown command \'%s\'\n", posv);
+ print_generic_help();
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/lint/cmd_list.c b/tools/lint/cmd_list.c
index ec7a021..166fbfa 100644
--- a/tools/lint/cmd_list.c
+++ b/tools/lint/cmd_list.c
@@ -2,9 +2,10 @@
* @file cmd_list.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'list' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_list_help(void)
@@ -37,53 +39,148 @@ cmd_list_help(void)
" modules.\n");
}
-void
-cmd_list(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_list_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"format", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
- LYD_FORMAT format = LYD_UNKNOWN;
- struct ly_out *out = NULL;
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ yo->data_out_format = LYD_UNKNOWN;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "f:h", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_LIST].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'f': /* --format */
if (!strcasecmp(optarg, "xml")) {
- format = LYD_XML;
+ yo->data_out_format = LYD_XML;
} else if (!strcasecmp(optarg, "json")) {
- format = LYD_JSON;
+ yo->data_out_format = LYD_JSON;
} else {
- YLMSG_E("Unknown output format %s\n", optarg);
+ YLMSG_E("Unknown output format %s.", optarg);
cmd_list_help();
- goto cleanup;
+ return 1;
}
break;
case 'h':
cmd_list_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_list_dep(struct yl_opt *yo, int posc)
+{
+ if (posc) {
+ YLMSG_E("No positional arguments are allowed.");
+ return 1;
+ }
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Unable to print to the standard output.");
+ return 1;
+ }
+ yo->out_stdout = 1;
+
+ return 0;
+}
+
+/**
+ * @brief Print yang library data.
+ *
+ * @param[in] ctx Context for libyang.
+ * @param[in] data_out_format Output format of printed data.
+ * @param[in] out Output handler.
+ * @return 0 on success.
+ */
+static int
+print_yang_lib_data(struct ly_ctx *ctx, LYD_FORMAT data_out_format, struct ly_out *out)
+{
+ struct lyd_node *ylib;
+
+ if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
+ YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, "
+ "use an option to add it internally.");
+ return 1;
+ }
+
+ lyd_print_all(out, ylib, data_out_format, 0);
+ lyd_free_all(ylib);
+
+ return 0;
+}
+
+int
+cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) posv;
+ uint32_t idx = 0, has_modules = 0;
+ const struct lys_module *mod;
+
+ if (yo->data_out_format != LYD_UNKNOWN) {
+ /* ietf-yang-library data are printed in the specified format */
+ if (print_yang_lib_data(*ctx, yo->data_out_format, yo->out)) {
+ return 1;
}
+ return 0;
}
- if (!ly_out_new_file(stdout, &out)) {
- print_list(out, *ctx, format);
- ly_out_free(out, NULL, 0);
- } else {
- YLMSG_E("Unable to print to the standard output.\n");
+ /* iterate schemas in context and provide just the basic info */
+ ly_print(yo->out, "List of the loaded models:\n");
+ while ((mod = ly_ctx_get_module_iter(*ctx, &idx))) {
+ has_modules++;
+
+ /* conformance print */
+ if (mod->implemented) {
+ ly_print(yo->out, " I");
+ } else {
+ ly_print(yo->out, " i");
+ }
+
+ /* module print */
+ ly_print(yo->out, " %s", mod->name);
+ if (mod->revision) {
+ ly_print(yo->out, "@%s", mod->revision);
+ }
+
+ /* submodules print */
+ if (mod->parsed && mod->parsed->includes) {
+ uint64_t u = 0;
+
+ ly_print(yo->out, " (");
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ ly_print(yo->out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
+ if (mod->parsed->includes[u].rev[0]) {
+ ly_print(yo->out, "@%s", mod->parsed->includes[u].rev);
+ }
+ }
+ ly_print(yo->out, ")");
+ }
+
+ /* finish the line */
+ ly_print(yo->out, "\n");
+ }
+
+ if (!has_modules) {
+ ly_print(yo->out, "\t(none)\n");
}
-cleanup:
- free_cmdline(argv);
+ ly_print_flush(yo->out);
+
+ return 0;
}
diff --git a/tools/lint/cmd_load.c b/tools/lint/cmd_load.c
index f5883e9..808c125 100644
--- a/tools/lint/cmd_load.c
+++ b/tools/lint/cmd_load.c
@@ -2,9 +2,10 @@
* @file cmd_load.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'load' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,6 +18,7 @@
#include "cmd.h"
+#include <assert.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
@@ -25,13 +27,15 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
void
cmd_load_help(void)
{
printf("Usage: load [-i] <module-name1>[@<revision>] [<module-name2>[@revision] ...]\n"
" Add a new module of the specified name, yanglint will find\n"
- " them in searchpaths. if the <revision> of the module not\n"
+ " them in searchpaths. If the <revision> of the module not\n"
" specified, the latest revision available is loaded.\n\n"
" -F FEATURES, --features=FEATURES\n"
" Features to support, default all in all implemented modules.\n"
@@ -39,101 +43,110 @@ cmd_load_help(void)
" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" <schema> module also implemented. If specified a second time,\n"
- " all the modules are set implemented.\n");
+ " all the modules are set implemented.\n"
+ " -X, --extended-leafref\n"
+ " Allow usage of deref() XPath function within leafref.\n");
}
-void
-cmd_load(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_load_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc, argc = 0;
int opt, opt_index;
struct option options[] = {
{"features", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"make-implemented", no_argument, NULL, 'i'},
+ {"extended-leafref", no_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
- uint16_t options_ctx = 0;
- const char *all_features[] = {"*", NULL};
- struct ly_set fset = {0};
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "F:hi", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_LOAD].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'F': /* --features */
- if (parse_features(optarg, &fset)) {
- goto cleanup;
+ if (parse_features(optarg, &yo->schema_features)) {
+ return 1;
}
break;
case 'h':
cmd_load_help();
- goto cleanup;
+ return 1;
case 'i': /* --make-implemented */
- if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
- options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
- options_ctx |= LY_CTX_ALL_IMPLEMENTED;
- } else {
- options_ctx |= LY_CTX_REF_IMPLEMENTED;
- }
+ yo_opt_update_make_implemented(yo);
+ break;
+
+ case 'X': /* --extended-leafref */
+ yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (argc == optind) {
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_load_dep(struct yl_opt *yo, int posc)
+{
+ if (yo->interactive && !posc) {
/* no argument */
- cmd_add_help();
- goto cleanup;
+ cmd_load_help();
+ return 1;
}
- if (!fset.count) {
+ if (!yo->schema_features.count) {
/* no features, enable all of them */
- options_ctx |= LY_CTX_ENABLE_IMP_FEATURES;
+ yo->ctx_options |= LY_CTX_ENABLE_IMP_FEATURES;
}
- if (options_ctx) {
- ly_ctx_set_options(*ctx, options_ctx);
- }
+ return 0;
+}
- for (int i = 0; i < argc - optind; i++) {
- /* process the schema module files */
- char *revision;
- const char **features = NULL;
+int
+cmd_load_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ const char *all_features[] = {"*", NULL};
+ char *revision;
+ const char **features = NULL;
- /* get revision */
- revision = strchr(argv[optind + i], '@');
- if (revision) {
- revision[0] = '\0';
- ++revision;
- }
+ assert(posv);
- /* get features list for this module */
- if (!fset.count) {
- features = all_features;
- } else {
- get_features(&fset, argv[optind + i], &features);
- }
+ if (yo->ctx_options) {
+ ly_ctx_set_options(*ctx, yo->ctx_options);
+ yo->ctx_options = 0;
+ }
- /* load the module */
- if (!ly_ctx_load_module(*ctx, argv[optind + i], revision, features)) {
- /* libyang printed the error messages */
- goto cleanup;
- }
+ /* get revision */
+ revision = strchr(posv, '@');
+ if (revision) {
+ revision[0] = '\0';
+ ++revision;
}
-cleanup:
- if (options_ctx) {
- ly_ctx_unset_options(*ctx, options_ctx);
+ /* get features list for this module */
+ if (!yo->schema_features.count) {
+ features = all_features;
+ } else {
+ get_features(&yo->schema_features, posv, &features);
}
- ly_set_erase(&fset, free_features);
- free_cmdline(argv);
+
+ /* load the module */
+ if (!ly_ctx_load_module(*ctx, posv, revision, features)) {
+ /* libyang printed the error messages */
+ return 1;
+ }
+
+ return 0;
}
diff --git a/tools/lint/cmd_print.c b/tools/lint/cmd_print.c
index c1a5359..ff5fb90 100644
--- a/tools/lint/cmd_print.c
+++ b/tools/lint/cmd_print.c
@@ -2,9 +2,10 @@
* @file cmd_print.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'print' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_print_help(void)
@@ -34,7 +36,8 @@ cmd_print_help(void)
printf("Usage: print [-f (yang | yin | tree [-q -P PATH -L LINE_LENGTH ] | info [-q -P PATH])]\n"
" [-o OUTFILE] [<module-name1>[@revision]] ...\n"
" Print a schema module. The <module-name> is not required\n"
- " only in case the -P option is specified.\n\n"
+ " only in case the -P option is specified. For yang, yin and tree\n"
+ " formats, a submodule can also be printed.\n\n"
" -f FORMAT, --format=FORMAT\n"
" Print the module in the specified FORMAT. If format not\n"
" specified, the 'tree' format is used.\n"
@@ -53,95 +56,10 @@ cmd_print_help(void)
" Write the output to OUTFILE instead of stdout.\n");
}
-static LY_ERR
-cmd_print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
-{
- LY_ERR erc;
- const struct lysp_submodule *submodule;
-
- submodule = revision ?
- ly_ctx_get_submodule(*ctx, name, revision) :
- ly_ctx_get_submodule_latest(*ctx, name);
-
- erc = submodule ?
- lys_print_submodule(out, submodule, format, line_length, options) :
- LY_ENOTFOUND;
-
- return erc;
-}
-
-static LY_ERR
-cmd_print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
-{
- LY_ERR erc;
- struct lys_module *module;
-
- module = revision ?
- ly_ctx_get_module(*ctx, name, revision) :
- ly_ctx_get_module_latest(*ctx, name);
-
- erc = module ?
- lys_print_module(out, module, format, line_length, options) :
- LY_ENOTFOUND;
-
- return erc;
-}
-
-static void
-cmd_print_modules(int argc, char **argv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
-{
- LY_ERR erc;
- char *name, *revision;
- ly_bool search_submodul;
- const int stop = argc - optind;
-
- for (int i = 0; i < stop; i++) {
- name = argv[optind + i];
- /* get revision */
- revision = strchr(name, '@');
- if (revision) {
- revision[0] = '\0';
- ++revision;
- }
-
- erc = cmd_print_module(out, ctx, name, revision, format, line_length, options);
-
- if (erc == LY_ENOTFOUND) {
- search_submodul = 1;
- erc = cmd_print_submodule(out, ctx, name, revision, format, line_length, options);
- } else {
- search_submodul = 0;
- }
-
- if (erc == LY_SUCCESS) {
- /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
- if ((format == LYS_OUT_TREE) && (i + 1 < stop)) {
- ly_print(out, "\n");
- }
- continue;
- } else if (erc == LY_ENOTFOUND) {
- if (revision) {
- YLMSG_E("No (sub)module \"%s\" in revision %s found.\n", name, revision);
- } else {
- YLMSG_E("No (sub)module \"%s\" found.\n", name);
- }
- break;
- } else {
- if (search_submodul) {
- YLMSG_E("Unable to print submodule %s.\n", name);
- } else {
- YLMSG_E("Unable to print module %s.\n", name);
- }
- break;
- }
- }
-}
-
-void
-cmd_print(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"format", required_argument, NULL, 'f'},
@@ -152,113 +70,230 @@ cmd_print(struct ly_ctx **ctx, const char *cmdline)
{"single-node", no_argument, NULL, 'q'},
{NULL, 0, NULL, 0}
};
- uint16_t options_print = 0;
- const char *node_path = NULL;
- LYS_OUTFORMAT format = LYS_OUT_TREE;
- struct ly_out *out = NULL;
- ly_bool out_stdout = 0;
- size_t line_length = 0;
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+
+ yo->schema_out_format = LYS_OUT_TREE;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "f:hL:o:P:q", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_PRINT].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'o': /* --output */
- if (out) {
- if (ly_out_filepath(out, optarg) != NULL) {
- YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
- goto cleanup;
- }
+ if (yo->out) {
+ YLMSG_E("Only a single output can be specified.");
+ return 1;
} else {
- if (ly_out_new_filepath(optarg, &out)) {
- YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
- goto cleanup;
+ if (ly_out_new_filepath(optarg, &yo->out)) {
+ YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
+ return 1;
}
}
break;
case 'f': /* --format */
- if (!strcasecmp(optarg, "yang")) {
- format = LYS_OUT_YANG;
- } else if (!strcasecmp(optarg, "yin")) {
- format = LYS_OUT_YIN;
- } else if (!strcasecmp(optarg, "info")) {
- format = LYS_OUT_YANG_COMPILED;
- } else if (!strcasecmp(optarg, "tree")) {
- format = LYS_OUT_TREE;
- } else {
- YLMSG_E("Unknown output format %s\n", optarg);
+ if (yl_opt_update_schema_out_format(optarg, yo)) {
cmd_print_help();
- goto cleanup;
+ return 1;
}
break;
case 'L': /* --tree-line-length */
- line_length = atoi(optarg);
+ yo->line_length = atoi(optarg);
break;
case 'P': /* --schema-node */
- node_path = optarg;
+ yo->schema_node_path = optarg;
break;
case 'q': /* --single-node */
- options_print |= LYS_PRINT_NO_SUBSTMT;
+ yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
break;
case 'h':
cmd_print_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_print_dep(struct yl_opt *yo, int posc)
+{
/* file name */
- if ((argc == optind) && !node_path) {
- YLMSG_E("Missing the name of the module to print.\n");
- goto cleanup;
+ if (yo->interactive && !posc && !yo->schema_node_path) {
+ YLMSG_E("Missing the name of the module to print.");
+ return 1;
}
- if ((format != LYS_OUT_TREE) && line_length) {
- YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
- goto cleanup;
+ if ((yo->schema_out_format != LYS_OUT_TREE) && yo->line_length) {
+ YLMSG_W("--tree-line-length take effect only in case of the tree output format.");
}
- if (!out) {
- if (ly_out_new_file(stdout, &out)) {
- YLMSG_E("Could not use stdout to print output.\n");
- goto cleanup;
+ if (!yo->out) {
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Could not use stdout to print output.");
}
- out_stdout = 1;
+ yo->out_stdout = 1;
}
- if (format == LYS_OUT_TREE) {
+ if (yo->schema_out_format == LYS_OUT_TREE) {
/* print tree from lysc_nodes */
- ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
+ yo->ctx_options |= LY_CTX_SET_PRIV_PARSED;
}
- if (node_path) {
- const struct lysc_node *node;
+ return 0;
+}
+
+static LY_ERR
+print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ const struct lysp_submodule *submodule;
- node = find_schema_path(*ctx, node_path);
+ submodule = revision ?
+ ly_ctx_get_submodule(*ctx, name, revision) :
+ ly_ctx_get_submodule_latest(*ctx, name);
+
+ erc = submodule ?
+ lys_print_submodule(out, submodule, format, line_length, options) :
+ LY_ENOTFOUND;
+
+ if (!erc) {
+ return 0;
+ } else if ((erc == LY_ENOTFOUND) && revision) {
+ YLMSG_E("No submodule \"%s\" found.", name);
+ } else {
+ YLMSG_E("Unable to print submodule %s.", name);
+ }
+
+ return erc;
+}
+
+static LY_ERR
+print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ struct lys_module *module;
+
+ module = revision ?
+ ly_ctx_get_module(*ctx, name, revision) :
+ ly_ctx_get_module_latest(*ctx, name);
+
+ erc = module ?
+ lys_print_module(out, module, format, line_length, options) :
+ LY_ENOTFOUND;
+
+ if (!erc) {
+ return 0;
+ } else if ((erc == LY_ENOTFOUND) && revision) {
+ YLMSG_E("No module \"%s\" found.", name);
+ } else {
+ YLMSG_E("Unable to print module %s.", name);
+ }
+
+ return erc;
+}
+
+static int
+cmd_print_module(const char *posv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format,
+ size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ char *name = NULL, *revision;
+
+ name = strdup(posv);
+ /* get revision */
+ revision = strchr(name, '@');
+ if (revision) {
+ revision[0] = '\0';
+ ++revision;
+ }
+
+ erc = print_module(out, ctx, name, revision, format, line_length, options);
+
+ if (erc == LY_ENOTFOUND) {
+ erc = print_submodule(out, ctx, name, revision, format, line_length, options);
+ }
+
+ free(name);
+ return erc;
+}
+
+/**
+ * @brief Print schema node path.
+ *
+ * @param[in] ctx Context for libyang.
+ * @param[in] yo Context for yanglint.
+ * @return 0 on success.
+ */
+static int
+print_node(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+ const struct lysc_node *node;
+ uint32_t temp_lo = 0;
+
+ if (yo->interactive) {
+ /* Use the same approach as for completion. */
+ node = find_schema_path(ctx, yo->schema_node_path);
if (!node) {
- YLMSG_E("The requested schema node \"%s\" does not exists.\n", node_path);
- goto cleanup;
+ YLMSG_E("The requested schema node \"%s\" does not exists.", yo->schema_node_path);
+ return 1;
}
-
- if (lys_print_node(out, node, format, 0, options_print)) {
- YLMSG_E("Unable to print schema node %s.\n", node_path);
- goto cleanup;
+ } else {
+ /* turn off logging so that the message is not repeated */
+ ly_temp_log_options(&temp_lo);
+ /* search operation input */
+ node = lys_find_path(ctx, NULL, yo->schema_node_path, 0);
+ if (!node) {
+ /* restore logging so an error may be displayed */
+ ly_temp_log_options(NULL);
+ /* search operation output */
+ node = lys_find_path(ctx, NULL, yo->schema_node_path, 1);
+ if (!node) {
+ YLMSG_E("Invalid schema path.");
+ return 1;
+ }
}
+ }
+
+ if (lys_print_node(yo->out, node, yo->schema_out_format, yo->line_length, yo->schema_print_options)) {
+ YLMSG_E("Unable to print schema node %s.", yo->schema_node_path);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ int rc = 0;
+
+ if (yo->ctx_options & LY_CTX_SET_PRIV_PARSED) {
+ /* print tree from lysc_nodes */
+ ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
+ }
+
+ if (yo->schema_node_path) {
+ rc = print_node(*ctx, yo);
+ } else if (!yo->interactive && yo->submodule) {
+ rc = print_submodule(yo->out, ctx, yo->submodule, NULL, yo->schema_out_format, yo->line_length,
+ yo->schema_print_options);
} else {
- cmd_print_modules(argc, argv, out, ctx, format, line_length, options_print);
- goto cleanup;
+ rc = cmd_print_module(posv, yo->out, ctx, yo->schema_out_format, yo->line_length, yo->schema_print_options);
+ if (!yo->last_one && (yo->schema_out_format == LYS_OUT_TREE)) {
+ ly_print(yo->out, "\n");
+ }
}
-cleanup:
- free_cmdline(argv);
- ly_out_free(out, NULL, out_stdout ? 0 : 1);
+ return rc;
}
diff --git a/tools/lint/cmd_searchpath.c b/tools/lint/cmd_searchpath.c
index 529e05d..a6aeacf 100644
--- a/tools/lint/cmd_searchpath.c
+++ b/tools/lint/cmd_searchpath.c
@@ -2,9 +2,10 @@
* @file cmd_searchpath.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'searchpath' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,51 +25,62 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_searchpath_help(void)
{
printf("Usage: searchpath [--clear] [<modules-dir-path> ...]\n"
- " Set paths of directories where to search for imports and\n"
- " includes of the schema modules. The current working directory\n"
- " and the path of the module being added is used implicitly.\n"
- " The 'load' command uses these paths to search even for the\n"
- " schema modules to be loaded.\n");
+ " Set paths of directories where to search for imports and includes\n"
+ " of the schema modules. Subdirectories are also searched. The current\n"
+ " working directory and the path of the module being added is used implicitly.\n"
+ " The 'load' command uses these paths to search even for the schema modules\n"
+ " to be loaded.\n");
}
-void
-cmd_searchpath(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_searchpath_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"clear", no_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
- int8_t cleared = 0;
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "ch", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_SEARCHPATH].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'c':
- ly_ctx_unset_searchdir(*ctx, NULL);
- cleared = 1;
+ yo->searchdir_unset = 1;
break;
case 'h':
cmd_searchpath_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (!cleared && (argc == optind)) {
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_searchpath_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ int rc = 0;
+
+ if (yo->searchdir_unset) {
+ ly_ctx_unset_searchdir(*ctx, NULL);
+ } else if (!yo->searchdir_unset && !posv) {
/* no argument - print the paths */
const char * const *dirs = ly_ctx_get_searchdirs(*ctx);
@@ -76,15 +88,9 @@ cmd_searchpath(struct ly_ctx **ctx, const char *cmdline)
for (uint32_t i = 0; dirs[i]; ++i) {
printf(" %s\n", dirs[i]);
}
- goto cleanup;
- }
-
- for (int i = 0; i < argc - optind; i++) {
- if (ly_ctx_set_searchdir(*ctx, argv[optind + i])) {
- goto cleanup;
- }
+ } else {
+ rc = ly_ctx_set_searchdir(*ctx, posv);
}
-cleanup:
- free_cmdline(argv);
+ return rc;
}
diff --git a/tools/lint/cmd_verb.c b/tools/lint/cmd_verb.c
new file mode 100644
index 0000000..33c8d1e
--- /dev/null
+++ b/tools/lint/cmd_verb.c
@@ -0,0 +1,114 @@
+/**
+ * @file cmd_verb.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'verb' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2023-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+void
+cmd_verb_help(void)
+{
+ printf("Usage: verb (error | warning | verbose | debug)\n");
+}
+
+int
+cmd_verb_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_VERB].optstring, options, &opt_index)) != -1) {
+ if (opt == 'h') {
+ cmd_verb_help();
+ return 1;
+ } else {
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_verb_dep(struct yl_opt *yo, int posc)
+{
+ (void) yo;
+
+ if (posc > 1) {
+ YLMSG_E("Only a single verbosity level can be set.");
+ cmd_verb_help();
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_verb_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) ctx, (void) yo;
+
+ if (!posv) {
+ /* no argument - print current value */
+ LY_LOG_LEVEL level = ly_log_level(LY_LLERR);
+
+ ly_log_level(level);
+ printf("Current verbosity level: ");
+ if (level == LY_LLERR) {
+ printf("error\n");
+ } else if (level == LY_LLWRN) {
+ printf("warning\n");
+ } else if (level == LY_LLVRB) {
+ printf("verbose\n");
+ } else if (level == LY_LLDBG) {
+ printf("debug\n");
+ }
+ return 0;
+ } else {
+ if (!strcasecmp("error", posv) || !strcmp("0", posv)) {
+ ly_log_level(LY_LLERR);
+ } else if (!strcasecmp("warning", posv) || !strcmp("1", posv)) {
+ ly_log_level(LY_LLWRN);
+ } else if (!strcasecmp("verbose", posv) || !strcmp("2", posv)) {
+ ly_log_level(LY_LLVRB);
+ } else if (!strcasecmp("debug", posv) || !strcmp("3", posv)) {
+ ly_log_level(LY_LLDBG);
+ } else {
+ YLMSG_E("Unknown verbosity \"%s\".", posv);
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/lint/common.c b/tools/lint/common.c
index fc9b1cd..d86c54f 100644
--- a/tools/lint/common.c
+++ b/tools/lint/common.c
@@ -1,9 +1,10 @@
/**
* @file common.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -19,7 +20,7 @@
#include <assert.h>
#include <errno.h>
-#include <getopt.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,6 +28,21 @@
#include "compat.h"
#include "libyang.h"
+#include "plugins_exts.h"
+#include "yl_opt.h"
+
+void
+yl_log(ly_bool err, const char *format, ...)
+{
+ char msg[256];
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(msg, 256, format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "YANGLINT[%c]: %s\n", err ? 'E' : 'W', msg);
+}
int
parse_schema_path(const char *path, char **dir, char **module)
@@ -38,6 +54,7 @@ parse_schema_path(const char *path, char **dir, char **module)
/* split the path to dirname and basename for further work */
*dir = strdup(path);
+ /* FIXME: this is broken on Windows */
*module = strrchr(*dir, '/');
if (!(*module)) {
*module = *dir;
@@ -65,722 +82,99 @@ get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_
/* check that the filepath exists and is a regular file */
if (stat(filepath, &st) == -1) {
- YLMSG_E("Unable to use input filepath (%s) - %s.\n", filepath, strerror(errno));
+ YLMSG_E("Unable to use input filepath (%s) - %s.", filepath, strerror(errno));
return -1;
}
if (!S_ISREG(st.st_mode)) {
- YLMSG_E("Provided input file (%s) is not a regular file.\n", filepath);
+ YLMSG_E("Provided input file (%s) is not a regular file.", filepath);
return -1;
}
- if ((format_schema && !*format_schema) || (format_data && !*format_data)) {
- /* get the file format */
- if (get_format(filepath, format_schema, format_data)) {
- return -1;
- }
- }
-
- if (ly_in_new_filepath(filepath, 0, in)) {
- YLMSG_E("Unable to process input file.\n");
+ if (get_format(filepath, format_schema, format_data)) {
return -1;
}
- return 0;
-}
-
-void
-free_features(void *flist)
-{
- struct schema_features *rec = (struct schema_features *)flist;
-
- if (rec) {
- free(rec->mod_name);
- if (rec->features) {
- for (uint32_t u = 0; rec->features[u]; ++u) {
- free(rec->features[u]);
- }
- free(rec->features);
- }
- free(rec);
- }
-}
-
-void
-get_features(struct ly_set *fset, const char *module, const char ***features)
-{
- /* get features list for this module */
- for (uint32_t u = 0; u < fset->count; ++u) {
- struct schema_features *sf = (struct schema_features *)fset->objs[u];
-
- if (!strcmp(module, sf->mod_name)) {
- /* matched module - explicitly set features */
- *features = (const char **)sf->features;
- sf->applied = 1;
- return;
- }
- }
-
- /* features not set so disable all */
- *features = NULL;
-}
-
-int
-parse_features(const char *fstring, struct ly_set *fset)
-{
- struct schema_features *rec;
- uint32_t count;
- char *p, **fp;
-
- rec = calloc(1, sizeof *rec);
- if (!rec) {
- YLMSG_E("Unable to allocate features information record (%s).\n", strerror(errno));
+ if (in && ly_in_new_filepath(filepath, 0, in)) {
+ YLMSG_E("Unable to process input file.");
return -1;
}
- if (ly_set_add(fset, rec, 1, NULL)) {
- YLMSG_E("Unable to store features information (%s).\n", strerror(errno));
- free(rec);
- return -1;
- }
-
- /* fill the record */
- p = strchr(fstring, ':');
- if (!p) {
- YLMSG_E("Invalid format of the features specification (%s).\n", fstring);
- return -1;
- }
- rec->mod_name = strndup(fstring, p - fstring);
-
- count = 0;
- while (p) {
- size_t len = 0;
- char *token = p + 1;
-
- p = strchr(token, ',');
- if (!p) {
- /* the last item, if any */
- len = strlen(token);
- } else {
- len = p - token;
- }
-
- if (len) {
- fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
- if (!fp) {
- YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
- return -1;
- }
- rec->features = fp;
- fp = &rec->features[count++]; /* array item to set */
- (*fp) = strndup(token, len);
- }
- }
-
- /* terminating NULL */
- fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
- if (!fp) {
- YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
- return -1;
- }
- rec->features = fp;
- rec->features[count++] = NULL;
return 0;
}
-struct cmdline_file *
-fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
+LYS_INFORMAT
+get_schema_format(const char *filename)
{
- struct cmdline_file *rec;
-
- rec = malloc(sizeof *rec);
- if (!rec) {
- YLMSG_E("Allocating memory for data file information failed.\n");
- return NULL;
- }
- rec->in = in;
- rec->path = path;
- rec->format = format;
-
- if (set && ly_set_add(set, rec, 1, NULL)) {
- free(rec);
- YLMSG_E("Storing data file information failed.\n");
- return NULL;
- }
-
- return rec;
-}
-
-int
-collect_features(const struct lys_module *mod, struct ly_set *set)
-{
- struct lysp_feature *f = NULL;
- uint32_t idx = 0;
-
- while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
- if (ly_set_add(set, (void *)f->name, 1, NULL)) {
- YLMSG_E("Memory allocation failed.\n");
- ly_set_erase(set, NULL);
- return 1;
- }
- }
-
- return 0;
-}
-
-void
-print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set)
-{
- size_t max_len;
- uint32_t j;
- const char *name;
-
- /* header */
- ly_print(out, "%s:\n", mod->name);
-
- /* no features */
- if (!set->count) {
- ly_print(out, "\t(none)\n\n");
- return;
- }
-
- /* get max len, so the statuses of all the features will be aligned */
- max_len = 0;
- for (j = 0; j < set->count; ++j) {
- name = set->objs[j];
- if (strlen(name) > max_len) {
- max_len = strlen(name);
- }
- }
-
- /* print features */
- for (j = 0; j < set->count; ++j) {
- name = set->objs[j];
- ly_print(out, "\t%-*s (%s)\n", (int)max_len, name, lys_feature_value(mod, name) ? "off" : "on");
- }
-
- ly_print(out, "\n");
-}
-
-int
-generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param)
-{
- uint32_t j;
- /*
- * features_len - length of all the features in the current module
- * added_len - length of a string to be added, = features_len + extra necessary length
- * param_len - length of the parameter before appending new string
- */
- size_t features_len, added_len, param_len;
- char *tmp;
-
- features_len = 0;
- for (j = 0; j < set->count; j++) {
- features_len += strlen(set->objs[j]);
- }
-
- if (j == 0) {
- /* no features */
- added_len = strlen("-F ") + strlen(mod->name) + strlen(":");
- } else {
- /* j = comma count, -1 because of trailing comma */
- added_len = strlen("-F ") + strlen(mod->name) + strlen(":") + features_len + j - 1;
- }
-
- /* to avoid strlen(NULL) if this is the first call */
- param_len = 0;
- if (*features_param) {
- param_len = strlen(*features_param);
- }
-
- /* +1 because of white space at the beginning */
- tmp = realloc(*features_param, param_len + added_len + 1 + 1);
- if (!tmp) {
- goto error;
- } else {
- *features_param = tmp;
- }
- sprintf(*features_param + param_len, " -F %s:", mod->name);
-
- for (j = 0; j < set->count; j++) {
- strcat(*features_param, set->objs[j]);
- /* no trailing comma */
- if (j != (set->count - 1)) {
- strcat(*features_param, ",");
- }
- }
-
- return 0;
-
-error:
- YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
- return 1;
-}
-
-int
-print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param)
-{
- int ret = 0;
- uint32_t i = 0;
- struct lys_module *mod;
- struct ly_set set = {0};
-
- while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
- /* only care about implemented modules */
- if (!mod->implemented) {
- continue;
- }
-
- /* always erase the set, so the previous module's features don't carry over to the next module's features */
- ly_set_erase(&set, NULL);
-
- if (collect_features(mod, &set)) {
- ret = 1;
- goto cleanup;
- }
-
- if (generate_features && generate_features_output(mod, &set, features_param)) {
- ret = 1;
- goto cleanup;
- }
- print_features(out, mod, &set);
- }
-
-cleanup:
- ly_set_erase(&set, NULL);
- return ret;
-}
-
-void
-free_cmdline_file(void *cmdline_file)
-{
- struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
-
- if (rec) {
- ly_in_free(rec->in, 1);
- free(rec);
- }
-}
-
-void
-free_cmdline(char *argv[])
-{
- if (argv) {
- free(argv[0]);
- free(argv);
- }
-}
-
-int
-parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
-{
- int count;
- char **vector;
char *ptr;
- char qmark = 0;
-
- assert(cmdline);
- assert(argc_p);
- assert(argv_p);
-
- /* init */
- optind = 0; /* reinitialize getopt() */
- count = 1;
- vector = malloc((count + 1) * sizeof *vector);
- vector[0] = strdup(cmdline);
-
- /* command name */
- strtok(vector[0], " ");
-
- /* arguments */
- while ((ptr = strtok(NULL, " "))) {
- size_t len;
- void *r;
-
- len = strlen(ptr);
-
- if (qmark) {
- /* still in quotated text */
- /* remove NULL termination of the previous token since it is not a token,
- * but a part of the quotation string */
- ptr[-1] = ' ';
-
- if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
- /* end of quotation */
- qmark = 0;
- /* shorten the argument by the terminating quotation mark */
- ptr[len - 1] = '\0';
- }
- continue;
- }
- /* another token in cmdline */
- ++count;
- r = realloc(vector, (count + 1) * sizeof *vector);
- if (!r) {
- YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
- free(vector);
- return -1;
- }
- vector = r;
- vector[count - 1] = ptr;
-
- if ((ptr[0] == '"') || (ptr[0] == '\'')) {
- /* remember the quotation mark to identify end of quotation */
- qmark = ptr[0];
-
- /* move the remembered argument after the quotation mark */
- ++vector[count - 1];
-
- /* check if the quotation is terminated within this token */
- if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
- /* end of quotation */
- qmark = 0;
- /* shorten the argument by the terminating quotation mark */
- ptr[len - 1] = '\0';
- }
+ if ((ptr = strrchr(filename, '.')) != NULL) {
+ ++ptr;
+ if (!strcmp(ptr, "yang")) {
+ return LYS_IN_YANG;
+ } else if (!strcmp(ptr, "yin")) {
+ return LYS_IN_YIN;
+ } else {
+ return LYS_IN_UNKNOWN;
}
+ } else {
+ return LYS_IN_UNKNOWN;
}
- vector[count] = NULL;
-
- *argc_p = count;
- *argv_p = vector;
-
- return 0;
}
-int
-get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
+LYD_FORMAT
+get_data_format(const char *filename)
{
char *ptr;
- LYS_INFORMAT informat_s;
- LYD_FORMAT informat_d;
- /* get the file format */
if ((ptr = strrchr(filename, '.')) != NULL) {
++ptr;
- if (!strcmp(ptr, "yang")) {
- informat_s = LYS_IN_YANG;
- informat_d = 0;
- } else if (!strcmp(ptr, "yin")) {
- informat_s = LYS_IN_YIN;
- informat_d = 0;
- } else if (!strcmp(ptr, "xml")) {
- informat_s = 0;
- informat_d = LYD_XML;
+ if (!strcmp(ptr, "xml")) {
+ return LYD_XML;
} else if (!strcmp(ptr, "json")) {
- informat_s = 0;
- informat_d = LYD_JSON;
+ return LYD_JSON;
} else if (!strcmp(ptr, "lyb")) {
- informat_s = 0;
- informat_d = LYD_LYB;
+ return LYD_LYB;
} else {
- YLMSG_E("Input file \"%s\" in an unknown format \"%s\".\n", filename, ptr);
- return 0;
+ return LYD_UNKNOWN;
}
} else {
- YLMSG_E("Input file \"%s\" without file extension - unknown format.\n", filename);
- return 1;
+ return LYD_UNKNOWN;
}
-
- if (informat_d) {
- if (!data) {
- YLMSG_E("Input file \"%s\" not expected to contain data instances (unexpected format).\n", filename);
- return 2;
- }
- (*data) = informat_d;
- } else if (informat_s) {
- if (!schema) {
- YLMSG_E("Input file \"%s\" not expected to contain schema definition (unexpected format).\n", filename);
- return 3;
- }
- (*schema) = informat_s;
- }
-
- return 0;
}
int
-print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
+get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
{
- struct lyd_node *ylib;
- uint32_t idx = 0, has_modules = 0;
- const struct lys_module *mod;
-
- if (outformat != LYD_UNKNOWN) {
- if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
- YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, use an option to add it internally.\n");
- return 1;
- }
-
- lyd_print_all(out, ylib, outformat, 0);
- lyd_free_all(ylib);
- return 0;
- }
-
- /* iterate schemas in context and provide just the basic info */
- ly_print(out, "List of the loaded models:\n");
- while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
- has_modules++;
-
- /* conformance print */
- if (mod->implemented) {
- ly_print(out, " I");
- } else {
- ly_print(out, " i");
- }
-
- /* module print */
- ly_print(out, " %s", mod->name);
- if (mod->revision) {
- ly_print(out, "@%s", mod->revision);
- }
+ LYS_INFORMAT schema;
+ LYD_FORMAT data;
- /* submodules print */
- if (mod->parsed && mod->parsed->includes) {
- uint64_t u = 0;
+ schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
+ data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
- ly_print(out, " (");
- LY_ARRAY_FOR(mod->parsed->includes, u) {
- ly_print(out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
- if (mod->parsed->includes[u].rev[0]) {
- ly_print(out, "@%s", mod->parsed->includes[u].rev);
- }
- }
- ly_print(out, ")");
- }
-
- /* finish the line */
- ly_print(out, "\n");
+ if (!schema) {
+ schema = get_schema_format(filepath);
}
-
- if (!has_modules) {
- ly_print(out, "\t(none)\n");
+ if (!data) {
+ data = get_data_format(filepath);
}
- ly_print_flush(out);
- return 0;
-}
-
-int
-evaluate_xpath(const struct lyd_node *tree, const char *xpath)
-{
- struct ly_set *set = NULL;
-
- if (lyd_find_xpath(tree, xpath, &set)) {
+ if (!schema && !data) {
+ YLMSG_E("Input schema format for %s file not recognized.", filepath);
+ return -1;
+ } else if (!data && !schema) {
+ YLMSG_E("Input data format for %s file not recognized.", filepath);
return -1;
}
+ assert(schema || data);
- /* print result */
- printf("XPath \"%s\" evaluation result:\n", xpath);
- if (!set->count) {
- printf("\tEmpty\n");
- } else {
- for (uint32_t u = 0; u < set->count; ++u) {
- struct lyd_node *node = (struct lyd_node *)set->objs[u];
-
- printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
- if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
- printf(" (value: \"%s\")\n", lyd_get_value(node));
- } else if (node->schema->nodetype == LYS_LIST) {
- printf(" (");
- for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
- printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
- key->schema->name, lyd_get_value(key));
- }
- printf(")\n");
- }
- }
- }
-
- ly_set_free(set, NULL);
- return 0;
-}
-
-LY_ERR
-process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
- uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
- struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
- char *path = NULL;
- struct ly_set *set = NULL;
-
- /* additional operational datastore */
- if (operational_f && operational_f->in) {
- ret = lyd_parse_data(ctx, NULL, operational_f->in, operational_f->format, LYD_PARSE_ONLY, 0, &oper_tree);
- if (ret) {
- YLMSG_E("Failed to parse operational datastore file \"%s\".\n", operational_f->path);
- goto cleanup;
- }
- }
-
- for (uint32_t u = 0; u < inputs->count; ++u) {
- struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
-
- switch (data_type) {
- case LYD_TYPE_DATA_YANG:
- ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, options_parse, options_validate, &tree);
- break;
- case LYD_TYPE_RPC_YANG:
- case LYD_TYPE_REPLY_YANG:
- case LYD_TYPE_NOTIF_YANG:
- ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &tree, &op);
- break;
- case LYD_TYPE_RPC_NETCONF:
- case LYD_TYPE_NOTIF_NETCONF:
- ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &envp, &op);
-
- /* adjust pointers */
- for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
- break;
- case LYD_TYPE_REPLY_NETCONF:
- /* parse source RPC operation */
- assert(rpc_f && rpc_f->in);
- ret = lyd_parse_op(ctx, NULL, rpc_f->in, rpc_f->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
- if (ret) {
- YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".\n", rpc_f->path);
- goto cleanup;
- }
-
- /* adjust pointers */
- for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
-
- /* free input */
- lyd_free_siblings(lyd_child(op));
-
- /* we do not care */
- lyd_free_all(envp);
- envp = NULL;
-
- ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, data_type, &envp, NULL);
- break;
- default:
- YLMSG_E("Internal error (%s:%d).\n", __FILE__, __LINE__);
- goto cleanup;
- }
-
- if (ret) {
- YLMSG_E("Failed to parse input data file \"%s\".\n", input_f->path);
- goto cleanup;
- }
-
- if (merge) {
- /* merge the data so far parsed for later validation and print */
- if (!merged_tree) {
- merged_tree = tree;
- } else {
- ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
- if (ret) {
- YLMSG_E("Merging %s with previous data failed.\n", input_f->path);
- goto cleanup;
- }
- }
- tree = NULL;
- } else if (format) {
- /* print */
- switch (data_type) {
- case LYD_TYPE_DATA_YANG:
- lyd_print_all(out, tree, format, options_print);
- break;
- case LYD_TYPE_RPC_YANG:
- case LYD_TYPE_REPLY_YANG:
- case LYD_TYPE_NOTIF_YANG:
- case LYD_TYPE_RPC_NETCONF:
- case LYD_TYPE_NOTIF_NETCONF:
- lyd_print_tree(out, tree, format, options_print);
- break;
- case LYD_TYPE_REPLY_NETCONF:
- /* just the output */
- lyd_print_tree(out, lyd_child(tree), format, options_print);
- break;
- default:
- assert(0);
- }
- } else {
- /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
- switch (data_type) {
- case LYD_TYPE_DATA_YANG:
- /* already validated */
- break;
- case LYD_TYPE_RPC_YANG:
- case LYD_TYPE_RPC_NETCONF:
- ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
- break;
- case LYD_TYPE_REPLY_YANG:
- case LYD_TYPE_REPLY_NETCONF:
- ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
- break;
- case LYD_TYPE_NOTIF_YANG:
- case LYD_TYPE_NOTIF_NETCONF:
- ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
- break;
- default:
- assert(0);
- }
- if (ret) {
- if (operational_f->path) {
- YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".\n",
- input_f->path, operational_f->path);
- } else {
- YLMSG_E("Failed to validate input data file \"%s\".\n", input_f->path);
- }
- goto cleanup;
- }
-
- if (op && oper_tree && lyd_parent(op)) {
- /* check operation parent existence */
- path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
- if (!path) {
- ret = LY_EMEM;
- goto cleanup;
- }
- if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
- goto cleanup;
- }
- if (!set->count) {
- YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
- ret = LY_EVALID;
- goto cleanup;
- }
- }
- }
-
- /* next iter */
- lyd_free_all(tree);
- tree = NULL;
- lyd_free_all(envp);
- envp = NULL;
+ if (schema_form) {
+ *schema_form = schema;
}
-
- if (merge) {
- /* validate the merged result */
- ret = lyd_validate_all(&merged_tree, ctx, LYD_VALIDATE_PRESENT, NULL);
- if (ret) {
- YLMSG_E("Merged data are not valid.\n");
- goto cleanup;
- }
-
- if (format) {
- /* and print it */
- lyd_print_all(out, merged_tree, format, options_print);
- }
-
- for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
- if (evaluate_xpath(merged_tree, (const char *)xpaths->objs[u])) {
- goto cleanup;
- }
- }
+ if (data_form) {
+ *data_form = data;
}
-cleanup:
- lyd_free_all(tree);
- lyd_free_all(envp);
- lyd_free_all(merged_tree);
- lyd_free_all(oper_tree);
- free(path);
- ly_set_free(set, NULL);
- return ret;
+ return 0;
}
const struct lysc_node *
@@ -812,7 +206,7 @@ find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
/* - 1 because module_name_end points to ':' */
module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
if (!module_name) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
parent_node = NULL;
goto cleanup;
}
@@ -866,3 +260,42 @@ cleanup:
free(module_name);
return parent_node;
}
+
+LY_ERR
+ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *data = NULL;
+
+ ctx = ext->module->ctx;
+ if (user_data) {
+ lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+searchpath_strcat(char **searchpaths, const char *path)
+{
+ uint64_t len;
+ char *new;
+
+ if (!(*searchpaths)) {
+ *searchpaths = strdup(path);
+ return LY_SUCCESS;
+ }
+
+ len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
+ new = realloc(*searchpaths, sizeof(char) * len + 1);
+ if (!new) {
+ return LY_EMEM;
+ }
+ strcat(new, PATH_SEPARATOR);
+ strcat(new, path);
+ *searchpaths = new;
+
+ return LY_SUCCESS;
+}
diff --git a/tools/lint/common.h b/tools/lint/common.h
index 7c6a8ad..7c50e72 100644
--- a/tools/lint/common.h
+++ b/tools/lint/common.h
@@ -1,9 +1,10 @@
/**
* @file common.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - common functions and definitions for both interactive and non-interactive mode.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -33,16 +34,21 @@
#define YL_DEFAULT_DATA_PARSE_OPTIONS LYD_PARSE_STRICT
/**
+ * @brief Default data validation flags.
+ */
+#define YL_DEFAULT_DATA_VALIDATE_OPTIONS LYD_VALIDATE_MULTI_ERROR
+
+/**
* @brief log error message
*/
#define YLMSG_E(...) \
- fprintf(stderr, "YANGLINT[E]: " __VA_ARGS__)
+ yl_log(1, __VA_ARGS__);
/**
* @brief log warning message
*/
#define YLMSG_W(...) \
- fprintf(stderr, "YANGLINT[W]: " __VA_ARGS__)
+ yl_log(0, __VA_ARGS__);
#ifndef _WIN32
# define PATH_SEPARATOR ":"
@@ -50,90 +56,16 @@
# define PATH_SEPARATOR ";"
#endif
-/**
- * @brief Storage for the list of the features (their names) in a specific YANG module.
- */
-struct schema_features {
- char *mod_name;
- char **features;
- ly_bool applied;
-};
-
-/**
- * @brief Data connected with a file provided on a command line as a file path.
- */
-struct cmdline_file {
- struct ly_in *in;
- const char *path;
- LYD_FORMAT format;
-};
-
-/**
- * @brief Free the schema features list (struct schema_features *)
- * @param[in,out] flist The (struct schema_features *) to free.
- */
-void free_features(void *flist);
-
-/**
- * @brief Get the list of features connected with the specific YANG module.
- *
- * @param[in] fset The set of features information (struct schema_features *).
- * @param[in] module Name of the YANG module which features should be found.
- * @param[out] features Pointer to the list of features being returned.
- */
-void get_features(struct ly_set *fset, const char *module, const char ***features);
-
-/**
- * @brief Parse features being specified for the specific YANG module.
- *
- * Format of the input @p fstring is as follows: <module_name>:[<feature>,]*
- *
- * @param[in] fstring Input string to be parsed.
- * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
- */
-int parse_features(const char *fstring, struct ly_set *fset);
-
-/**
- * @brief Collect all features of a module.
- *
- * @param[in] mod Module to be searched for features.
- * @param[out] set Set in which the features will be stored.
- * @return 0 on success.
- * @return 1 on error.
- */
-int collect_features(const struct lys_module *mod, struct ly_set *set);
-
-/**
- * @brief Print all features of a single module.
- *
- * @param[in] out The output handler for printing.
- * @param[in] mod Module which contains the features.
- * @param[in] set Set which holds the features.
- */
-void print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set);
-
-/**
- * @brief Generate a string, which will contain features paramater.
- *
- * @param[in] mod Module, for which the string will be generated.
- * @param[in] set Set containing the features.
- * @param[out] features_param String which will contain the output.
- * @return 0 on success.
- * @return 1 on error.
- */
-int generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param);
+struct cmdline_file;
/**
- * @brief Print all features of all implemented modules.
+ * @brief Log a yanglint message.
*
- * @param[in] out The output handler for printing.
- * @param[in] ctx Libyang context.
- * @param[in] generate_features Flag expressing whether to generate features parameter.
- * @param[out] features_param String, which will contain the output if the above flag is set.
- * @return 0 on success.
- * @return 1 on error.
+ * @param[in] err Whether the message is an error or a warning.
+ * @param[in] format Message format.
+ * @param[in] ... Format arguments.
*/
-int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param);
+void yl_log(ly_bool err, const char *format, ...);
/**
* @brief Parse path of a schema module file into the directory and module name.
@@ -141,7 +73,7 @@ int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool gen
* @param[in] path Schema module file path to be parsed.
* @param[out] dir Pointer to the directory path where the file resides. Caller is expected to free the returned string.
* @param[out] module Pointer to the name of the module (without file suffixes or revision information) specified by the
- * @path. Caller is expected to free the returned string.
+ * @p path. Caller is expected to free the returned string.
* @return 0 on success
* @return -1 on error
*/
@@ -158,100 +90,69 @@ int parse_schema_path(const char *path, char **dir, char **module);
* prohibited and such files are refused.
* @param[out] format_data Format of the data detected from the file name. If NULL specified, the data formats are
* prohibited and such files are refused.
- * @param[out] in Created input handler referring the file behind the @p filepath.
+ * @param[out] in Created input handler referring the file behind the @p filepath. Can be NULL.
* @return 0 on success.
* @return -1 on failure.
*/
int get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in);
/**
- * @brief Free the command line file data (struct cmdline_file *)
- * @param[in,out] cmdline_file The (struct cmdline_file *) to free.
- */
-void free_cmdline_file(void *cmdline_file);
-
-/**
- * @brief Create and fill the command line file data (struct cmdline_file *).
- * @param[in] set Optional parameter in case the record is supposed to be added into a set.
- * @param[in] in Input file handler.
- * @param[in] path Filepath of the file.
- * @param[in] format Format of the data file.
- * @return The created command line file structure.
- * @return NULL on failure
- */
-struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
-
-/**
- * @brief Helper function to prepare argc, argv pair from a command line string.
+ * @brief Get schema format of the @p filename's content according to the @p filename's suffix.
*
- * @param[in] cmdline Complete command line string.
- * @param[out] argc_p Pointer to store argc value.
- * @param[out] argv_p Pointer to store argv vector.
- * @return 0 on success, non-zero on failure.
+ * @param[in] filename Name of the file to examine.
+ * @return Detected schema input format.
*/
-int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
+LYS_INFORMAT get_schema_format(const char *filename);
/**
- * @brief Destructor for the argument vector prepared by ::parse_cmdline().
+ * @brief Get data format of the @p filename's content according to the @p filename's suffix.
*
- * @param[in,out] argv Argument vector to destroy.
+ * @param[in] filename Name of the file to examine.
+ * @return Detected data input format.
*/
-void free_cmdline(char *argv[]);
+LYD_FORMAT get_data_format(const char *filename);
/**
- * @brief Get expected format of the @p filename's content according to the @p filename's suffix.
- * @param[in] filename Name of the file to examine.
- * @param[out] schema Pointer to a variable to store the expected input schema format. Do not provide the pointer in case a
- * schema format is not expected.
- * @param[out] data Pointer to a variable to store the expected input data format. Do not provide the pointer in case a data
- * format is not expected.
+ * @brief Get format of the @p filename's content according to the @p filename's suffix.
+ *
+ * Either the @p schema or @p data parameter is set.
+ *
+ * @param[in] filepath Name of the file to examine.
+ * @param[out] schema_form Pointer to a variable to store the input schema format.
+ * @param[out] data_form Pointer to a variable to store the expected input data format.
* @return zero in case a format was successfully detected.
* @return nonzero in case it is not possible to get valid format from the @p filename.
*/
-int get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data);
+int get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form);
/**
- * @brief Print list of schemas in the context.
+ * @brief Get the node specified by the path.
*
- * @param[in] out Output handler where to print.
- * @param[in] ctx Context to print.
- * @param[in] outformat Optional output format. If not specified (:LYD_UNKNOWN), a simple list with single module per line
- * is printed. Otherwise, the ietf-yang-library data are printed in the specified format.
- * @return zero in case the data successfully printed.
- * @return nonzero in case of error.
+ * @param[in] ctx libyang context with schema.
+ * @param[in] schema_path Path to the wanted node.
+ * @return Pointer to the schema node specified by the path on success, NULL otherwise.
*/
-int print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
+const struct lysc_node *find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
/**
- * @brief Process the input data files - parse, validate and print according to provided options.
+ * @brief General callback providing run-time extension instance data.
*
- * @param[in] ctx libyang context with schema.
- * @param[in] data_type The type of data in the input files.
- * @param[in] merge Flag if the data should be merged before validation.
- * @param[in] format Data format for printing.
- * @param[in] out The output handler for printing.
- * @param[in] options_parse Parser options.
- * @param[in] options_validate Validation options.
- * @param[in] options_print Printer options.
- * @param[in] operational_f Optional operational datastore file information for the case of an extended validation of
- * operation(s).
- * @param[in] rpc_f Source RPC operation file information for parsing NETCONF rpc-reply.
- * @param[in] inputs Set of file informations of input data files.
- * @param[in] xpath The set of XPaths to be evaluated on the processed data tree, basic information about the resulting set
- * is printed. Alternative to data printing.
+ * @param[in] ext Compiled extension instance.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] ext_data Provided extension instance data.
+ * @param[out] ext_data_free Whether the extension instance should free @p ext_data or not.
* @return LY_ERR value.
*/
-LY_ERR process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
- uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
- struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths);
+LY_ERR ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free);
/**
- * @brief Get the node specified by the path.
+ * @brief Concatenation of paths into one string.
*
- * @param[in] ctx libyang context with schema.
- * @param[in] schema_path Path to the wanted node.
- * @return Pointer to the schema node specified by the path on success, NULL otherwise.
+ * @param[in,out] searchpaths Collection of paths in the single string. Paths are delimited by colon ":"
+ * (on Windows, used semicolon ";" instead).
+ * @param[in] path Path to add.
+ * @return LY_ERR value.
*/
-const struct lysc_node * find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
+LY_ERR searchpath_strcat(char **searchpaths, const char *path);
#endif /* COMMON_H_ */
diff --git a/tools/lint/completion.c b/tools/lint/completion.c
index 9843816..67c6b68 100644
--- a/tools/lint/completion.c
+++ b/tools/lint/completion.c
@@ -43,14 +43,18 @@ cmd_completion_add_match(const char *match, char ***matches, unsigned int *match
{
void *p;
- ++(*match_count);
- p = realloc(*matches, *match_count * sizeof **matches);
+ p = realloc(*matches, (*match_count + 1) * sizeof **matches);
if (!p) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
return;
}
*matches = p;
- (*matches)[*match_count - 1] = strdup(match);
+ (*matches)[*match_count] = strdup(match);
+ if (!((*matches)[*match_count])) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
+ return;
+ }
+ ++(*match_count);
}
/**
@@ -76,6 +80,34 @@ get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
}
/**
+ * @brief Provides completion for arguments.
+ *
+ * @param[in] hint User input.
+ * @param[in] args Array of all possible arguments. The last element must be NULL.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_arg_completion(const char *hint, const char **args, char ***matches, unsigned int *match_count)
+{
+ int i;
+
+ *match_count = 0;
+ *matches = NULL;
+
+ for (i = 0; args[i]; i++) {
+ if (!strncmp(hint, args[i], strlen(hint))) {
+ cmd_completion_add_match(args[i], matches, match_count);
+ }
+ }
+ if (*match_count == 0) {
+ for (i = 0; args[i]; i++) {
+ cmd_completion_add_match(args[i], matches, match_count);
+ }
+ }
+}
+
+/**
* @brief Provides completion for module names.
*
* @param[in] hint User input.
@@ -108,21 +140,21 @@ get_model_completion(const char *hint, char ***matches, unsigned int *match_coun
/**
* @brief Add all child nodes of a single node to the completion hint.
*
- * @param[in] last_node Node of which children will be added to the hint.
- * @param matches[out] Matches provided to the user as a completion hint.
- * @param match_count[out] Number of matches.
+ * @param[in] parent Node of which children will be added to the hint.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
*/
static void
-single_hint_add_children(const struct lysc_node *last_node, char ***matches, unsigned int *match_count)
+single_hint_add_children(const struct lysc_node *parent, char ***matches, unsigned int *match_count)
{
const struct lysc_node *node = NULL;
char *match;
- if (!last_node) {
+ if (!parent) {
return;
}
- while ((node = lys_getnext(node, last_node, NULL, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
+ while ((node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
match = lysc_path(node, LYSC_PATH_LOG, NULL, 0);
cmd_completion_add_match(match, matches, match_count);
free(match);
@@ -157,13 +189,13 @@ add_all_children_nodes(const struct lysc_module *module, const struct lysc_node
if (parent && (node->module != parent->module)) {
/* augmented node */
if (asprintf(&node_name, "%s:%s", node->module->name, node->name) == -1) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
break;
}
} else {
node_name = strdup(node->name);
if (!node_name) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
break;
}
}
@@ -229,7 +261,7 @@ get_schema_completion(const char *hint, char ***matches, unsigned int *match_cou
/* module name known */
module_name = strndup(start, end - start);
if (!module_name) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
rc = 1;
goto cleanup;
}
@@ -255,7 +287,7 @@ get_schema_completion(const char *hint, char ***matches, unsigned int *match_cou
/* get rid of stuff after the last '/' to obtain the parent node */
path = strndup(hint, start - hint);
if (!path) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
rc = 1;
goto cleanup;
}
@@ -277,6 +309,90 @@ cleanup:
}
/**
+ * @brief Get all possible argument hints for option.
+ *
+ * @param[in] hint User input.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_print_format_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"yang", "yin", "tree", "info", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_data_type_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"data", "config", "get", "getconfig", "edit", "rpc", "reply", "notif", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_data_in_format_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"xml", "json", "lyb", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_data_default_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"all", "all-tagged", "trim", "implicit-tagged", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_list_format_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"xml", "json", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_verb_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"error", "warning", "verbose", "debug", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+#ifndef NDEBUG
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_debug_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"dict", "xpath", "dep-sets", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+#endif
+
+/**
* @brief Get the string before the hint, which autocompletion is for.
*
* @param[in] buf Complete user input.
@@ -315,22 +431,37 @@ void
complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
{
struct autocomplete {
- const char *cmd; /**< command */
- const char *opt; /**< optional option */
- int last_opt; /**< whether to autocomplete even if an option is last in the hint */
-
+ enum COMMAND_INDEX ci; /**< command index to global variable 'commands' */
+ const char *opt; /**< optional option */
void (*ln_cb)(const char *, const char *, linenoiseCompletions *); /**< linenoise callback to call */
void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
} ac[] = {
- {"add", NULL, 1, linenoisePathCompletion, NULL},
- {"searchpath", NULL, 0, linenoisePathCompletion, NULL},
- {"data", NULL, 0, linenoisePathCompletion, NULL},
- {"print", NULL, 0, NULL, get_model_completion},
- {"feature", NULL, 0, NULL, get_model_completion},
- {"print", "-P", 1, NULL, get_schema_completion},
+ {CMD_ADD, NULL, linenoisePathCompletion, NULL},
+ {CMD_PRINT, "-f", NULL, get_print_format_arg},
+ {CMD_PRINT, "-P", NULL, get_schema_completion},
+ {CMD_PRINT, "-o", linenoisePathCompletion, NULL},
+ {CMD_PRINT, NULL, NULL, get_model_completion},
+ {CMD_SEARCHPATH, NULL, linenoisePathCompletion, NULL},
+ {CMD_EXTDATA, NULL, linenoisePathCompletion, NULL},
+ {CMD_CLEAR, "-Y", linenoisePathCompletion, NULL},
+ {CMD_DATA, "-t", NULL, get_data_type_arg},
+ {CMD_DATA, "-O", linenoisePathCompletion, NULL},
+ {CMD_DATA, "-R", linenoisePathCompletion, NULL},
+ {CMD_DATA, "-f", NULL, get_data_in_format_arg},
+ {CMD_DATA, "-F", NULL, get_data_in_format_arg},
+ {CMD_DATA, "-d", NULL, get_data_default_arg},
+ {CMD_DATA, "-o", linenoisePathCompletion, NULL},
+ {CMD_DATA, NULL, linenoisePathCompletion, NULL},
+ {CMD_LIST, NULL, NULL, get_list_format_arg},
+ {CMD_FEATURE, NULL, NULL, get_model_completion},
+ {CMD_VERB, NULL, NULL, get_verb_arg},
+#ifndef NDEBUG
+ {CMD_DEBUG, NULL, NULL, get_debug_arg},
+#endif
};
- size_t cmd_len;
- const char *last;
+ size_t name_len;
+ const char *last, *name, *getoptstr;
+ char opt[3] = {'\0', ':', '\0'};
char **matches = NULL;
unsigned int match_count = 0, i;
@@ -340,24 +471,27 @@ complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
} else {
for (i = 0; i < (sizeof ac / sizeof *ac); ++i) {
- cmd_len = strlen(ac[i].cmd);
- if (strncmp(buf, ac[i].cmd, cmd_len) || (buf[cmd_len] != ' ')) {
+ /* Find the right command. */
+ name = commands[ac[i].ci].name;
+ name_len = strlen(name);
+ if (strncmp(buf, name, name_len) || (buf[name_len] != ' ')) {
/* not this command */
continue;
}
+ /* Select based on the right option. */
last = get_last_str(buf, hint);
- if (ac[i].opt && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
- /* autocompletion for (another) option */
+ opt[0] = (last[0] == '-') && last[1] ? last[1] : '\0';
+ getoptstr = commands[ac[i].ci].optstring;
+ if (!ac[i].opt && opt[0] && strstr(getoptstr, opt)) {
+ /* completion for the argument must be defined */
continue;
- }
- if (!ac[i].last_opt && (last[0] == '-')) {
- /* autocompletion for the command, not an option */
+ } else if (ac[i].opt && opt[0] && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
+ /* completion for (another) option */
+ continue;
+ } else if (ac[i].opt && !opt[0]) {
+ /* completion is defined for option */
continue;
- }
- if ((last != buf) && (last[0] != '-')) {
- /* autocompleted */
- return;
}
/* callback */
diff --git a/tools/lint/configuration.c b/tools/lint/configuration.c
index 86179fa..e3db668 100644
--- a/tools/lint/configuration.c
+++ b/tools/lint/configuration.c
@@ -37,14 +37,14 @@ get_yanglint_dir(void)
char *user_home, *yl_dir;
if (!(pw = getpwuid(getuid()))) {
- YLMSG_E("Determining home directory failed (%s).\n", strerror(errno));
+ YLMSG_E("Determining home directory failed (%s).", strerror(errno));
return NULL;
}
user_home = pw->pw_dir;
yl_dir = malloc(strlen(user_home) + 1 + strlen(YL_DIR) + 1);
if (!yl_dir) {
- YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ YLMSG_E("Memory allocation failed (%s).", strerror(errno));
return NULL;
}
sprintf(yl_dir, "%s/%s", user_home, YL_DIR);
@@ -53,17 +53,17 @@ get_yanglint_dir(void)
if (ret == -1) {
if (errno == ENOENT) {
/* directory does not exist */
- YLMSG_W("Configuration directory \"%s\" does not exist, creating it.\n", yl_dir);
+ YLMSG_W("Configuration directory \"%s\" does not exist, creating it.", yl_dir);
if (mkdir(yl_dir, 00700)) {
if (errno != EEXIST) {
/* parallel execution, yay */
- YLMSG_E("Configuration directory \"%s\" cannot be created (%s).\n", yl_dir, strerror(errno));
+ YLMSG_E("Configuration directory \"%s\" cannot be created (%s).", yl_dir, strerror(errno));
free(yl_dir);
return NULL;
}
}
} else {
- YLMSG_E("Configuration directory \"%s\" exists but cannot be accessed (%s).\n", yl_dir, strerror(errno));
+ YLMSG_E("Configuration directory \"%s\" exists but cannot be accessed (%s).", yl_dir, strerror(errno));
free(yl_dir);
return NULL;
}
@@ -83,16 +83,16 @@ load_config(void)
history_file = malloc(strlen(yl_dir) + 9);
if (!history_file) {
- YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ YLMSG_E("Memory allocation failed (%s).", strerror(errno));
free(yl_dir);
return;
}
sprintf(history_file, "%s/history", yl_dir);
if (access(history_file, F_OK) && (errno == ENOENT)) {
- YLMSG_W("No saved history.\n");
+ YLMSG_W("No saved history.");
} else if (linenoiseHistoryLoad(history_file)) {
- YLMSG_E("Failed to load history.\n");
+ YLMSG_E("Failed to load history.");
}
free(history_file);
@@ -110,14 +110,14 @@ store_config(void)
history_file = malloc(strlen(yl_dir) + 9);
if (!history_file) {
- YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ YLMSG_E("Memory allocation failed (%s).", strerror(errno));
free(yl_dir);
return;
}
sprintf(history_file, "%s/history", yl_dir);
if (linenoiseHistorySave(history_file)) {
- YLMSG_E("Failed to save history.\n");
+ YLMSG_E("Failed to save history.");
}
free(history_file);
diff --git a/tools/lint/examples/README.md b/tools/lint/examples/README.md
index 604591c..93d3c2a 100644
--- a/tools/lint/examples/README.md
+++ b/tools/lint/examples/README.md
@@ -33,11 +33,11 @@ combination with the command name you are interested in:
```
> help searchpath
Usage: searchpath [--clear] [<modules-dir-path> ...]
- Set paths of directories where to search for imports and
- includes of the schema modules. The current working directory
- and the path of the module being added is used implicitly.
- The 'load' command uses these paths to search even for the
- schema modules to be loaded.
+ Set paths of directories where to search for imports and includes
+ of the schema modules. Subdirectories are also searched. The current
+ working directory and the path of the module being added is used implicitly.
+ The 'load' command uses these paths to search even for the schema modules
+ to be loaded.
```
The input files referred in this document are available together with this
@@ -469,3 +469,68 @@ ietf-ip features:
}
}
```
+
+## YANG modules with the Schema Mount extension
+
+In these examples the non-interactive `yanglint` is used to simplify creating the context, a `yang-library` data file is
+used. The working directory is `libyang/tools/lint/examples` and *libyang* must be installed.
+
+**Print tree output of a model with Schema Mount**
+
+Command and its output:
+
+```
+$ yanglint -f tree -p . -Y sm-context-main.xml -x sm-context-extension.xml sm-main.yang
+module: sm-main
+ +--mp root* [node]
+ | +--rw node string
+ +--mp root2
+ +--rw root3
+ +--mp my-list* [name]
+ +--rw things/* [name]
+ | +--rw name -> /if:interfaces/if:interface/if:name
+ | +--rw attribute? uint32
+ +--rw not-compiled/
+ | +--rw first? string
+ | +--rw second? string
+ +--rw interfaces@
+ | +--rw interface* [name]
+ | +--rw name string
+ | +--rw type identityref
+ +--rw name string
+```
+
+**Validating and printing mounted data**
+
+Command and its output:
+
+```
+$ yanglint -f json -t config -p . -Y sm-context-main.xml -x sm-context-extension.xml sm-data.xml
+{
+ "ietf-interfaces:interfaces": {
+ "interface": [
+ {
+ "name": "eth0",
+ "type": "iana-if-type:ethernetCsmacd"
+ },
+ {
+ "name": "eth1",
+ "type": "iana-if-type:ethernetCsmacd"
+ }
+ ]
+ },
+ "sm-main:root3": {
+ "my-list": [
+ {
+ "name": "list item 1",
+ "sm-extension:things": [
+ {
+ "name": "eth0",
+ "attribute": 1
+ }
+ ]
+ }
+ ]
+ }
+}
+```
diff --git a/tools/lint/main.c b/tools/lint/main.c
index 9f0d027..43b90c8 100644
--- a/tools/lint/main.c
+++ b/tools/lint/main.c
@@ -1,9 +1,10 @@
/**
* @file main.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
#include "completion.h"
#include "configuration.h"
#include "linenoise/linenoise.h"
+#include "yl_opt.h"
int done;
struct ly_ctx *ctx = NULL;
@@ -37,25 +39,32 @@ int main_ni(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
- char *cmdline;
- int cmdlen;
+ int cmdlen, posc, i, j;
+ struct yl_opt yo = {0};
+ char *empty = NULL, *cmdline;
+ char **posv;
+ uint8_t cmd_found;
if (argc > 1) {
/* run in non-interactive mode */
return main_ni(argc, argv);
}
+ yo.interactive = 1;
/* continue in interactive mode */
linenoiseSetCompletionCallback(complete_cmd);
load_config();
if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {
- YLMSG_E("Failed to create context.\n");
+ YLMSG_E("Failed to create context.");
return 1;
}
while (!done) {
- uint8_t executed = 0;
+ cmd_found = 0;
+
+ posv = &empty;
+ posc = 0;
/* get the command from user */
cmdline = linenoise(PROMPT);
@@ -76,25 +85,48 @@ main(int argc, char *argv[])
for (cmdlen = 0; cmdline[cmdlen] && (cmdline[cmdlen] != ' '); cmdlen++) {}
/* execute the command if any valid specified */
- for (uint16_t i = 0; commands[i].name; i++) {
+ for (i = 0; commands[i].name; i++) {
if (strncmp(cmdline, commands[i].name, (size_t)cmdlen) || (commands[i].name[cmdlen] != '\0')) {
continue;
}
- commands[i].func(&ctx, cmdline);
- executed = 1;
+ cmd_found = 1;
+ if (commands[i].opt_func && commands[i].opt_func(&yo, cmdline, &posv, &posc)) {
+ break;
+ }
+ if (commands[i].dep_func && commands[i].dep_func(&yo, posc)) {
+ break;
+ }
+ if (posc) {
+ for (j = 0; j < posc; j++) {
+ yo.last_one = (j + 1) == posc;
+ if (commands[i].exec_func(&ctx, &yo, posv[j])) {
+ break;
+ }
+ }
+ } else {
+ commands[i].exec_func(&ctx, &yo, NULL);
+ }
+ if (commands[i].fin_func) {
+ commands[i].fin_func(ctx, &yo);
+ }
+
break;
}
- if (!executed) {
+ if (!cmd_found) {
/* if unknown command specified, tell it to user */
- YLMSG_E("Unknown command \"%.*s\", type 'help' for more information.\n", cmdlen, cmdline);
+ YLMSG_E("Unknown command \"%.*s\", type 'help' for more information.", cmdlen, cmdline);
}
linenoiseHistoryAdd(cmdline);
free(cmdline);
+ yl_opt_erase(&yo);
}
+ /* Global variables in commands are freed. */
+ cmd_free();
+
store_config();
ly_ctx_destroy(ctx);
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 04c2340..c08acc3 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -2,9 +2,10 @@
* @file main_ni.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - non-interactive code
*
- * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -25,107 +26,13 @@
#include <sys/stat.h>
#include "libyang.h"
-#include "plugins_exts.h"
+#include "cmd.h"
#include "common.h"
#include "out.h"
#include "tools/config.h"
-
-/**
- * @brief Context structure to hold and pass variables in a structured form.
- */
-struct context {
- /* libyang context for the run */
- const char *yang_lib_file;
- uint16_t ctx_options;
- struct ly_ctx *ctx;
-
- /* prepared output (--output option or stdout by default) */
- struct ly_out *out;
-
- char *searchpaths;
-
- /* options flags */
- uint8_t list; /* -l option to print list of schemas */
-
- /* line length for 'tree' format */
- size_t line_length; /* --tree-line-length */
-
- /*
- * schema
- */
- /* set schema modules' features via --features option (struct schema_features *) */
- struct ly_set schema_features;
-
- /* set of loaded schema modules (struct lys_module *) */
- struct ly_set schema_modules;
-
- /* options to parse and print schema modules */
- uint32_t schema_parse_options;
- uint32_t schema_print_options;
-
- /* specification of printing schema node subtree, option --schema-node */
- const char *schema_node_path;
- const struct lysc_node *schema_node;
- const char *submodule;
-
- /* name of file containing explicit context passed to callback
- * for schema-mount extension. This also causes a callback to
- * be registered.
- */
- char *schema_context_filename;
-
- /* value of --format in case of schema format */
- LYS_OUTFORMAT schema_out_format;
- ly_bool feature_param_format;
-
- /*
- * data
- */
- /* various options based on --type option */
- enum lyd_type data_type;
- uint32_t data_parse_options;
- uint32_t data_validate_options;
- uint32_t data_print_options;
-
- /* flag for --merge option */
- uint8_t data_merge;
-
- /* value of --format in case of data format */
- LYD_FORMAT data_out_format;
-
- /* input data files (struct cmdline_file *) */
- struct ly_set data_inputs;
-
- /* storage for --operational */
- struct cmdline_file data_operational;
-
- /* storage for --reply-rpc */
- struct cmdline_file reply_rpc;
-};
-
-static void
-erase_context(struct context *c)
-{
- /* data */
- ly_set_erase(&c->data_inputs, free_cmdline_file);
- ly_in_free(c->data_operational.in, 1);
-
- /* schema */
- ly_set_erase(&c->schema_features, free_features);
- ly_set_erase(&c->schema_modules, NULL);
-
- /* context */
- free(c->searchpaths);
- c->searchpaths = NULL;
-
- ly_out_free(c->out, NULL, 0);
- ly_ctx_destroy(c->ctx);
-
- if (c->schema_context_filename) {
- free(c->schema_context_filename);
- }
-}
+#include "yl_opt.h"
+#include "yl_schema_features.h"
static void
version(void)
@@ -146,7 +53,8 @@ help(int shortout)
" printing them in the specified format.\n\n"
" yanglint -t (nc-)rpc/notif [-O <operational-file>] <schema>... <file>\n"
" Validates the YANG/NETCONF RPC/notification <file> according to the <schema>(s) using\n"
- " <operational-file> with possible references to the operational datastore data.\n\n"
+ " <operational-file> with possible references to the operational datastore data.\n"
+ " To validate nested-notification or action, the <operational-file> is required.\n\n"
" yanglint -t nc-reply -R <rpc-file> [-O <operational-file>] <schema>... <file>\n"
" Validates the NETCONF rpc-reply <file> of RPC <rpc-file> according to the <schema>(s)\n"
" using <operational-file> with possible references to the operational datastore data.\n\n"
@@ -169,10 +77,16 @@ help(int shortout)
" yang, yin, tree, info and feature-param for schemas,\n"
" xml, json, and lyb for data.\n\n");
+ printf(" -I FORMAT, --in-format=FORMAT\n"
+ " Load the data in one of the following formats:\n"
+ " xml, json, lyb\n"
+ " If input format not specified, it is detected from the file extension.\n\n");
+
printf(" -p PATH, --path=PATH\n"
" Search path for schema (YANG/YIN) modules. The option can be\n"
" used multiple times. The current working directory and the\n"
- " path of the module being added is used implicitly.\n\n");
+ " path of the module being added is used implicitly. Subdirectories\n"
+ " are also searched\n\n");
printf(" -D, --disable-searchdir\n"
" Do not implicitly search in current working directory for\n"
@@ -251,6 +165,13 @@ help(int shortout)
" trim - Remove all nodes with a default value.\n"
" implicit-tagged - Add missing nodes and mark them with the attribute.\n\n");
+ printf(" -E XPATH, --data-xpath=XPATH\n"
+ " Evaluate XPATH expression over the data and print the nodes satisfying\n"
+ " the expression. The output format is specific and the option cannot\n"
+ " be combined with the -f and -d options. Also all the data\n"
+ " inputs are merged into a single data tree where the expression\n"
+ " is evaluated, so the -m option is always set implicitly.\n\n");
+
printf(" -l, --list Print info about the loaded schemas.\n"
" (i - imported module, I - implemented module)\n"
" In case the '-f' option with data encoding is specified,\n"
@@ -267,8 +188,8 @@ help(int shortout)
" Provide optional data to extend validation of the '(nc-)rpc',\n"
" '(nc-)reply' or '(nc-)notif' TYPEs. The FILE is supposed to contain\n"
" the operational datastore referenced from the operation.\n"
- " In case of a nested operation, its parent existence is also\n"
- " checked in these operational data.\n\n");
+ " In case of a nested notification or action, its parent existence\n"
+ " is also checked in these operational data.\n\n");
printf(" -R FILE, --reply-rpc=FILE\n"
" Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
@@ -287,6 +208,9 @@ help(int shortout)
" create an exact YANG schema context. If specified, the '-F'\n"
" parameter (enabled features) is ignored.\n\n");
+ printf(" -X, --extended-leafref\n"
+ " Allow usage of deref() XPath function within leafref\n\n");
+
#ifndef NDEBUG
printf(" -G GROUPS, --debug=GROUPS\n"
" Enable printing of specific debugging message group\n"
@@ -321,11 +245,11 @@ libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
}
}
-static struct schema_features *
+static struct yl_schema_features *
get_features_not_applied(const struct ly_set *fset)
{
for (uint32_t u = 0; u < fset->count; ++u) {
- struct schema_features *sf = fset->objs[u];
+ struct yl_schema_features *sf = fset->objs[u];
if (!sf->applied) {
return sf;
@@ -335,190 +259,166 @@ get_features_not_applied(const struct ly_set *fset)
return NULL;
}
-static LY_ERR
-ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
-{
- struct ly_ctx *ctx;
- struct lyd_node *data = NULL;
-
- ctx = ext->module->ctx;
- if (user_data) {
- lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
- }
-
- *ext_data = data;
- *ext_data_free = 1;
- return LY_SUCCESS;
-}
-
-static LY_ERR
-searchpath_strcat(char **searchpaths, const char *path)
-{
- uint64_t len;
- char *new;
-
- if (!(*searchpaths)) {
- *searchpaths = strdup(path);
- return LY_SUCCESS;
- }
-
- len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
- new = realloc(*searchpaths, sizeof(char) * len + 1);
- if (!new) {
- return LY_EMEM;
- }
- strcat(new, PATH_SEPARATOR);
- strcat(new, path);
- *searchpaths = new;
-
- return LY_SUCCESS;
-}
-
+/**
+ * @brief Create the libyang context.
+ *
+ * @param[in] yang_lib_file Context can be defined in yang library file.
+ * @param[in] searchpaths Directories in which modules are searched.
+ * @param[in,out] schema_features Set of features.
+ * @param[in,out] ctx_options Options for libyang context.
+ * @param[out] ctx Context for libyang.
+ * @return 0 on success.
+ */
static int
-fill_context_inputs(int argc, char *argv[], struct context *c)
+create_ly_context(const char *yang_lib_file, const char *searchpaths, struct ly_set *schema_features,
+ uint16_t *ctx_options, struct ly_ctx **ctx)
{
- struct ly_in *in = NULL;
- struct schema_features *sf;
- struct lys_module *mod;
- const char *all_features[] = {"*", NULL};
- char *dir = NULL, *module = NULL;
-
- /* Create libyang context. */
- if (c->yang_lib_file) {
+ if (yang_lib_file) {
/* ignore features */
- ly_set_erase(&c->schema_features, free_features);
+ ly_set_erase(schema_features, yl_schema_features_free);
- if (ly_ctx_new_ylpath(c->searchpaths, c->yang_lib_file, LYD_UNKNOWN, c->ctx_options, &c->ctx)) {
- YLMSG_E("Unable to modify libyang context with yang-library data.\n");
+ if (ly_ctx_new_ylpath(searchpaths, yang_lib_file, LYD_UNKNOWN, *ctx_options, ctx)) {
+ YLMSG_E("Unable to modify libyang context with yang-library data.");
return -1;
}
} else {
/* set imp feature flag if all should be enabled */
- c->ctx_options |= !c->schema_features.count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
+ (*ctx_options) |= !schema_features->count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
- if (ly_ctx_new(c->searchpaths, c->ctx_options, &c->ctx)) {
- YLMSG_E("Unable to create libyang context\n");
+ if (ly_ctx_new(searchpaths, *ctx_options, ctx)) {
+ YLMSG_E("Unable to create libyang context.");
return -1;
}
}
- /* set callback providing run-time extension instance data */
- if (c->schema_context_filename) {
- ly_ctx_set_ext_data_clb(c->ctx, ext_data_clb, c->schema_context_filename);
- }
+ return 0;
+}
- /* process the operational and/or reply RPC content if any */
- if (c->data_operational.path) {
- if (get_input(c->data_operational.path, NULL, &c->data_operational.format, &c->data_operational.in)) {
- return -1;
+/**
+ * @brief Implement module if some feature has not been applied.
+ *
+ * @param[in] schema_features Set of features.
+ * @param[in,out] ctx Context for libyang.
+ * @return 0 on success.
+ */
+static int
+apply_features(struct ly_set *schema_features, struct ly_ctx *ctx)
+{
+ struct yl_schema_features *sf;
+ struct lys_module *mod;
+
+ /* check that all specified features were applied, apply now if possible */
+ while ((sf = get_features_not_applied(schema_features))) {
+ /* try to find implemented or the latest revision of this module */
+ mod = ly_ctx_get_module_implemented(ctx, sf->mod_name);
+ if (!mod) {
+ mod = ly_ctx_get_module_latest(ctx, sf->mod_name);
}
- }
- if (c->reply_rpc.path) {
- if (get_input(c->reply_rpc.path, NULL, &c->reply_rpc.format, &c->reply_rpc.in)) {
- return -1;
+ if (!mod) {
+ YLMSG_E("Specified features not applied, module \"%s\" not loaded.", sf->mod_name);
+ return 1;
}
- }
- for (int i = 0; i < argc - optind; i++) {
- LYS_INFORMAT format_schema = LYS_IN_UNKNOWN;
- LYD_FORMAT format_data = LYD_UNKNOWN;
-
- if (get_input(argv[optind + i], &format_schema, &format_data, &in)) {
- goto error;
+ /* we have the module, implement it if needed and enable the specific features */
+ if (lys_set_implemented(mod, (const char **)sf->features)) {
+ YLMSG_E("Implementing module \"%s\" failed.", mod->name);
+ return 1;
}
+ sf->applied = 1;
+ }
- if (format_schema) {
- LY_ERR ret;
- uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
- const char **features;
+ return 0;
+}
- /* parse the input */
- if (parse_schema_path(argv[optind + i], &dir, &module)) {
- goto error;
- }
+/**
+ * @brief Parse and compile modules, data are only stored for later processing.
+ *
+ * @param[in] argc Number of strings in @p argv.
+ * @param[in] argv Strings from command line.
+ * @param[in] optind Index to the first input file in @p argv.
+ * @param[in] data_in_format Specified input data format.
+ * @param[in,out] ctx Context for libyang.
+ * @param[in,out] yo Options for yanglint.
+ * @return 0 on success.
+ */
+static int
+fill_context_inputs(int argc, char *argv[], int optind, LYD_FORMAT data_in_format, struct ly_ctx *ctx,
+ struct yl_opt *yo)
+{
+ char *filepath = NULL;
+ LYS_INFORMAT format_schema;
+ LYD_FORMAT format_data;
- if (c->yang_lib_file) {
- /* just get the module, it should already be parsed */
- mod = ly_ctx_get_module_implemented(c->ctx, module);
- if (!mod) {
- YLMSG_E("Schema module \"%s\" not implemented in yang-library data.\n", module);
- goto error;
- }
- } else {
- /* add temporarily also the path of the module itself */
- if (ly_ctx_set_searchdir(c->ctx, dir) == LY_EEXIST) {
- path_unset = 0;
- }
+ for (int i = 0; i < argc - optind; i++) {
+ format_schema = LYS_IN_UNKNOWN;
+ format_data = data_in_format;
- /* get features list for this module */
- if (!c->schema_features.count) {
- features = all_features;
- } else {
- get_features(&c->schema_features, module, &features);
- }
+ filepath = argv[optind + i];
- /* parse module */
- ret = lys_parse(c->ctx, in, format_schema, features, &mod);
- ly_ctx_unset_searchdir_last(c->ctx, path_unset);
- if (ret) {
- YLMSG_E("Parsing schema module \"%s\" failed.\n", argv[optind + i]);
- goto error;
- }
- }
+ if (!filepath) {
+ return -1;
+ }
+ if (get_format(filepath, &format_schema, &format_data)) {
+ return -1;
+ }
- /* temporary cleanup */
- free(dir);
- dir = NULL;
- free(module);
- module = NULL;
-
- if (c->schema_out_format || c->feature_param_format) {
- /* module will be printed */
- if (ly_set_add(&c->schema_modules, (void *)mod, 1, NULL)) {
- YLMSG_E("Storing parsed schema module (%s) for print failed.\n", argv[optind + i]);
- goto error;
- }
+ if (format_schema) {
+ if (cmd_add_exec(&ctx, yo, filepath)) {
+ return -1;
}
- } else if (format_data) {
- if (!fill_cmdline_file(&c->data_inputs, in, argv[optind + i], format_data)) {
- goto error;
+ } else {
+ if (cmd_data_store(&ctx, yo, filepath)) {
+ return -1;
}
- in = NULL;
}
+ }
- ly_in_free(in, 1);
- in = NULL;
+ /* Check that all specified features were applied, apply now if possible. */
+ if (apply_features(&yo->schema_features, ctx)) {
+ return -1;
}
- /* check that all specified features were applied, apply now if possible */
- while ((sf = get_features_not_applied(&c->schema_features))) {
- /* try to find implemented or the latest revision of this module */
- mod = ly_ctx_get_module_implemented(c->ctx, sf->mod_name);
- if (!mod) {
- mod = ly_ctx_get_module_latest(c->ctx, sf->mod_name);
- }
- if (!mod) {
- YLMSG_E("Specified features not applied, module \"%s\" not loaded.\n", sf->mod_name);
- goto error;
- }
+ return 0;
+}
- /* we have the module, implement it if needed and enable the specific features */
- if (lys_set_implemented(mod, (const char **)sf->features)) {
- YLMSG_E("Implementing module \"%s\" failed.\n", mod->name);
- goto error;
+#ifndef NDEBUG
+/**
+ * @brief Enable specific debugging messages.
+ *
+ * @param[in] groups String in the form "<group>[,group>]*".
+ * @param[in,out] yo Options for yanglint.
+ * return 0 on success.
+ */
+static int
+set_debug_groups(char *groups, struct yl_opt *yo)
+{
+ int rc;
+ char *str, *end;
+
+ /* Process all debug arguments except the last one. */
+ for (str = groups; (end = strchr(str, ',')); str = end + 1) {
+ /* Temporary modify input string. */
+ *end = '\0';
+ rc = cmd_debug_store(NULL, yo, str);
+ *end = ',';
+ if (rc) {
+ return -1;
}
- sf->applied = 1;
+ }
+ /* Process single/last debug argument. */
+ if (cmd_debug_store(NULL, yo, str)) {
+ return -1;
+ }
+ /* All debug arguments are valid, so they can apply. */
+ if (cmd_debug_setlog(NULL, yo)) {
+ return -1;
}
return 0;
-
-error:
- ly_in_free(in, 1);
- free(dir);
- free(module);
- return -1;
}
+#endif
+
/**
* @brief Process command line options and store the settings into the context.
*
@@ -527,10 +427,8 @@ error:
* return 1 in case of success, but expect to exit.
*/
static int
-fill_context(int argc, char *argv[], struct context *c)
+fill_context(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
{
- int ret;
-
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
@@ -542,6 +440,7 @@ fill_context(int argc, char *argv[], struct context *c)
{"disable-searchdir", no_argument, NULL, 'D'},
{"features", required_argument, NULL, 'F'},
{"make-implemented", no_argument, NULL, 'i'},
+ {"in-format", required_argument, NULL, 'I'},
{"schema-node", required_argument, NULL, 'P'},
{"single-node", no_argument, NULL, 'q'},
{"submodule", required_argument, NULL, 's'},
@@ -550,6 +449,7 @@ fill_context(int argc, char *argv[], struct context *c)
{"present", no_argument, NULL, 'e'},
{"type", required_argument, NULL, 't'},
{"default", required_argument, NULL, 'd'},
+ {"data-xpath", required_argument, NULL, 'E'},
{"list", no_argument, NULL, 'l'},
{"tree-line-length", required_argument, NULL, 'L'},
{"output", required_argument, NULL, 'o'},
@@ -558,6 +458,7 @@ fill_context(int argc, char *argv[], struct context *c)
{"merge", no_argument, NULL, 'm'},
{"yang-library", no_argument, NULL, 'y'},
{"yang-library-file", required_argument, NULL, 'Y'},
+ {"extended-leafref", no_argument, NULL, 'X'},
#ifndef NDEBUG
{"debug", required_argument, NULL, 'G'},
#endif
@@ -565,15 +466,16 @@ fill_context(int argc, char *argv[], struct context *c)
};
uint8_t data_type_set = 0;
- c->ctx_options = YL_DEFAULT_CTX_OPTIONS;
- c->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
- c->line_length = 0;
+ yo->ctx_options = YL_DEFAULT_CTX_OPTIONS;
+ yo->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
+ yo->data_validate_options = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
+ yo->line_length = 0;
opterr = 0;
#ifndef NDEBUG
- while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:G:", options, &opt_index)) != -1)
+ while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:Xx:G:", options, &opt_index)) != -1)
#else
- while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:", options, &opt_index)) != -1)
+ while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:Xx:", options, &opt_index)) != -1)
#endif
{
switch (opt) {
@@ -609,138 +511,77 @@ fill_context(int argc, char *argv[], struct context *c)
} /* case 'Q' */
case 'f': /* --format */
- if (!strcasecmp(optarg, "yang")) {
- c->schema_out_format = LYS_OUT_YANG;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "yin")) {
- c->schema_out_format = LYS_OUT_YIN;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "info")) {
- c->schema_out_format = LYS_OUT_YANG_COMPILED;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "tree")) {
- c->schema_out_format = LYS_OUT_TREE;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "xml")) {
- c->schema_out_format = 0;
- c->data_out_format = LYD_XML;
- } else if (!strcasecmp(optarg, "json")) {
- c->schema_out_format = 0;
- c->data_out_format = LYD_JSON;
- } else if (!strcasecmp(optarg, "lyb")) {
- c->schema_out_format = 0;
- c->data_out_format = LYD_LYB;
- } else if (!strcasecmp(optarg, "feature-param")) {
- c->feature_param_format = 1;
- } else {
- YLMSG_E("Unknown output format %s\n", optarg);
+ if (yl_opt_update_out_format(optarg, yo)) {
help(1);
return -1;
}
break;
- case 'p': { /* --path */
- struct stat st;
-
- if (stat(optarg, &st) == -1) {
- YLMSG_E("Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
- return -1;
- }
- if (!S_ISDIR(st.st_mode)) {
- YLMSG_E("Provided search path is not a directory.\n");
+ case 'I': /* --in-format */
+ if (yo_opt_update_data_in_format(optarg, yo)) {
+ YLMSG_E("Unknown input format %s.", optarg);
+ help(1);
return -1;
}
+ break;
- if (searchpath_strcat(&c->searchpaths, optarg)) {
- YLMSG_E("Storing searchpath failed.\n");
+ case 'p': /* --path */
+ if (searchpath_strcat(&yo->searchpaths, optarg)) {
+ YLMSG_E("Storing searchpath failed.");
return -1;
}
-
break;
- } /* case 'p' */
+ /* case 'p' */
- case 'D': /* --disable-search */
- if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
- YLMSG_W("The -D option specified too many times.\n");
- }
- if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIR_CWD) {
- c->ctx_options &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
- c->ctx_options |= LY_CTX_DISABLE_SEARCHDIRS;
- } else {
- c->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ case 'D': /* --disable-searchdir */
+ if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
+ YLMSG_W("The -D option specified too many times.");
}
+ yo_opt_update_disable_searchdir(yo);
break;
case 'F': /* --features */
- if (parse_features(optarg, &c->schema_features)) {
+ if (parse_features(optarg, &yo->schema_features)) {
return -1;
}
break;
case 'i': /* --make-implemented */
- if (c->ctx_options & LY_CTX_REF_IMPLEMENTED) {
- c->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
- c->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
- } else {
- c->ctx_options |= LY_CTX_REF_IMPLEMENTED;
- }
+ yo_opt_update_make_implemented(yo);
break;
case 'P': /* --schema-node */
- c->schema_node_path = optarg;
+ yo->schema_node_path = optarg;
break;
case 'q': /* --single-node */
- c->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
+ yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
break;
case 's': /* --submodule */
- c->submodule = optarg;
+ yo->submodule = optarg;
break;
case 'x': /* --ext-data */
- c->schema_context_filename = strdup(optarg);
+ yo->schema_context_filename = optarg;
break;
case 'n': /* --not-strict */
- c->data_parse_options &= ~LYD_PARSE_STRICT;
+ yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
case 'e': /* --present */
- c->data_validate_options |= LYD_VALIDATE_PRESENT;
+ yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;
case 't': /* --type */
if (data_type_set) {
- YLMSG_E("The data type (-t) cannot be set multiple times.\n");
+ YLMSG_E("The data type (-t) cannot be set multiple times.");
return -1;
}
- if (!strcasecmp(optarg, "config")) {
- c->data_parse_options |= LYD_PARSE_NO_STATE;
- c->data_validate_options |= LYD_VALIDATE_NO_STATE;
- } else if (!strcasecmp(optarg, "get")) {
- c->data_parse_options |= LYD_PARSE_ONLY;
- } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config")) {
- c->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
- } else if (!strcasecmp(optarg, "edit")) {
- c->data_parse_options |= LYD_PARSE_ONLY;
- } else if (!strcasecmp(optarg, "rpc")) {
- c->data_type = LYD_TYPE_RPC_YANG;
- } else if (!strcasecmp(optarg, "nc-rpc")) {
- c->data_type = LYD_TYPE_RPC_NETCONF;
- } else if (!strcasecmp(optarg, "reply")) {
- c->data_type = LYD_TYPE_REPLY_YANG;
- } else if (!strcasecmp(optarg, "nc-reply")) {
- c->data_type = LYD_TYPE_REPLY_NETCONF;
- } else if (!strcasecmp(optarg, "notif")) {
- c->data_type = LYD_TYPE_NOTIF_YANG;
- } else if (!strcasecmp(optarg, "nc-notif")) {
- c->data_type = LYD_TYPE_NOTIF_NETCONF;
- } else if (!strcasecmp(optarg, "data")) {
- /* default option */
- } else {
- YLMSG_E("Unknown data tree type %s\n", optarg);
+ if (yl_opt_update_data_type(optarg, yo)) {
+ YLMSG_E("Unknown data tree type %s.", optarg);
help(1);
return -1;
}
@@ -749,184 +590,128 @@ fill_context(int argc, char *argv[], struct context *c)
break;
case 'd': /* --default */
- if (!strcasecmp(optarg, "all")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
- } else if (!strcasecmp(optarg, "all-tagged")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
- } else if (!strcasecmp(optarg, "trim")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
- } else if (!strcasecmp(optarg, "implicit-tagged")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
- } else {
- YLMSG_E("Unknown default mode %s\n", optarg);
+ if (yo_opt_update_data_default(optarg, yo)) {
+ YLMSG_E("Unknown default mode %s.", optarg);
help(1);
return -1;
}
break;
+ case 'E': /* --data-xpath */
+ if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
+ YLMSG_E("Storing XPath \"%s\" failed.", optarg);
+ return -1;
+ }
+ break;
+
case 'l': /* --list */
- c->list = 1;
+ yo->list = 1;
break;
case 'L': /* --tree-line-length */
- c->line_length = atoi(optarg);
+ yo->line_length = atoi(optarg);
break;
case 'o': /* --output */
- if (c->out) {
- YLMSG_E("Only a single output can be specified.\n");
+ if (yo->out) {
+ YLMSG_E("Only a single output can be specified.");
return -1;
} else {
- if (ly_out_new_filepath(optarg, &c->out)) {
- YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
+ if (ly_out_new_filepath(optarg, &yo->out)) {
+ YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
return -1;
}
}
break;
case 'O': /* --operational */
- if (c->data_operational.path) {
- YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
+ if (yo->data_operational.path) {
+ YLMSG_E("The operational datastore (-O) cannot be set multiple times.");
return -1;
}
- c->data_operational.path = optarg;
+ yo->data_operational.path = optarg;
break;
case 'R': /* --reply-rpc */
- if (c->reply_rpc.path) {
- YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.\n");
+ if (yo->reply_rpc.path) {
+ YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.");
return -1;
}
- c->reply_rpc.path = optarg;
+ yo->reply_rpc.path = optarg;
break;
case 'm': /* --merge */
- c->data_merge = 1;
+ yo->data_merge = 1;
break;
case 'y': /* --yang-library */
- c->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
break;
case 'Y': /* --yang-library-file */
- c->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
- c->yang_lib_file = optarg;
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->yang_lib_file = optarg;
break;
-#ifndef NDEBUG
- case 'G': { /* --debug */
- uint32_t dbg_groups = 0;
- const char *ptr = optarg;
-
- while (ptr[0]) {
- if (!strncasecmp(ptr, "dict", sizeof "dict" - 1)) {
- dbg_groups |= LY_LDGDICT;
- ptr += sizeof "dict" - 1;
- } else if (!strncasecmp(ptr, "xpath", sizeof "xpath" - 1)) {
- dbg_groups |= LY_LDGXPATH;
- ptr += sizeof "xpath" - 1;
- } else if (!strncasecmp(ptr, "dep-sets", sizeof "dep-sets" - 1)) {
- dbg_groups |= LY_LDGDEPSETS;
- ptr += sizeof "dep-sets" - 1;
- }
+ case 'X': /* --extended-leafref */
+ yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
+ break;
- if (ptr[0]) {
- if (ptr[0] != ',') {
- YLMSG_E("Unknown debug group string \"%s\"\n", optarg);
- return -1;
- }
- ++ptr;
- }
+#ifndef NDEBUG
+ case 'G': /* --debug */
+ if (set_debug_groups(optarg, yo)) {
+ return -1;
}
- ly_log_dbg_groups(dbg_groups);
break;
- } /* case 'G' */
+ /* case 'G' */
#endif
default:
- YLMSG_E("Invalid option or missing argument: -%c\n", optopt);
+ YLMSG_E("Invalid option or missing argument: -%c.", optopt);
return -1;
} /* switch */
}
/* additional checks for the options combinations */
- if (!c->list && (optind >= argc)) {
+ if (!yo->list && (optind >= argc)) {
help(1);
- YLMSG_E("Missing <schema> to process.\n");
+ YLMSG_E("Missing <schema> to process.");
return 1;
}
- if (c->data_merge) {
- if (c->data_type || (c->data_parse_options & LYD_PARSE_ONLY)) {
- /* switch off the option, incompatible input data type */
- c->data_merge = 0;
- } else {
- /* postpone validation after the merge of all the input data */
- c->data_parse_options |= LYD_PARSE_ONLY;
- }
- }
-
- if (c->data_operational.path && !c->data_type) {
- YLMSG_E("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.\n");
- c->data_operational.path = NULL;
- }
-
- if (c->reply_rpc.path && (c->data_type != LYD_TYPE_REPLY_NETCONF)) {
- YLMSG_E("Source RPC is needed only for NETCONF Reply input data type.\n");
- c->data_operational.path = NULL;
- } else if (!c->reply_rpc.path && (c->data_type == LYD_TYPE_REPLY_NETCONF)) {
- YLMSG_E("Missing source RPC (-R) for NETCONF Reply input data type.\n");
+ if (cmd_data_dep(yo, 0)) {
return -1;
}
-
- if ((c->schema_out_format != LYS_OUT_TREE) && c->line_length) {
- YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
+ if (cmd_print_dep(yo, 0)) {
+ return -1;
}
- /* default output stream */
- if (!c->out) {
- if (ly_out_new_file(stdout, &c->out)) {
- YLMSG_E("Unable to set stdout as output.\n");
- return -1;
- }
+ /* Create the libyang context. */
+ if (create_ly_context(yo->yang_lib_file, yo->searchpaths, &yo->schema_features, &yo->ctx_options, ctx)) {
+ return -1;
}
- if (c->schema_out_format == LYS_OUT_TREE) {
- /* print tree from lysc_nodes */
- c->ctx_options |= LY_CTX_SET_PRIV_PARSED;
+ /* Set callback providing run-time extension instance data. */
+ if (yo->schema_context_filename) {
+ ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, yo->schema_context_filename);
}
- /* process input files provided as standalone command line arguments,
- * schema modules are parsed and inserted into the context,
- * data files are just checked and prepared into internal structures for further processing */
- ret = fill_context_inputs(argc, argv, c);
- if (ret) {
- return ret;
+ /* Schema modules and data files are just checked and prepared into internal structures for further processing. */
+ if (fill_context_inputs(argc, argv, optind, yo->data_in_format, *ctx, yo)) {
+ return -1;
}
/* the second batch of checks */
- if (c->schema_print_options && !c->schema_out_format) {
- YLMSG_W("Schema printer options specified, but the schema output format is missing.\n");
+ if (yo->schema_print_options && !yo->schema_out_format) {
+ YLMSG_W("Schema printer options specified, but the schema output format is missing.");
}
- if (c->schema_parse_options && !c->schema_modules.count) {
- YLMSG_W("Schema parser options specified, but no schema input file provided.\n");
+ if (yo->schema_parse_options && !yo->schema_modules.count) {
+ YLMSG_W("Schema parser options specified, but no schema input file provided.");
}
- if (c->data_print_options && !c->data_out_format) {
- YLMSG_W("data printer options specified, but the data output format is missing.\n");
+ if (yo->data_print_options && !yo->data_out_format) {
+ YLMSG_W("data printer options specified, but the data output format is missing.");
}
- if (((c->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || c->data_type) && !c->data_inputs.count) {
- YLMSG_W("Data parser options specified, but no data input file provided.\n");
- }
-
- if (c->schema_node_path) {
- c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 0);
- if (!c->schema_node) {
- c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 1);
-
- if (!c->schema_node) {
- YLMSG_E("Invalid schema path.\n");
- return -1;
- }
- }
+ if (((yo->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || yo->data_type) && !yo->data_inputs.count) {
+ YLMSG_W("Data parser options specified, but no data input file provided.");
}
return 0;
@@ -936,7 +721,8 @@ int
main_ni(int argc, char *argv[])
{
int ret = EXIT_SUCCESS, r;
- struct context c = {0};
+ struct yl_opt yo = {0};
+ struct ly_ctx *ctx = NULL;
char *features_output = NULL;
struct ly_set set = {0};
uint32_t u;
@@ -944,7 +730,7 @@ main_ni(int argc, char *argv[])
/* set callback for printing libyang messages */
ly_set_log_clb(libyang_verbclb, 1);
- r = fill_context(argc, argv, &c);
+ r = fill_context(argc, argv, &yo, &ctx);
if (r < 0) {
ret = EXIT_FAILURE;
}
@@ -954,72 +740,46 @@ main_ni(int argc, char *argv[])
/* do the required job - parse, validate, print */
- if (c.list) {
+ if (yo.list) {
/* print the list of schemas */
- print_list(c.out, c.ctx, c.data_out_format);
- } else {
- if (c.feature_param_format) {
- for (u = 0; u < c.schema_modules.count; u++) {
- if (collect_features(c.schema_modules.objs[u], &set)) {
- YLMSG_E("Unable to read features from a module.\n");
- goto cleanup;
- }
- if (generate_features_output(c.schema_modules.objs[u], &set, &features_output)) {
- YLMSG_E("Unable to generate feature command output.\n");
- goto cleanup;
- }
- ly_set_erase(&set, NULL);
- }
- ly_print(c.out, "%s\n", features_output);
- } else if (c.schema_out_format) {
- if (c.schema_node) {
- ret = lys_print_node(c.out, c.schema_node, c.schema_out_format, 0, c.schema_print_options);
- if (ret) {
- YLMSG_E("Unable to print schema node %s.\n", c.schema_node_path);
- goto cleanup;
- }
- } else if (c.submodule) {
- const struct lysp_submodule *submod = ly_ctx_get_submodule_latest(c.ctx, c.submodule);
-
- if (!submod) {
- YLMSG_E("Unable to find submodule %s.\n", c.submodule);
- goto cleanup;
- }
-
- ret = lys_print_submodule(c.out, submod, c.schema_out_format, c.line_length, c.schema_print_options);
- if (ret) {
- YLMSG_E("Unable to print submodule %s.\n", submod->name);
- goto cleanup;
- }
- } else {
- for (u = 0; u < c.schema_modules.count; ++u) {
- ret = lys_print_module(c.out, (struct lys_module *)c.schema_modules.objs[u], c.schema_out_format,
- c.line_length, c.schema_print_options);
- /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
- if ((c.schema_out_format == LYS_OUT_TREE) && (u + 1 < c.schema_modules.count)) {
- ly_print(c.out, "\n");
- }
- if (ret) {
- YLMSG_E("Unable to print module %s.\n", ((struct lys_module *)c.schema_modules.objs[u])->name);
- goto cleanup;
- }
- }
+ ret = cmd_list_exec(&ctx, &yo, NULL);
+ goto cleanup;
+ }
+ if (yo.feature_param_format) {
+ for (u = 0; u < yo.schema_modules.count; u++) {
+ if ((ret = cmd_feature_exec(&ctx, &yo, ((struct lys_module *)yo.schema_modules.objs[u])->name))) {
+ goto cleanup;
}
}
-
- /* do the data validation despite the schema was printed */
- if (c.data_inputs.size) {
- ret = process_data(c.ctx, c.data_type, c.data_merge, c.data_out_format, c.out, c.data_parse_options,
- c.data_validate_options, c.data_print_options, &c.data_operational, &c.reply_rpc, &c.data_inputs, NULL);
- if (ret) {
+ cmd_feature_fin(ctx, &yo);
+ } else if (yo.schema_out_format && yo.schema_node_path) {
+ if ((ret = cmd_print_exec(&ctx, &yo, NULL))) {
+ goto cleanup;
+ }
+ } else if (yo.schema_out_format && yo.submodule) {
+ if ((ret = cmd_print_exec(&ctx, &yo, yo.submodule))) {
+ goto cleanup;
+ }
+ } else if (yo.schema_out_format) {
+ for (u = 0; u < yo.schema_modules.count; ++u) {
+ yo.last_one = (u + 1) == yo.schema_modules.count;
+ if ((ret = cmd_print_exec(&ctx, &yo, ((struct lys_module *)yo.schema_modules.objs[u])->name))) {
goto cleanup;
}
}
}
+ /* do the data validation despite the schema was printed */
+ if (yo.data_inputs.size) {
+ if ((ret = cmd_data_process(ctx, &yo))) {
+ goto cleanup;
+ }
+ }
+
cleanup:
/* cleanup */
- erase_context(&c);
+ yl_opt_erase(&yo);
+ ly_ctx_destroy(ctx);
free(features_output);
ly_set_erase(&set, NULL);
diff --git a/tools/lint/tests/expect/common.exp b/tools/lint/tests/expect/common.exp
deleted file mode 100644
index 0381e6c..0000000
--- a/tools/lint/tests/expect/common.exp
+++ /dev/null
@@ -1,68 +0,0 @@
-# detect the path to the yanglint binary
-if { [info exists ::env(YANGLINT)] } {
- set yanglint "$env(YANGLINT)"
-} else {
- set yanglint "../../../../build/yanglint"
-}
-
-# set the timeout to 1 second
-set timeout 1
-
-# expect a single line of anchored regex output
-proc expect_output {output} {
- expect {
- -re "^${output}$" {}
- timeout {exit 1}
- }
-}
-
-# send a command and either expect some anchored regex output if specified or just an empty line
-proc expect_command {command has_output output} {
- send -- "${command}\r"
-
- if ($has_output==1) {
- expect {
- -re "^${command}\r\n${output}$" {}
- timeout {exit 1}
- }
- } else {
- # input echoes
- expect {
- -re "^${command}\r\n$" {}
- timeout {exit 1}
- }
- expect {
- -re "^> $" {}
- timeout {exit 1}
- }
- }
-}
-
-# send a completion request and check if the anchored regex output matches
-proc expect_completion {input output} {
- send -- "${input}\t"
-
- expect {
- # expecting echoing input, output and 10 terminal control characters
- -re "^${input}\r> ${output}.*\r.*$" {}
- timeout {exit 1}
- }
-}
-
-# send a completion request and check if the anchored regex hint options match
-proc expect_hint {input prev_input hints} {
- set output {}
- foreach i $hints {
- # each element might have some number of spaces and CRLF around it
- append output "${i} *(?:\\r\\n)?"
- }
-
- send -- "${input}\t"
-
- expect {
- # expecting the hints, previous input from which the hints were generated
- # and some number of terminal control characters
- -re "^\r\n${output}\r> ${prev_input}.*\r.*$" {}
- timeout {exit 1}
- }
-}
diff --git a/tools/lint/tests/expect/completion.exp b/tools/lint/tests/expect/completion.exp
deleted file mode 100755
index ed4f6bd..0000000
--- a/tools/lint/tests/expect/completion.exp
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/expect -f
-
-if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
- source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
- set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
-} else {
- source "common.exp"
- set yang_dir "../../examples"
-}
-
-spawn $yanglint
-
-# skip no dir and/or no history warnings
-expect_output "(YANGLINT.*)*> "
-
-expect_command "clear -ii" 0 ""
-
-expect_command "add ${yang_dir}/ietf-ip.yang" 0 ""
-
-expect_completion "print -f info -P " "print -f info -P /ietf-"
-
-set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"}
-
-expect_hint "" "print -f info -P /ietf-" $hints
-
-expect_completion "i" "print -f info -P /ietf-interfaces:interfaces"
-
-expect_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface"
-
-set hints {"/ietf-interfaces:interfaces/interface"
-"/ietf-interfaces:interfaces/interface/name" "/ietf-interfaces:interfaces/interface/description"
-"/ietf-interfaces:interfaces/interface/type" "/ietf-interfaces:interfaces/interface/enabled"
-"/ietf-interfaces:interfaces/interface/link-up-down-trap-enable"
-"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6"}
-
-expect_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints
-
-expect_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv"
-
-expect_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4"
-
-set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled"
-"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu"
-"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor"
-}
-
-expect_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints
-
-expect_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled "
-
-send -- "\r"
-
-expect {
- -re ".*\r\n> " {}
- timeout {exit 1}
-}
-
-send -- "exit\r"
-
-expect eof
diff --git a/tools/lint/tests/expect/feature.exp b/tools/lint/tests/expect/feature.exp
deleted file mode 100755
index 37680b0..0000000
--- a/tools/lint/tests/expect/feature.exp
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/expect -f
-
-if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
- source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
- set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
-} else {
- source "common.exp"
- set yang_dir "../../examples"
-}
-
-spawn $yanglint
-
-# skip no dir and/or no history warnings
-expect_output "(YANGLINT.*)*> "
-
-expect_command "feature -a" 1 "yang:\r\n\t\\(none\\)\r\n\r\nietf-yang-schema-mount:\r\n\t\\(none\\)\r\n\r\n> "
-
-send -- "exit\r"
-
-expect eof
diff --git a/tools/lint/tests/expect/list.exp b/tools/lint/tests/expect/list.exp
deleted file mode 100755
index ec3cdba..0000000
--- a/tools/lint/tests/expect/list.exp
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/expect -f
-
-if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
- source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
- set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
-} else {
- source "common.exp"
- set yang_dir "../../examples"
-}
-
-spawn $yanglint
-
-# skip no dir and/or no history warnings
-expect_output "(YANGLINT.*)*> "
-
-expect_command "list" 1 "List of the loaded models:\r\n *i ietf-yang-metadata@2016-08-05\r\n *I yang@2022-06-16\r\n *i ietf-inet-types@2013-07-15\r\n *i ietf-yang-types@2013-07-15\r\n *I ietf-yang-schema-mount@2019-01-14\r\n *i ietf-yang-structure-ext@2020-06-17\r\n> "
-
-send -- "exit\r"
-
-expect eof
diff --git a/tools/lint/tests/shunit2/feature.sh b/tools/lint/tests/shunit2/feature.sh
deleted file mode 100755
index fb2ee88..0000000
--- a/tools/lint/tests/shunit2/feature.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-testFeature() {
- models=( "iana-if-type@2014-05-08.yang" "ietf-netconf@2011-06-01.yang" "ietf-netconf-with-defaults@2011-06-01.yang"
- "sm.yang" "ietf-interfaces@2014-05-08.yang" "ietf-netconf-acm@2018-02-14.yang" "ietf-origin@2018-02-14.yang"
- "ietf-ip@2014-06-16.yang" "ietf-restconf@2017-01-26.yang" )
- features=( " -F iana-if-type:"
- " -F ietf-netconf:writable-running,candidate,confirmed-commit,rollback-on-error,validate,startup,url,xpath"
- " -F ietf-netconf-with-defaults:" " -F sm:" " -F ietf-interfaces:arbitrary-names,pre-provisioning,if-mib"
- " -F ietf-netconf-acm:" " -F ietf-origin:" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf"
- " -F ietf-restconf:" )
-
- for i in ${!models[@]}; do
- output=`${YANGLINT} -f feature-param ${YANG_MODULES_DIR}/${models[$i]}`
- assertEquals "Unexpected features of module ${models[$i]}." "${features[$i]}" "${output}"
- done
-}
-
-. shunit2
diff --git a/tools/lint/tests/shunit2/list.sh b/tools/lint/tests/shunit2/list.sh
deleted file mode 100755
index d64503a..0000000
--- a/tools/lint/tests/shunit2/list.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-
-LIST_BASE="List of the loaded models:
- i ietf-yang-metadata@2016-08-05
- I yang@2022-06-16
- i ietf-inet-types@2013-07-15
- i ietf-yang-types@2013-07-15
- I ietf-yang-schema-mount@2019-01-14
- i ietf-yang-structure-ext@2020-06-17"
-
-testListEmptyContext() {
- output=`${YANGLINT} -l`
- assertEquals "Unexpected list of modules in empty context." "${LIST_BASE}" "${output}"
-}
-
-testListAllImplemented() {
- LIST_BASE_ALLIMPLEMENTED="List of the loaded models:
- I ietf-yang-metadata@2016-08-05
- I yang@2022-06-16
- I ietf-inet-types@2013-07-15
- I ietf-yang-types@2013-07-15
- I ietf-yang-schema-mount@2019-01-14
- I ietf-yang-structure-ext@2020-06-17"
- output=`${YANGLINT} -lii`
- assertEquals "Unexpected list of modules in empty context with -ii." "${LIST_BASE_ALLIMPLEMENTED}" "${output}"
-}
-
-. shunit2
diff --git a/tools/lint/yl_opt.c b/tools/lint/yl_opt.c
new file mode 100644
index 0000000..7cd855f
--- /dev/null
+++ b/tools/lint/yl_opt.c
@@ -0,0 +1,344 @@
+/**
+ * @file yl_opt.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Settings options for the libyang context.
+ *
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <strings.h>
+
+#include "in.h" /* ly_in_free */
+
+#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
+
+struct cmdline_file *
+fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
+{
+ struct cmdline_file *rec;
+
+ rec = malloc(sizeof *rec);
+ if (!rec) {
+ YLMSG_E("Allocating memory for data file information failed.");
+ return NULL;
+ }
+ rec->in = in;
+ rec->path = path;
+ rec->format = format;
+
+ if (set && ly_set_add(set, rec, 1, NULL)) {
+ free(rec);
+ YLMSG_E("Storing data file information failed.");
+ return NULL;
+ }
+
+ return rec;
+}
+
+void
+free_cmdline_file_items(struct cmdline_file *rec)
+{
+ if (rec && rec->in) {
+ ly_in_free(rec->in, 1);
+ }
+}
+
+void
+free_cmdline_file(void *cmdline_file)
+{
+ struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
+
+ if (rec) {
+ free_cmdline_file_items(rec);
+ free(rec);
+ }
+}
+
+void
+yl_opt_erase(struct yl_opt *yo)
+{
+ ly_bool interactive;
+
+ interactive = yo->interactive;
+
+ /* data */
+ ly_set_erase(&yo->data_inputs, free_cmdline_file);
+ ly_in_free(yo->data_operational.in, 1);
+ ly_set_erase(&yo->data_xpath, NULL);
+
+ /* schema */
+ ly_set_erase(&yo->schema_features, yl_schema_features_free);
+ ly_set_erase(&yo->schema_modules, NULL);
+
+ /* context */
+ free(yo->searchpaths);
+
+ /* --reply-rpc */
+ ly_in_free(yo->reply_rpc.in, 1);
+
+ ly_out_free(yo->out, NULL, yo->out_stdout ? 0 : 1);
+
+ free_cmdline(yo->argv);
+
+ *yo = (const struct yl_opt) {
+ 0
+ };
+ yo->interactive = interactive;
+}
+
+int
+yl_opt_update_schema_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "yang")) {
+ yo->schema_out_format = LYS_OUT_YANG;
+ yo->data_out_format = 0;
+ } else if (!strcasecmp(arg, "yin")) {
+ yo->schema_out_format = LYS_OUT_YIN;
+ yo->data_out_format = 0;
+ } else if (!strcasecmp(arg, "info")) {
+ yo->schema_out_format = LYS_OUT_YANG_COMPILED;
+ yo->data_out_format = 0;
+ } else if (!strcasecmp(arg, "tree")) {
+ yo->schema_out_format = LYS_OUT_TREE;
+ yo->data_out_format = 0;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yl_opt_update_data_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "xml")) {
+ yo->schema_out_format = 0;
+ yo->data_out_format = LYD_XML;
+ } else if (!strcasecmp(arg, "json")) {
+ yo->schema_out_format = 0;
+ yo->data_out_format = LYD_JSON;
+ } else if (!strcasecmp(arg, "lyb")) {
+ yo->schema_out_format = 0;
+ yo->data_out_format = LYD_LYB;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+yl_opt_update_other_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "feature-param")) {
+ yo->feature_param_format = 1;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yl_opt_update_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!yl_opt_update_schema_out_format(arg, yo)) {
+ return 0;
+ }
+ if (!yl_opt_update_data_out_format(arg, yo)) {
+ return 0;
+ }
+ if (!yl_opt_update_other_out_format(arg, yo)) {
+ return 0;
+ }
+
+ YLMSG_E("Unknown output format %s.", arg);
+ return 1;
+}
+
+int
+yl_opt_update_data_type(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "config")) {
+ yo->data_parse_options |= LYD_PARSE_NO_STATE;
+ yo->data_validate_options |= LYD_VALIDATE_NO_STATE;
+ } else if (!strcasecmp(arg, "get")) {
+ yo->data_parse_options |= LYD_PARSE_ONLY;
+ } else if (!strcasecmp(arg, "getconfig") || !strcasecmp(arg, "get-config") || !strcasecmp(arg, "edit")) {
+ yo->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+ } else if (!strcasecmp(arg, "rpc") || !strcasecmp(arg, "action")) {
+ yo->data_type = LYD_TYPE_RPC_YANG;
+ } else if (!strcasecmp(arg, "nc-rpc")) {
+ yo->data_type = LYD_TYPE_RPC_NETCONF;
+ } else if (!strcasecmp(arg, "reply") || !strcasecmp(arg, "rpcreply")) {
+ yo->data_type = LYD_TYPE_REPLY_YANG;
+ } else if (!strcasecmp(arg, "nc-reply")) {
+ yo->data_type = LYD_TYPE_REPLY_NETCONF;
+ } else if (!strcasecmp(arg, "notif") || !strcasecmp(arg, "notification")) {
+ yo->data_type = LYD_TYPE_NOTIF_YANG;
+ } else if (!strcasecmp(arg, "nc-notif")) {
+ yo->data_type = LYD_TYPE_NOTIF_NETCONF;
+ } else if (!strcasecmp(arg, "data")) {
+ /* default option */
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yo_opt_update_data_default(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "all")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
+ } else if (!strcasecmp(arg, "all-tagged")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
+ } else if (!strcasecmp(arg, "trim")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
+ } else if (!strcasecmp(arg, "implicit-tagged")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yo_opt_update_data_in_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "xml")) {
+ yo->data_in_format = LYD_XML;
+ } else if (!strcasecmp(arg, "json")) {
+ yo->data_in_format = LYD_JSON;
+ } else if (!strcasecmp(arg, "lyb")) {
+ yo->data_in_format = LYD_LYB;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+yo_opt_update_make_implemented(struct yl_opt *yo)
+{
+ if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
+ yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
+ yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
+ } else {
+ yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
+ }
+}
+
+void
+yo_opt_update_disable_searchdir(struct yl_opt *yo)
+{
+ if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIR_CWD) {
+ yo->ctx_options &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
+ yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIRS;
+ } else {
+ yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ }
+}
+
+void
+free_cmdline(char *argv[])
+{
+ if (argv) {
+ free(argv[0]);
+ free(argv);
+ }
+}
+
+int
+parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
+{
+ int count;
+ char **vector;
+ char *ptr;
+ char qmark = 0;
+
+ assert(cmdline);
+ assert(argc_p);
+ assert(argv_p);
+
+ /* init */
+ optind = 0; /* reinitialize getopt() */
+ count = 1;
+ vector = malloc((count + 1) * sizeof *vector);
+ vector[0] = strdup(cmdline);
+
+ /* command name */
+ strtok(vector[0], " ");
+
+ /* arguments */
+ while ((ptr = strtok(NULL, " "))) {
+ size_t len;
+ void *r;
+
+ len = strlen(ptr);
+
+ if (qmark) {
+ /* still in quotated text */
+ /* remove NULL termination of the previous token since it is not a token,
+ * but a part of the quotation string */
+ ptr[-1] = ' ';
+
+ if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+ /* end of quotation */
+ qmark = 0;
+ /* shorten the argument by the terminating quotation mark */
+ ptr[len - 1] = '\0';
+ }
+ continue;
+ }
+
+ /* another token in cmdline */
+ ++count;
+ r = realloc(vector, (count + 1) * sizeof *vector);
+ if (!r) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
+ free(vector);
+ return -1;
+ }
+ vector = r;
+ vector[count - 1] = ptr;
+
+ if ((ptr[0] == '"') || (ptr[0] == '\'')) {
+ /* remember the quotation mark to identify end of quotation */
+ qmark = ptr[0];
+
+ /* move the remembered argument after the quotation mark */
+ ++vector[count - 1];
+
+ /* check if the quotation is terminated within this token */
+ if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+ /* end of quotation */
+ qmark = 0;
+ /* shorten the argument by the terminating quotation mark */
+ ptr[len - 1] = '\0';
+ }
+ }
+ }
+ vector[count] = NULL;
+
+ *argc_p = count;
+ *argv_p = vector;
+
+ return 0;
+}
diff --git a/tools/lint/yl_opt.h b/tools/lint/yl_opt.h
new file mode 100644
index 0000000..d66ae4d
--- /dev/null
+++ b/tools/lint/yl_opt.h
@@ -0,0 +1,237 @@
+/**
+ * @file yl_opt.h
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Settings options for the libyang context.
+ *
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef YL_OPT_H_
+#define YL_OPT_H_
+
+#include "parser_data.h" /* enum lyd_type */
+#include "printer_schema.h" /* LYS_OUTFORMAT */
+#include "set.h" /* ly_set */
+
+/**
+ * @brief Data connected with a file provided on a command line as a file path.
+ */
+struct cmdline_file {
+ struct ly_in *in;
+ const char *path;
+ LYD_FORMAT format;
+};
+
+/**
+ * @brief Create and fill the command line file data (struct cmdline_file *).
+ * @param[in] set Optional parameter in case the record is supposed to be added into a set.
+ * @param[in] in Input file handler.
+ * @param[in] path Filepath of the file.
+ * @param[in] format Format of the data file.
+ * @return The created command line file structure.
+ * @return NULL on failure
+ */
+struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
+
+/**
+ * @brief Free the command line file data items.
+ * @param[in,out] rec record to free.
+ */
+void free_cmdline_file_items(struct cmdline_file *rec);
+
+/**
+ * @brief Free the command line file data (struct cmdline_file *).
+ * @param[in,out] cmdline_file The (struct cmdline_file *) to free.
+ */
+void free_cmdline_file(void *cmdline_file);
+
+/**
+ * @brief Context structure to hold and pass variables in a structured form.
+ */
+struct yl_opt {
+ /* Set to 1 if yanglint running in the interactive mode */
+ ly_bool interactive;
+ ly_bool last_one;
+
+ /* libyang context for the run */
+ char *yang_lib_file;
+ uint16_t ctx_options;
+
+ /* prepared output (--output option or stdout by default) */
+ ly_bool out_stdout;
+ struct ly_out *out;
+
+ char *searchpaths;
+ ly_bool searchdir_unset;
+
+ /* options flags */
+ uint8_t list; /* -l option to print list of schemas */
+
+ /* line length for 'tree' format */
+ size_t line_length; /* --tree-line-length */
+
+ uint32_t dbg_groups;
+
+ /*
+ * schema
+ */
+ /* set schema modules' features via --features option (struct schema_features *) */
+ struct ly_set schema_features;
+
+ /* set of loaded schema modules (struct lys_module *) */
+ struct ly_set schema_modules;
+
+ /* options to parse and print schema modules */
+ uint32_t schema_parse_options;
+ uint32_t schema_print_options;
+
+ /* specification of printing schema node subtree, option --schema-node */
+ char *schema_node_path;
+ char *submodule;
+
+ /* name of file containing explicit context passed to callback
+ * for schema-mount extension. This also causes a callback to
+ * be registered.
+ */
+ char *schema_context_filename;
+ ly_bool extdata_unset;
+
+ /* value of --format in case of schema format */
+ LYS_OUTFORMAT schema_out_format;
+ ly_bool feature_param_format;
+ ly_bool feature_print_all;
+
+ /*
+ * data
+ */
+ /* various options based on --type option */
+ enum lyd_type data_type;
+ uint32_t data_parse_options;
+ uint32_t data_validate_options;
+ uint32_t data_print_options;
+
+ /* flag for --merge option */
+ uint8_t data_merge;
+
+ /* value of --format in case of data format */
+ LYD_FORMAT data_out_format;
+
+ /* value of --in-format in case of data format */
+ LYD_FORMAT data_in_format;
+
+ /* input data files (struct cmdline_file *) */
+ struct ly_set data_inputs;
+
+ /* storage for --operational */
+ struct cmdline_file data_operational;
+
+ /* storage for --reply-rpc */
+ struct cmdline_file reply_rpc;
+
+ /* storage for --data-xpath */
+ struct ly_set data_xpath;
+
+ char **argv;
+};
+
+/**
+ * @brief Erase all values in @p opt.
+ *
+ * The yl_opt.interactive item is not deleted.
+ *
+ * @param[in,out] yo Option context to erase.
+ */
+void yl_opt_erase(struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the schema --format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example yang, yin, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_schema_out_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example xml, json, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_data_out_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the general --format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example yang, xml, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_out_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --type parameter.
+ *
+ * @param[in] arg Format parameter argument (for example config, rpc, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_data_type(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --default parameter.
+ *
+ * @param[in] arg Format parameter argument (for example all, trim, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yo_opt_update_data_default(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --in-format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example xml, json, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yo_opt_update_data_in_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the --make-implemented parameter.
+ *
+ * @param[in,out] yo yanglint options used to update.
+ */
+void yo_opt_update_make_implemented(struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the --disable-searchdir parameter.
+ *
+ * @param[in,out] yo yanglint options used to update.
+ */
+void yo_opt_update_disable_searchdir(struct yl_opt *yo);
+
+/**
+ * @brief Helper function to prepare argc, argv pair from a command line string.
+ *
+ * @param[in] cmdline Complete command line string.
+ * @param[out] argc_p Pointer to store argc value.
+ * @param[out] argv_p Pointer to store argv vector.
+ * @return 0 on success, non-zero on failure.
+ */
+int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
+
+/**
+ * @brief Destructor for the argument vector prepared by ::parse_cmdline().
+ *
+ * @param[in,out] argv Argument vector to destroy.
+ */
+void free_cmdline(char *argv[]);
+
+#endif /* YL_OPT_H_ */
diff --git a/tools/lint/yl_schema_features.c b/tools/lint/yl_schema_features.c
new file mode 100644
index 0000000..74f88b9
--- /dev/null
+++ b/tools/lint/yl_schema_features.c
@@ -0,0 +1,212 @@
+/**
+ * @file yl_schema_features.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Control features for the schema.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdlib.h> /* calloc */
+#include <string.h> /* strcmp */
+
+#include "compat.h" /* strndup */
+#include "set.h" /* ly_set */
+
+#include "common.h"
+#include "yl_schema_features.h"
+
+void
+yl_schema_features_free(void *flist)
+{
+ struct yl_schema_features *rec = (struct yl_schema_features *)flist;
+
+ if (rec) {
+ free(rec->mod_name);
+ if (rec->features) {
+ for (uint32_t u = 0; rec->features[u]; ++u) {
+ free(rec->features[u]);
+ }
+ free(rec->features);
+ }
+ free(rec);
+ }
+}
+
+void
+get_features(const struct ly_set *fset, const char *module, const char ***features)
+{
+ /* get features list for this module */
+ for (uint32_t u = 0; u < fset->count; ++u) {
+ struct yl_schema_features *sf = (struct yl_schema_features *)fset->objs[u];
+
+ if (!strcmp(module, sf->mod_name)) {
+ /* matched module - explicitly set features */
+ *features = (const char **)sf->features;
+ sf->applied = 1;
+ return;
+ }
+ }
+
+ /* features not set so disable all */
+ *features = NULL;
+}
+
+int
+parse_features(const char *fstring, struct ly_set *fset)
+{
+ struct yl_schema_features *rec = NULL;
+ uint32_t count;
+ char *p, **fp;
+
+ rec = calloc(1, sizeof *rec);
+ if (!rec) {
+ YLMSG_E("Unable to allocate features information record (%s).", strerror(errno));
+ goto error;
+ }
+
+ /* fill the record */
+ p = strchr(fstring, ':');
+ if (!p) {
+ YLMSG_E("Invalid format of the features specification (%s).", fstring);
+ goto error;
+ }
+ rec->mod_name = strndup(fstring, p - fstring);
+
+ count = 0;
+ while (p) {
+ size_t len = 0;
+ char *token = p + 1;
+
+ p = strchr(token, ',');
+ if (!p) {
+ /* the last item, if any */
+ len = strlen(token);
+ } else {
+ len = p - token;
+ }
+
+ if (len) {
+ fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+ if (!fp) {
+ YLMSG_E("Unable to store features list information (%s).", strerror(errno));
+ goto error;
+ }
+ rec->features = fp;
+ fp = &rec->features[count++]; /* array item to set */
+ (*fp) = strndup(token, len);
+ }
+ }
+
+ /* terminating NULL */
+ fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+ if (!fp) {
+ YLMSG_E("Unable to store features list information (%s).", strerror(errno));
+ goto error;
+ }
+ rec->features = fp;
+ rec->features[count++] = NULL;
+
+ /* Store record to the output set. */
+ if (ly_set_add(fset, rec, 1, NULL)) {
+ YLMSG_E("Unable to store features information (%s).", strerror(errno));
+ goto error;
+ }
+ rec = NULL;
+
+ return 0;
+
+error:
+ yl_schema_features_free(rec);
+ return -1;
+}
+
+void
+print_features(struct ly_out *out, const struct lys_module *mod)
+{
+ struct lysp_feature *f;
+ uint32_t idx;
+ size_t max_len, len;
+
+ ly_print(out, "%s:\n", mod->name);
+
+ /* get max len, so the statuses of all the features will be aligned */
+ max_len = 0, idx = 0, f = NULL;
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ len = strlen(f->name);
+ max_len = (max_len > len) ? max_len : len;
+ }
+ if (!max_len) {
+ ly_print(out, "\t(none)\n");
+ return;
+ }
+
+ /* print features */
+ idx = 0, f = NULL;
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ ly_print(out, "\t%-*s (%s)\n", (int)max_len, f->name, lys_feature_value(mod, f->name) ? "off" : "on");
+ }
+}
+
+void
+print_feature_param(struct ly_out *out, const struct lys_module *mod)
+{
+ struct lysp_feature *f = NULL;
+ uint32_t idx = 0;
+ uint8_t first = 1;
+
+ ly_print(out, " -F %s:", mod->name);
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ if (first) {
+ ly_print(out, "%s", f->name);
+ first = 0;
+ } else {
+ ly_print(out, ",%s", f->name);
+ }
+ }
+}
+
+void
+print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t feature_param)
+{
+ uint32_t i;
+ struct lys_module *mod;
+ uint8_t first;
+
+ /* Print features for all implemented modules. */
+ first = 1;
+ i = 0;
+ while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
+ if (!mod->implemented) {
+ continue;
+ }
+ if (first) {
+ print_features(out, mod);
+ first = 0;
+ } else {
+ ly_print(out, "\n");
+ print_features(out, mod);
+ }
+ }
+
+ if (!feature_param) {
+ return;
+ }
+ ly_print(out, "\n");
+
+ /* Print features for all implemented modules in 'feature-param' format. */
+ i = 0;
+ while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
+ if (mod->implemented) {
+ print_feature_param(out, mod);
+ }
+ }
+}
diff --git a/tools/lint/yl_schema_features.h b/tools/lint/yl_schema_features.h
new file mode 100644
index 0000000..7bfe9fd
--- /dev/null
+++ b/tools/lint/yl_schema_features.h
@@ -0,0 +1,84 @@
+/**
+ * @file yl_schema_features.h
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Control features for the schema.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef YL_SCHEMA_FEATURES_H_
+#define YL_SCHEMA_FEATURES_H_
+
+#include <stdint.h>
+
+struct ly_set;
+struct lys_module;
+struct ly_out;
+struct ly_ctx;
+
+/**
+ * @brief Storage for the list of the features (their names) in a specific YANG module.
+ */
+struct yl_schema_features {
+ char *mod_name;
+ char **features;
+ uint8_t applied;
+};
+
+/**
+ * @brief Free the schema features list (struct schema_features *)
+ * @param[in,out] flist The (struct schema_features *) to free.
+ */
+void yl_schema_features_free(void *flist);
+
+/**
+ * @brief Get the list of features connected with the specific YANG module.
+ *
+ * @param[in] fset The set of features information (struct schema_features *).
+ * @param[in] module Name of the YANG module which features should be found.
+ * @param[out] features Pointer to the list of features being returned.
+ */
+void get_features(const struct ly_set *fset, const char *module, const char ***features);
+
+/**
+ * @brief Parse features being specified for the specific YANG module.
+ *
+ * Format of the input @p fstring is as follows: "<module_name>:[<feature>,]*"
+ *
+ * @param[in] fstring Input string to be parsed.
+ * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
+ */
+int parse_features(const char *fstring, struct ly_set *fset);
+
+/**
+ * @brief Print all features of a single module.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] mod Module which can contains the features.
+ */
+void print_features(struct ly_out *out, const struct lys_module *mod);
+
+/**
+ * @brief Print all features in the 'feature-param' format.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] mod Module which can contains the features.
+ */
+void print_feature_param(struct ly_out *out, const struct lys_module *mod);
+
+/**
+ * @brief Print all features of all implemented modules.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] ctx Libyang context.
+ * @param[in] feature_param Flag expressing whether to print features parameter.
+ */
+void print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t feature_param);
+
+#endif /* YL_SCHEMA_FEATURES_H_ */