yanglint REFACTOR new yl_opt and cmd callbacks

The yanglint options were stored in non-interactive mode in the
structure named 'context'. This structure was renamed to yl_opt
and is now also used in the interactive mode, which has also been
expanded with new callbacks. These callbacks are the basis for
further refactoring commits, which aim to reduce duplicate code
between interactive and non-interactive modes to a minimum.
diff --git a/tools/lint/CMakeLists.txt b/tools/lint/CMakeLists.txt
index dd052d8..9ff39b2 100644
--- a/tools/lint/CMakeLists.txt
+++ b/tools/lint/CMakeLists.txt
@@ -18,6 +18,10 @@
     cmd_print.c
     cmd_searchpath.c
     cmd_extdata.c
+    cmd_help.c
+    cmd_verb.c
+    cmd_debug.c
+    yl_opt.c
     common.c
 )
 if(YANGLINT_INTERACTIVE)
diff --git a/tools/lint/cmd.c b/tools/lint/cmd.c
index cefd657..872e3db 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,67 +31,6 @@
 COMMAND commands[];
 extern int done;
 
-#ifndef NDEBUG
-
-void
-cmd_debug_help(void)
-{
-    printf("Usage: debug (dict | xpath | dep-sets)+\n");
-}
-
-void
-cmd_debug(struct ly_ctx **UNUSED(ctx), const char *cmdline)
-{
-    int argc = 0;
-    char **argv = NULL;
-    int opt, opt_index;
-    struct option options[] = {
-        {"help", no_argument, NULL, 'h'},
-        {NULL, 0, NULL, 0}
-    };
-    uint32_t dbg_groups = 0;
-
-    if (parse_cmdline(cmdline, &argc, &argv)) {
-        goto cleanup;
-    }
-
-    while ((opt = getopt_long(argc, argv, commands[CMD_DEBUG].optstring, options, &opt_index)) != -1) {
-        switch (opt) {
-        case 'h':
-            cmd_debug_help();
-            goto cleanup;
-        default:
-            YLMSG_E("Unknown option.\n");
-            goto cleanup;
-        }
-    }
-    if (argc == optind) {
-        /* no argument */
-        cmd_debug_help();
-        goto cleanup;
-    }
-
-    for (int i = 0; i < argc - optind; i++) {
-        if (!strcasecmp("dict", argv[optind + i])) {
-            dbg_groups |= LY_LDGDICT;
-        } else if (!strcasecmp("xpath", argv[optind + i])) {
-            dbg_groups |= LY_LDGXPATH;
-        } else if (!strcasecmp("dep-sets", argv[optind + i])) {
-            dbg_groups |= LY_LDGDEPSETS;
-        } 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_free(void)
 {
@@ -103,173 +43,68 @@
     }
 }
 
-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, commands[CMD_VERB].optstring, 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, commands[CMD_HELP].optstring, 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, NULL, "Display commands description", "h"},
-    {"add", cmd_add, cmd_add_help, NULL, "Add a new module from a specific file", "DF:hiX"},
-    {"load", cmd_load, cmd_load_help, NULL, "Load a new schema from the searchdirs", "F:hiX"},
-    {"print", cmd_print, cmd_print_help, NULL, "Print a module", "f:hL:o:P:q"},
-    {"data", cmd_data, cmd_data_help, NULL, "Load, validate and optionally print instance data", "d:ef:F:hmo:O:R:r:nt:x:"},
-    {"list", cmd_list, cmd_list_help, NULL, "List all the loaded modules", "f:h"},
-    {"feature", cmd_feature, cmd_feature_help, NULL, "Print all features of module(s) with their state", "haf"},
-    {"searchpath", cmd_searchpath, cmd_searchpath_help, NULL, "Print/set the search path(s) for schemas", "ch"},
-    {"extdata", cmd_extdata, cmd_extdata_help, cmd_extdata_free, "Set the specific data required by an extension", "ch"},
-    {"clear", cmd_clear, cmd_clear_help, NULL, "Clear the context - remove all the loaded modules", "iyhY:"},
-    {"verb", cmd_verb, cmd_verb_help, NULL, "Change verbosity", "h"},
+    {
+        "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_exec, cmd_data_fin, 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, NULL, "Display specific debug message groups", "h"},
+    {
+        "debug", cmd_debug_opt, cmd_debug_dep, cmd_debug_exec, cmd_debug_fin, cmd_debug_help, NULL,
+        "Display specific debug message groups", "h"
+    },
 #endif
-    {"quit", cmd_quit, NULL, NULL, "Quit the program", "h"},
+    {"quit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
     /* synonyms for previous commands */
-    {"?", cmd_help, NULL, NULL, "Display commands description", "h"},
-    {"exit", cmd_quit, NULL, NULL, "Quit the program", "h"},
-    {NULL, NULL, 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 5898cfe..9cf816d 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,17 +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. */
-    void (*free_func)(void);                         /* Freeing global variables allocated by the command. */
-    char *helpstring;                                /* Documentation for this function. */
-    char *optstring;                                 /* Option characters used in function getopt_long. */
+    /* 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;
 
 /**
@@ -62,40 +90,307 @@
 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_exec(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_fin(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 Generate features for the command 'add'.
+ *
+ * @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_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 */
-void cmd_extdata(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @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_exec(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_fin(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 37bf78c..3904c7a 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,6 +18,7 @@
 
 #include "cmd.h"
 
+#include <assert.h>
 #include <getopt.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -25,6 +27,7 @@
 #include "libyang.h"
 
 #include "common.h"
+#include "yl_opt.h"
 
 void
 cmd_add_help(void)
@@ -48,11 +51,10 @@
             "                  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'},
@@ -62,121 +64,123 @@
         {"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, commands[CMD_ADD].optstring, 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) {
+            if (yo->ctx_options & 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;
+            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 {
-                options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+                yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
             }
             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;
+            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 'X': /* --extended-leafref */
-            options_ctx |= LY_CTX_LEAFREF_EXTENDED;
+            yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
             break;
 
         default:
             YLMSG_E("Unknown option.\n");
-            goto cleanup;
+            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;
+}
+
+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, *module;
+    const char **features = NULL;
+    struct ly_in *in = NULL;
+
+    assert(posv);
+
+    if (yo->ctx_options) {
+        ly_ctx_set_options(*ctx, yo->ctx_options);
+        yo->ctx_options = 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;
-
-        if (parse_schema_path(argv[optind + i], &dir, &module)) {
-            goto cleanup;
-        }
-
-        /* add temporarily also the path of the module itself */
-        if (ly_ctx_set_searchdir(*ctx, dir) == LY_EEXIST) {
-            path_unset = 0;
-        }
-
-        /* get features list for this module */
-        if (!fset.count) {
-            features = all_features;
-        } else {
-            get_features(&fset, module, &features);
-        }
-
-        /* temporary cleanup */
-        free(dir);
-        free(module);
-
-        /* prepare input handler */
-        ret = ly_in_new_filepath(argv[optind + i], 0, &in);
-        if (ret) {
-            goto cleanup;
-        }
-
-        /* parse the file */
-        ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, NULL);
-        ly_in_free(in, 1);
-        ly_ctx_unset_searchdir_last(*ctx, path_unset);
-
-        if (ret) {
-            /* libyang printed the error messages */
-            goto cleanup;
-        }
+    if (parse_schema_path(posv, &dir, &module)) {
+        return 1;
     }
 
-cleanup:
-    if (options_ctx) {
-        ly_ctx_unset_options(*ctx, options_ctx);
+    /* add temporarily also the path of the module itself */
+    if (ly_ctx_set_searchdir(*ctx, dir) == LY_EEXIST) {
+        path_unset = 0;
     }
-    ly_set_erase(&fset, free_features);
-    free_cmdline(argv);
+
+    /* get features list for this module */
+    if (!yo->schema_features.count) {
+        features = all_features;
+    } else {
+        get_features(&yo->schema_features, module, &features);
+    }
+
+    /* temporary cleanup */
+    free(dir);
+    free(module);
+
+    /* prepare input handler */
+    ret = ly_in_new_filepath(posv, 0, &in);
+    if (ret) {
+        return 1;
+    }
+
+    /* 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);
+
+    return ret;
 }
diff --git a/tools/lint/cmd_clear.c b/tools/lint/cmd_clear.c
index 403dc26..233cc92 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)
@@ -45,6 +47,74 @@
             "                are used, but then deleted.\n");
 }
 
+int
+cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+    int rc = 0, argc = 0;
+    int opt, opt_index;
+    struct option options[] = {
+        {"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}
+    };
+
+    yo->ctx_options = LY_CTX_NO_YANGLIBRARY;
+
+    if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+        return rc;
+    }
+
+    while ((opt = getopt_long(argc, yo->argv, commands[CMD_CLEAR].optstring, options, &opt_index)) != -1) {
+        switch (opt) {
+        case 'i':
+            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;
+            }
+            break;
+        case 'y':
+            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.\n");
+                return 1;
+            }
+            break;
+        case 'h':
+            cmd_clear_help();
+            return 1;
+        default:
+            YLMSG_E("Unknown option.\n");
+            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.\n");
+        return 1;
+    }
+
+    return 0;
+}
+
 /**
  * @brief Convert searchpaths into single string.
  *
@@ -70,68 +140,25 @@
     return rc;
 }
 
-void
-cmd_clear(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
 {
-    int argc = 0;
-    char **argv = NULL;
-    int opt, opt_index;
-    struct option options[] = {
-        {"make-implemented",    no_argument, NULL, 'i'},
-        {"yang-library",        no_argument, NULL, 'y'},
-        {"yang-library-file",   no_argument, NULL, 'Y'},
-        {"help",             no_argument, NULL, 'h'},
-        {NULL, 0, NULL, 0}
-    };
-    uint16_t options_ctx = LY_CTX_NO_YANGLIBRARY;
+    (void) posv;
     struct ly_ctx *ctx_new = NULL;
-    char *ylibfile = NULL;
-    char *searchpaths = NULL;
 
-    if (parse_cmdline(cmdline, &argc, &argv)) {
-        goto cleanup;
-    }
-
-    while ((opt = getopt_long(argc, 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;
-            } else {
-                options_ctx |= LY_CTX_REF_IMPLEMENTED;
-            }
-            break;
-        case 'y':
-            options_ctx &= ~LY_CTX_NO_YANGLIBRARY;
-            break;
-        case 'Y':
-            options_ctx &= ~LY_CTX_NO_YANGLIBRARY;
-            ylibfile = optarg;
-            break;
-        case 'h':
-            cmd_clear_help();
-            goto cleanup;
-        default:
-            YLMSG_E("Unknown option.\n");
-            goto cleanup;
-        }
-    }
-
-    if (ylibfile) {
-        /* Create context according to the provided yang-library data in a file but use already defined searchpaths. */
-        if (searchpaths_to_str(*ctx, &searchpaths)) {
+    if (yo->yang_lib_file) {
+        if (searchpaths_to_str(*ctx, &yo->searchpaths)) {
             YLMSG_E("Storing searchpaths failed.\n");
-            goto cleanup;
+            return 1;
         }
-        if (ly_ctx_new_ylpath(searchpaths, ylibfile, LYD_UNKNOWN, options_ctx, &ctx_new)) {
+        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.\n");
-            goto cleanup;
+            return 1;
         }
     } else {
-        if (ly_ctx_new(NULL, options_ctx, &ctx_new)) {
+        if (ly_ctx_new(NULL, yo->ctx_options, &ctx_new)) {
             YLMSG_W("Failed to create context.\n");
-            goto cleanup;
+            return 1;
         }
     }
 
@@ -141,7 +168,5 @@
     ly_ctx_destroy(*ctx);
     *ctx = ctx_new;
 
-cleanup:
-    free_cmdline(argv);
-    free(searchpaths);
+    return 0;
 }
diff --git a/tools/lint/cmd_data.c b/tools/lint/cmd_data.c
index 75b4760..423ba8f 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,6 +29,7 @@
 #include "libyang.h"
 
 #include "common.h"
+#include "yl_opt.h"
 
 static void
 cmd_data_help_header(void)
@@ -145,11 +148,10 @@
     printf("\n");
 }
 
-void
-cmd_data(struct ly_ctx **ctx, const char *cmdline)
+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'},
@@ -167,248 +169,261 @@
         {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 = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
-    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 cmdline_file *reply_rpc = 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, commands[CMD_DATA].optstring, 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;
+                yo->data_print_options = (yo->data_print_options & ~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;
+                yo->data_print_options = (yo->data_print_options & ~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;
+                yo->data_print_options = (yo->data_print_options & ~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;
+                yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
             } else {
                 YLMSG_E("Unknown default mode %s\n", optarg);
                 cmd_data_help_default();
-                goto cleanup;
+                return 1;
             }
             break;
         case 'f': /* --format */
             if (!strcasecmp(optarg, "xml")) {
-                outformat = LYD_XML;
+                yo->data_out_format = LYD_XML;
             } else if (!strcasecmp(optarg, "json")) {
-                outformat = LYD_JSON;
+                yo->data_out_format = LYD_JSON;
             } else if (!strcasecmp(optarg, "lyb")) {
-                outformat = LYD_LYB;
+                yo->data_out_format = LYD_LYB;
             } else {
                 YLMSG_E("Unknown output format %s\n", optarg);
                 cmd_data_help_format();
-                goto cleanup;
+                return 1;
             }
             break;
         case 'F': /* --in-format */
             if (!strcasecmp(optarg, "xml")) {
-                informat = LYD_XML;
+                yo->data_in_format = LYD_XML;
             } else if (!strcasecmp(optarg, "json")) {
-                informat = LYD_JSON;
+                yo->data_in_format = LYD_JSON;
             } else if (!strcasecmp(optarg, "lyb")) {
-                informat = LYD_LYB;
+                yo->data_in_format = LYD_LYB;
             } else {
                 YLMSG_E("Unknown input format %s\n", optarg);
                 cmd_data_help_in_format();
-                goto cleanup;
+                return 1;
             }
             break;
         case 'o': /* --output */
-            if (out) {
+            if (yo->out) {
                 YLMSG_E("Only a single output can be specified.\n");
-                goto cleanup;
+                return 1;
             } else {
-                if (ly_out_new_filepath(optarg, &out)) {
+                if (ly_out_new_filepath(optarg, &yo->out)) {
                     YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
-                    goto cleanup;
+                    return 1;
                 }
             }
             break;
-        case 'O': { /* --operational */
-            struct ly_in *in;
-            LYD_FORMAT f = LYD_UNKNOWN;
-
-            if (operational) {
+        case 'O':   /* --operational */
+            if (yo->data_operational.path) {
                 YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
-                goto cleanup;
+                return 1;
             }
-            if (get_input(optarg, NULL, &f, &in)) {
-                goto cleanup;
-            }
-            operational = fill_cmdline_file(NULL, in, optarg, f);
+            yo->data_operational.path = optarg;
             break;
-        } /* case 'O' */
-        case 'R': { /* --reply-rpc */
-            struct ly_in *in;
-            LYD_FORMAT f = LYD_UNKNOWN;
-
-            if (reply_rpc) {
+        case 'R':   /* --reply-rpc */
+            if (yo->reply_rpc.path) {
                 YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.\n");
-                goto cleanup;
+                return 1;
             }
-            if (get_input(optarg, NULL, &f, &in)) {
-                goto cleanup;
-            }
-            reply_rpc = fill_cmdline_file(NULL, in, optarg, f);
+            yo->reply_rpc.path = optarg;
             break;
-        } /* case 'R' */
         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;
+                return 1;
             }
 
             if (!strcasecmp(optarg, "config")) {
-                options_parse |= LYD_PARSE_NO_STATE;
-                options_validate |= LYD_VALIDATE_NO_STATE;
+                yo->data_parse_options |= LYD_PARSE_NO_STATE;
+                yo->data_validate_options |= LYD_VALIDATE_NO_STATE;
             } else if (!strcasecmp(optarg, "get")) {
-                options_parse |= LYD_PARSE_ONLY;
+                yo->data_parse_options |= LYD_PARSE_ONLY;
             } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config") || !strcasecmp(optarg, "edit")) {
-                options_parse |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+                yo->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
             } else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
-                data_type = LYD_TYPE_RPC_YANG;
+                yo->data_type = LYD_TYPE_RPC_YANG;
             } else if (!strcasecmp(optarg, "nc-rpc")) {
-                data_type = LYD_TYPE_RPC_NETCONF;
+                yo->data_type = LYD_TYPE_RPC_NETCONF;
             } else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
-                data_type = LYD_TYPE_REPLY_YANG;
+                yo->data_type = LYD_TYPE_REPLY_YANG;
             } else if (!strcasecmp(optarg, "nc-reply")) {
-                data_type = LYD_TYPE_REPLY_NETCONF;
+                yo->data_type = LYD_TYPE_REPLY_NETCONF;
             } else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
-                data_type = LYD_TYPE_NOTIF_YANG;
+                yo->data_type = LYD_TYPE_NOTIF_YANG;
             } else if (!strcasecmp(optarg, "nc-notif")) {
-                data_type = LYD_TYPE_NOTIF_NETCONF;
+                yo->data_type = LYD_TYPE_NOTIF_NETCONF;
             } else if (!strcasecmp(optarg, "data")) {
                 /* default option */
             } else {
                 YLMSG_E("Unknown data tree type %s.\n", optarg);
                 cmd_data_help_type();
-                goto cleanup;
+                return 1;
             }
 
             data_type_set = 1;
             break;
 
         case 'x': /* --xpath */
-            if (ly_set_add(&xpaths, optarg, 0, NULL)) {
+            if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
                 YLMSG_E("Storing XPath \"%s\" failed.\n", optarg);
-                goto cleanup;
+                return 1;
             }
             break;
 
         case 'h': /* --help */
             cmd_data_help();
-            goto cleanup;
+            return 1;
         default:
             YLMSG_E("Unknown option.\n");
-            goto cleanup;
+            return 1;
         }
     }
 
-    if (optind == argc) {
+    *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.\n");
-        goto cleanup;
+        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 */
             YLMSG_W("The --merge option has effect only for 'data' and 'config' TYPEs\n");
-            data_merge = 0;
+            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) {
+    if (yo->data_xpath.count && yo->data_out_format) {
         YLMSG_E("The --format option cannot be combined with --xpath option.\n");
-        cmd_data_help_xpath();
-        goto cleanup;
+        if (yo->interactive) {
+            cmd_data_help_xpath();
+        }
+        return 1;
     }
-    if (xpaths.count && (options_print & LYD_PRINT_WD_MASK)) {
+    if (yo->data_xpath.count && (yo->data_print_options & LYD_PRINT_WD_MASK)) {
         YLMSG_E("The --default option cannot be combined with --xpath option.\n");
-        cmd_data_help_xpath();
-        goto cleanup;
+        if (yo->interactive) {
+            cmd_data_help_xpath();
+        }
+        return 1;
     }
 
-    if (operational && !data_type) {
+    if (yo->data_operational.path && !yo->data_type) {
         YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notifications input data types.\n");
-        free_cmdline_file(operational);
-        operational = NULL;
+        yo->data_operational.path = NULL;
     }
 
-    if (reply_rpc && (data_type != LYD_TYPE_REPLY_NETCONF)) {
+    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.\n");
-        free_cmdline_file(operational);
-        operational = NULL;
-    } else if (!reply_rpc && (data_type == LYD_TYPE_REPLY_NETCONF)) {
+        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.\n");
-        goto cleanup;
+        return 1;
     }
 
-    if (!out && (outformat == LYD_LYB)) {
+    if (!yo->out && (yo->data_out_format == LYD_LYB)) {
         YLMSG_E("The LYB format requires the -o option specified.\n");
-        goto cleanup;
-    }
-
-    /* process input data files provided as standalone command line arguments */
-    for (int i = 0; i < argc - optind; i++) {
-        struct ly_in *in;
-
-        if (get_input(argv[optind + i], NULL, &informat, &in)) {
-            goto cleanup;
-        }
-
-        if (!fill_cmdline_file(&inputs, in, argv[optind + i], informat)) {
-            ly_in_free(in, 1);
-            goto cleanup;
-        }
+        return 1;
     }
 
     /* default output stream */
-    if (!out) {
-        if (ly_out_new_file(stdout, &out)) {
+    if (!yo->out) {
+        if (ly_out_new_file(stdout, &yo->out)) {
             YLMSG_E("Unable to set stdout as output.\n");
-            goto cleanup;
+            return 1;
+        }
+        yo->out_stdout = 1;
+    }
+
+    /* 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;
         }
     }
 
-    /* parse, validate and print data */
-    if (process_data(*ctx, data_type, data_merge, outformat, out, options_parse, options_validate, options_print,
-            operational, reply_rpc, &inputs, &xpaths)) {
-        goto cleanup;
+    return 0;
+}
+
+int
+cmd_data_exec(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 */
+    if (get_input(posv, NULL, &format_data, &in)) {
+        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);
+    if (!fill_cmdline_file(&yo->data_inputs, in, posv, format_data)) {
+        ly_in_free(in, 1);
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+cmd_data_fin(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+    /* parse, validate and print data */
+    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;
+    }
+
+    return 0;
 }
diff --git a/tools/lint/cmd_debug.c b/tools/lint/cmd_debug.c
new file mode 100644
index 0000000..6c59586
--- /dev/null
+++ b/tools/lint/cmd_debug.c
@@ -0,0 +1,109 @@
+/**
+ * @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"
+
+void
+cmd_debug_help(void)
+{
+    printf("Usage: debug (dict | xpath | dep-sets)+\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.\n");
+            return 1;
+        }
+    }
+
+    *posv = &yo->argv[optind];
+    *posc = argc - optind;
+
+    return 0;
+}
+
+int
+cmd_debug_dep(struct yl_opt *yo, int posc)
+{
+    (void) yo;
+
+    if (!posc) {
+        /* no argument */
+        cmd_debug_help();
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+cmd_debug_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+    (void) ctx;
+
+    assert(posv);
+
+    if (!strcasecmp("dict", posv)) {
+        yo->dbg_groups |= LY_LDGDICT;
+    } else if (!strcasecmp("xpath", posv)) {
+        yo->dbg_groups |= LY_LDGXPATH;
+    } else if (!strcasecmp("dep-sets", posv)) {
+        yo->dbg_groups |= LY_LDGDEPSETS;
+    } else {
+        YLMSG_E("Unknown debug group \"%s\"\n", posv);
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+cmd_debug_fin(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
index a3b1cfd..fe14f4a 100644
--- a/tools/lint/cmd_extdata.c
+++ b/tools/lint/cmd_extdata.c
@@ -24,6 +24,7 @@
 #include "libyang.h"
 
 #include "common.h"
+#include "yl_opt.h"
 
 char *filename;
 
@@ -43,59 +44,72 @@
             "                 expected in the file. File format is guessed.\n");
 }
 
-void
-cmd_extdata(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_extdata_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;
-    int8_t file_count = 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, commands[CMD_EXTDATA].optstring, options, &opt_index)) != -1) {
+    while ((opt = getopt_long(argc, yo->argv, commands[CMD_EXTDATA].optstring, options, &opt_index)) != -1) {
         switch (opt) {
         case 'c':
-            ly_ctx_set_ext_data_clb(*ctx, NULL, NULL);
+            yo->extdata_unset = 1;
             free(filename);
             filename = NULL;
-            cleared = 1;
             break;
         case 'h':
             cmd_extdata_help();
-            goto cleanup;
+            return 1;
         default:
             YLMSG_E("Unknown option.\n");
-            goto cleanup;
+            return 1;
         }
     }
 
-    file_count = argc - 1 - cleared;
+    *posv = &yo->argv[optind];
+    *posc = argc - optind;
 
-    if (!cleared && (file_count == 0)) {
+    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.\n");
+        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 (file_count == 1) {
+    } else if (posv) {
         /* set callback providing run-time extension instance data */
         free(filename);
-        filename = strdup(argv[optind]);
+        filename = strdup(posv);
         if (!filename) {
             YLMSG_E("Memory allocation error.\n");
-            goto cleanup;
+            return 1;
         }
         ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, filename);
-    } else if (!cleared) {
-        YLMSG_E("Only one file must be entered.\n");
     }
 
-cleanup:
-    free_cmdline(argv);
+    return 0;
 }
diff --git a/tools/lint/cmd_feature.c b/tools/lint/cmd_feature.c
index 383cf93..a2ca07e 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,7 @@
 #include "libyang.h"
 
 #include "common.h"
+#include "yl_opt.h"
 
 void
 cmd_feature_help(void)
@@ -37,17 +39,11 @@
             "                  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 +51,115 @@
         {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, commands[CMD_FEATURE].optstring, 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;
+            return 1;
         }
     }
 
-    if (ly_out_new_file(stdout, &out)) {
+    *posv = &yo->argv[optind];
+    *posc = argc - optind;
+
+    return 0;
+}
+
+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.\n");
-        goto cleanup;
+        return 1;
     }
+    yo->out_stdout = 1;
 
-    if (print_all) {
-        if (print_all_features(out, *ctx, generate_features, &features_output)) {
-            YLMSG_E("Printing all features failed.\n");
-            goto cleanup;
-        }
-        if (generate_features) {
-            printf("%s\n", features_output);
-        }
-        goto cleanup;
-    }
-
-    if (argc == optind) {
+    if (yo->interactive && !yo->feature_print_all && !posc) {
         YLMSG_E("Missing modules to print.\n");
+        return 1;
+    }
+
+    if (yo->feature_print_all && posc) {
+        YLMSG_E("No positional arguments are allowed.\n");
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+    int rc = 0;
+    struct ly_set set = {0};
+    const struct lys_module *mod;
+
+    if (yo->feature_print_all) {
+        if (print_all_features(yo->out, *ctx, yo->feature_param_format, &yo->features_output)) {
+            YLMSG_E("Printing all features failed.\n");
+            rc = 1;
+            goto cleanup;
+        }
+        if (yo->feature_param_format) {
+            printf("%s\n", yo->features_output);
+        }
         goto cleanup;
     }
 
-    for (i = 0; i < argc - optind; i++) {
-        /* always erase the set, so the previous module's features don't carry over to the next module's features */
-        ly_set_erase(&set, NULL);
+    /* always erase the set, so the previous module's features don't carry over to the next module's features */
+    ly_set_erase(&set, NULL);
 
-        mod = ly_ctx_get_module_latest(*ctx, argv[optind + i]);
-        if (!mod) {
-            YLMSG_E("Module \"%s\" not found.\n", argv[optind + i]);
-            goto cleanup;
-        }
-
-        /* collect features of the module */
-        if (collect_features(mod, &set)) {
-            goto cleanup;
-        }
-
-        if (generate_features) {
-            if (generate_features_output(mod, &set, &features_output)) {
-                goto cleanup;
-            }
-            /* don't print features and their state of each module if generating features parameter */
-            continue;
-        }
-
-        print_features(out, mod, &set);
+    mod = ly_ctx_get_module_latest(*ctx, posv);
+    if (!mod) {
+        YLMSG_E("Module \"%s\" not found.\n", posv);
+        rc = 1;
+        goto cleanup;
     }
 
-    if (generate_features) {
-        printf("%s\n", features_output);
+    /* collect features of the module */
+    if (collect_features(mod, &set)) {
+        rc = 1;
+        goto cleanup;
     }
 
+    if (yo->feature_param_format) {
+        if (generate_features_output(mod, &set, &yo->features_output)) {
+            rc = 1;
+            goto cleanup;
+        }
+        /* don't print features and their state of each module if generating features parameter */
+        goto cleanup;
+    }
+
+    print_features(yo->out, mod, &set);
+
 cleanup:
-    free_cmdline(argv);
-    ly_out_free(out, NULL, 0);
     ly_set_erase(&set, NULL);
-    free(features_output);
+
+    return rc;
+}
+
+int
+cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+    (void) ctx;
+
+    if (yo->feature_param_format) {
+        printf("%s\n", yo->features_output);
+    }
+
+    return 0;
 }
diff --git a/tools/lint/cmd_help.c b/tools/lint/cmd_help.c
new file mode 100644
index 0000000..8a2a597
--- /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.\n");
+            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 77b3ec4..47a7495 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,70 @@
             "                  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, commands[CMD_LIST].optstring, 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);
                 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;
+            return 1;
         }
     }
 
-    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");
-    }
+    *posv = &yo->argv[optind];
+    *posc = argc - optind;
 
-cleanup:
-    free_cmdline(argv);
+    return 0;
+}
+
+int
+cmd_list_dep(struct yl_opt *yo, int posc)
+{
+    if (posc) {
+        YLMSG_E("No positional arguments are allowed.\n");
+        return 1;
+    }
+    if (ly_out_new_file(stdout, &yo->out)) {
+        YLMSG_E("Unable to print to the standard output.\n");
+        return 1;
+    }
+    yo->out_stdout = 1;
+
+    return 0;
+}
+
+int
+cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+    (void) posv;
+    return print_list(yo->out, *ctx, yo->data_out_format);
 }
diff --git a/tools/lint/cmd_load.c b/tools/lint/cmd_load.c
index 797167c..bcd23d4 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,6 +27,7 @@
 #include "libyang.h"
 
 #include "common.h"
+#include "yl_opt.h"
 
 void
 cmd_load_help(void)
@@ -44,11 +47,10 @@
             "                  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'},
@@ -57,90 +59,98 @@
         {"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, commands[CMD_LOAD].optstring, 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;
+            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 'X': /* --extended-leafref */
-            options_ctx |= LY_CTX_LEAFREF_EXTENDED;
+            yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
             break;
 
         default:
             YLMSG_E("Unknown option.\n");
-            goto cleanup;
+            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;
+}
+
+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;
+
+    assert(posv);
+
+    if (yo->ctx_options) {
+        ly_ctx_set_options(*ctx, yo->ctx_options);
+        yo->ctx_options = 0;
     }
 
-    for (int i = 0; i < argc - optind; i++) {
-        /* process the schema module files */
-        char *revision;
-        const char **features = NULL;
-
-        /* get revision */
-        revision = strchr(argv[optind + i], '@');
-        if (revision) {
-            revision[0] = '\0';
-            ++revision;
-        }
-
-        /* get features list for this module */
-        if (!fset.count) {
-            features = all_features;
-        } else {
-            get_features(&fset, argv[optind + i], &features);
-        }
-
-        /* load the module */
-        if (!ly_ctx_load_module(*ctx, argv[optind + i], revision, features)) {
-            /* libyang printed the error messages */
-            goto cleanup;
-        }
+    /* 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 b187a71..37fe3f0 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)
@@ -54,8 +56,112 @@
             "                  Write the output to OUTFILE instead of stdout.\n");
 }
 
+int
+cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+    int rc = 0, argc = 0;
+    int opt, opt_index;
+    struct option options[] = {
+        {"format", required_argument, NULL, 'f'},
+        {"help", no_argument, NULL, 'h'},
+        {"tree-line-length", required_argument, NULL, 'L'},
+        {"output", required_argument, NULL, 'o'},
+        {"schema-node", required_argument, NULL, 'P'},
+        {"single-node", no_argument, NULL, 'q'},
+        {NULL, 0, NULL, 0}
+    };
+
+    yo->schema_out_format = LYS_OUT_TREE;
+
+    if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+        return rc;
+    }
+
+    while ((opt = getopt_long(argc, yo->argv, commands[CMD_PRINT].optstring, options, &opt_index)) != -1) {
+        switch (opt) {
+        case 'o': /* --output */
+            if (yo->out) {
+                if (ly_out_filepath(yo->out, optarg) != NULL) {
+                    YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
+                    return 1;
+                }
+            } else {
+                if (ly_out_new_filepath(optarg, &yo->out)) {
+                    YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
+                    return 1;
+                }
+            }
+            break;
+
+        case 'f': /* --format */
+            if (!strcasecmp(optarg, "yang")) {
+                yo->schema_out_format = LYS_OUT_YANG;
+            } else if (!strcasecmp(optarg, "yin")) {
+                yo->schema_out_format = LYS_OUT_YIN;
+            } else if (!strcasecmp(optarg, "info")) {
+                yo->schema_out_format = LYS_OUT_YANG_COMPILED;
+            } else if (!strcasecmp(optarg, "tree")) {
+                yo->schema_out_format = LYS_OUT_TREE;
+            } else {
+                YLMSG_E("Unknown output format %s\n", optarg);
+                cmd_print_help();
+                return 1;
+            }
+            break;
+
+        case 'L': /* --tree-line-length */
+            yo->line_length = atoi(optarg);
+            break;
+
+        case 'P': /* --schema-node */
+            yo->schema_node_path = optarg;
+            break;
+
+        case 'q': /* --single-node */
+            yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
+            break;
+
+        case 'h':
+            cmd_print_help();
+            return 1;
+        default:
+            YLMSG_E("Unknown option.\n");
+            return 1;
+        }
+    }
+
+    *posv = &yo->argv[optind];
+    *posc = argc - optind;
+
+    return 0;
+}
+
+int
+cmd_print_dep(struct yl_opt *yo, int posc)
+{
+    /* file name */
+    if (!posc && !yo->schema_node_path) {
+        YLMSG_E("Missing the name of the module to print.\n");
+        return 1;
+    }
+
+    if ((yo->schema_out_format != LYS_OUT_TREE) && yo->line_length) {
+        YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
+        return 1;
+    }
+
+    if (!yo->out) {
+        if (ly_out_new_file(stdout, &yo->out)) {
+            YLMSG_E("Could not use stdout to print output.\n");
+        }
+        yo->out_stdout = 1;
+    }
+
+    return 0;
+}
+
 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)
+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;
@@ -72,7 +178,7 @@
 }
 
 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)
+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;
@@ -88,178 +194,86 @@
     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)
+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)
 {
+    int rc = 0;
     LY_ERR erc;
-    char *name, *revision;
+    char *name = NULL, *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;
-        }
+    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) {
+        search_submodul = 1;
+        erc = print_submodule(out, ctx, name, revision, format, line_length, options);
+    } else {
+        search_submodul = 0;
+    }
+
+    if (erc == LY_SUCCESS) {
+        rc = 0;
+    } 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);
+        }
+        rc = 1;
+    } else {
+        if (search_submodul) {
+            YLMSG_E("Unable to print submodule %s.\n", name);
+        } else {
+            YLMSG_E("Unable to print module %s.\n", name);
+        }
+        rc = 1;
+    }
+
+    free(name);
+    return rc;
 }
 
-void
-cmd_print(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
 {
-    int argc = 0;
-    char **argv = NULL;
-    int opt, opt_index;
-    struct option options[] = {
-        {"format", required_argument, NULL, 'f'},
-        {"help", no_argument, NULL, 'h'},
-        {"tree-line-length", required_argument, NULL, 'L'},
-        {"output", required_argument, NULL, 'o'},
-        {"schema-node", required_argument, NULL, 'P'},
-        {"single-node", no_argument, NULL, 'q'},
-        {NULL, 0, NULL, 0}
-    };
-    uint16_t options_print = 0;
-    const char *node_path = NULL;
-    LYS_OUTFORMAT format = LYS_OUT_TREE;
-    struct ly_out *out = NULL;
-    ly_bool out_stdout = 0;
-    size_t line_length = 0;
+    int rc = 0;
 
-    if (parse_cmdline(cmdline, &argc, &argv)) {
-        goto cleanup;
-    }
-
-    while ((opt = getopt_long(argc, 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;
-                }
-            } else {
-                if (ly_out_new_filepath(optarg, &out)) {
-                    YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
-                    goto cleanup;
-                }
-            }
-            break;
-
-        case 'f': /* --format */
-            if (!strcasecmp(optarg, "yang")) {
-                format = LYS_OUT_YANG;
-            } else if (!strcasecmp(optarg, "yin")) {
-                format = LYS_OUT_YIN;
-            } else if (!strcasecmp(optarg, "info")) {
-                format = LYS_OUT_YANG_COMPILED;
-            } else if (!strcasecmp(optarg, "tree")) {
-                format = LYS_OUT_TREE;
-            } else {
-                YLMSG_E("Unknown output format %s\n", optarg);
-                cmd_print_help();
-                goto cleanup;
-            }
-            break;
-
-        case 'L': /* --tree-line-length */
-            line_length = atoi(optarg);
-            break;
-
-        case 'P': /* --schema-node */
-            node_path = optarg;
-            break;
-
-        case 'q': /* --single-node */
-            options_print |= LYS_PRINT_NO_SUBSTMT;
-            break;
-
-        case 'h':
-            cmd_print_help();
-            goto cleanup;
-        default:
-            YLMSG_E("Unknown option.\n");
-            goto cleanup;
-        }
-    }
-
-    /* file name */
-    if ((argc == optind) && !node_path) {
-        YLMSG_E("Missing the name of the module to print.\n");
-        goto cleanup;
-    }
-
-    if ((format != LYS_OUT_TREE) && line_length) {
-        YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
-        goto cleanup;
-    }
-
-    if (!out) {
-        if (ly_out_new_file(stdout, &out)) {
-            YLMSG_E("Could not use stdout to print output.\n");
-            goto cleanup;
-        }
-        out_stdout = 1;
-    }
-
-    if (format == LYS_OUT_TREE) {
+    if (yo->schema_out_format == LYS_OUT_TREE) {
         /* print tree from lysc_nodes */
         ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
     }
 
-    if (node_path) {
+    if (yo->schema_node_path) {
         const struct lysc_node *node;
 
-        node = find_schema_path(*ctx, node_path);
+        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.\n", yo->schema_node_path);
+            return 1;
         }
 
-        if (lys_print_node(out, node, format, line_length, options_print)) {
-            YLMSG_E("Unable to print schema node %s.\n", node_path);
-            goto cleanup;
+        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.\n", yo->schema_node_path);
+            return 1;
         }
     } 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) {
+            /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
+            if (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 b3a83ad..15c98a1 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,6 +25,7 @@
 #include "libyang.h"
 
 #include "common.h"
+#include "yl_opt.h"
 
 void
 cmd_searchpath_help(void)
@@ -36,39 +38,49 @@
             "                  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, commands[CMD_SEARCHPATH].optstring, 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;
+            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 @@
         for (uint32_t i = 0; dirs[i]; ++i) {
             printf("    %s\n", dirs[i]);
         }
-        goto cleanup;
+    } else {
+        rc = ly_ctx_set_searchdir(*ctx, posv);
     }
 
-    for (int i = 0; i < argc - optind; i++) {
-        if (ly_ctx_set_searchdir(*ctx, argv[optind + i])) {
-            goto cleanup;
-        }
-    }
-
-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..6d31c33
--- /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.\n");
+            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.\n");
+        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\"\n", posv);
+            return 1;
+        }
+    }
+
+    return 0;
+}
diff --git a/tools/lint/common.c b/tools/lint/common.c
index 96fdb76..205c32a 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.
@@ -28,6 +29,7 @@
 #include "compat.h"
 #include "libyang.h"
 #include "plugins_exts.h"
+#include "yl_opt.h"
 
 int
 parse_schema_path(const char *path, char **dir, char **module)
@@ -104,7 +106,7 @@
 }
 
 void
-get_features(struct ly_set *fset, const char *module, const char ***features)
+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) {
@@ -185,29 +187,6 @@
     return 0;
 }
 
-struct cmdline_file *
-fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
-{
-    struct cmdline_file *rec;
-
-    rec = malloc(sizeof *rec);
-    if (!rec) {
-        YLMSG_E("Allocating memory for data file information failed.\n");
-        return NULL;
-    }
-    rec->in = in;
-    rec->path = path;
-    rec->format = format;
-
-    if (set && ly_set_add(set, rec, 1, NULL)) {
-        free(rec);
-        YLMSG_E("Storing data file information failed.\n");
-        return NULL;
-    }
-
-    return rec;
-}
-
 int
 collect_features(const struct lys_module *mod, struct ly_set *set)
 {
@@ -348,104 +327,6 @@
     return ret;
 }
 
-void
-free_cmdline_file(void *cmdline_file)
-{
-    struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
-
-    if (rec) {
-        ly_in_free(rec->in, 1);
-        free(rec);
-    }
-}
-
-void
-free_cmdline(char *argv[])
-{
-    if (argv) {
-        free(argv[0]);
-        free(argv);
-    }
-}
-
-int
-parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
-{
-    int count;
-    char **vector;
-    char *ptr;
-    char qmark = 0;
-
-    assert(cmdline);
-    assert(argc_p);
-    assert(argv_p);
-
-    /* init */
-    optind = 0; /* reinitialize getopt() */
-    count = 1;
-    vector = malloc((count + 1) * sizeof *vector);
-    vector[0] = strdup(cmdline);
-
-    /* command name */
-    strtok(vector[0], " ");
-
-    /* arguments */
-    while ((ptr = strtok(NULL, " "))) {
-        size_t len;
-        void *r;
-
-        len = strlen(ptr);
-
-        if (qmark) {
-            /* still in quotated text */
-            /* remove NULL termination of the previous token since it is not a token,
-             * but a part of the quotation string */
-            ptr[-1] = ' ';
-
-            if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
-                /* end of quotation */
-                qmark = 0;
-                /* shorten the argument by the terminating quotation mark */
-                ptr[len - 1] = '\0';
-            }
-            continue;
-        }
-
-        /* another token in cmdline */
-        ++count;
-        r = realloc(vector, (count + 1) * sizeof *vector);
-        if (!r) {
-            YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
-            free(vector);
-            return -1;
-        }
-        vector = r;
-        vector[count - 1] = ptr;
-
-        if ((ptr[0] == '"') || (ptr[0] == '\'')) {
-            /* remember the quotation mark to identify end of quotation */
-            qmark = ptr[0];
-
-            /* move the remembered argument after the quotation mark */
-            ++vector[count - 1];
-
-            /* check if the quotation is terminated within this token */
-            if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
-                /* end of quotation */
-                qmark = 0;
-                /* shorten the argument by the terminating quotation mark */
-                ptr[len - 1] = '\0';
-            }
-        }
-    }
-    vector[count] = NULL;
-
-    *argc_p = count;
-    *argv_p = vector;
-
-    return 0;
-}
-
 LYS_INFORMAT
 get_schema_format(const char *filename)
 {
diff --git a/tools/lint/common.h b/tools/lint/common.h
index 0c7206d..ed9f9eb 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.
@@ -55,6 +56,8 @@
 # define PATH_SEPARATOR ";"
 #endif
 
+struct cmdline_file;
+
 /**
  * @brief Storage for the list of the features (their names) in a specific YANG module.
  */
@@ -65,15 +68,6 @@
 };
 
 /**
- * @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.
  */
@@ -86,7 +80,7 @@
  * @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);
+void get_features(const struct ly_set *fset, const char *module, const char ***features);
 
 /**
  * @brief Parse features being specified for the specific YANG module.
@@ -170,40 +164,6 @@
 int get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in);
 
 /**
- * @brief Free the command line file data (struct cmdline_file *)
- * @param[in,out] cmdline_file The (struct cmdline_file *) to free.
- */
-void free_cmdline_file(void *cmdline_file);
-
-/**
- * @brief Create and fill the command line file data (struct cmdline_file *).
- * @param[in] set Optional parameter in case the record is supposed to be added into a set.
- * @param[in] in Input file handler.
- * @param[in] path Filepath of the file.
- * @param[in] format Format of the data file.
- * @return The created command line file structure.
- * @return NULL on failure
- */
-struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
-
-/**
- * @brief Helper function to prepare argc, argv pair from a command line string.
- *
- * @param[in] cmdline Complete command line string.
- * @param[out] argc_p Pointer to store argc value.
- * @param[out] argv_p Pointer to store argv vector.
- * @return 0 on success, non-zero on failure.
- */
-int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
-
-/**
- * @brief Destructor for the argument vector prepared by ::parse_cmdline().
- *
- * @param[in,out] argv Argument vector to destroy.
- */
-void free_cmdline(char *argv[]);
-
-/**
  * @brief Get schema format of the @p filename's content according to the @p filename's suffix.
  *
  * @param[in] filename Name of the file to examine.
diff --git a/tools/lint/main.c b/tools/lint/main.c
index b3d985a..987ea10 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,13 +39,17 @@
 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);
@@ -55,7 +61,10 @@
     }
 
     while (!done) {
-        uint8_t executed = 0;
+        cmd_found = 0;
+
+        posv = &empty;
+        posc = 0;
 
         /* get the command from user */
         cmdline = linenoise(PROMPT);
@@ -76,23 +85,43 @@
         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);
         }
 
         linenoiseHistoryAdd(cmdline);
         free(cmdline);
+        yl_opt_erase(&yo);
     }
 
     /* Global variables in commands are freed. */
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index b836ff1..57a5eb5 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.
@@ -29,112 +30,7 @@
 #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;
-
-    /* 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;
-};
-
-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);
-    ly_set_erase(&c->data_xpath, NULL);
-
-    /* schema */
-    ly_set_erase(&c->schema_features, free_features);
-    ly_set_erase(&c->schema_modules, NULL);
-
-    /* context */
-    free(c->searchpaths);
-    c->searchpaths = NULL;
-
-    /* --reply-rpc */
-    ly_in_free(c->reply_rpc.in, 1);
-
-    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"
 
 static void
 version(void)
@@ -362,55 +258,60 @@
 }
 
 static int
-fill_context_inputs(int argc, char *argv[], struct context *c)
+fill_context_inputs(int argc, char *argv[], struct yl_opt *yo, 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;
+    char *dir = NULL, *module = NULL, *filepath = NULL;
 
     /* Create libyang context. */
-    if (c->yang_lib_file) {
+    if (yo->yang_lib_file) {
         /* ignore features */
-        ly_set_erase(&c->schema_features, free_features);
+        ly_set_erase(&yo->schema_features, free_features);
 
-        if (ly_ctx_new_ylpath(c->searchpaths, c->yang_lib_file, LYD_UNKNOWN, c->ctx_options, &c->ctx)) {
+        if (ly_ctx_new_ylpath(yo->searchpaths, yo->yang_lib_file, LYD_UNKNOWN, yo->ctx_options, ctx)) {
             YLMSG_E("Unable to modify libyang context with yang-library data.\n");
             return -1;
         }
     } else {
         /* set imp feature flag if all should be enabled */
-        c->ctx_options |= !c->schema_features.count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
+        yo->ctx_options |= !yo->schema_features.count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
 
-        if (ly_ctx_new(c->searchpaths, c->ctx_options, &c->ctx)) {
+        if (ly_ctx_new(yo->searchpaths, yo->ctx_options, ctx)) {
             YLMSG_E("Unable to create libyang context\n");
             return -1;
         }
     }
 
     /* set callback providing run-time extension instance data */
-    if (c->schema_context_filename) {
-        ly_ctx_set_ext_data_clb(c->ctx, ext_data_clb, c->schema_context_filename);
+    if (yo->schema_context_filename) {
+        ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, yo->schema_context_filename);
     }
 
     /* process the operational and/or reply RPC content if any */
-    if (c->data_operational.path) {
-        if (get_input(c->data_operational.path, NULL, &c->data_operational.format, &c->data_operational.in)) {
+    if (yo->data_operational.path) {
+        if (get_input(yo->data_operational.path, NULL, &yo->data_operational.format, &yo->data_operational.in)) {
             return -1;
         }
     }
-    if (c->reply_rpc.path) {
-        if (get_input(c->reply_rpc.path, NULL, &c->reply_rpc.format, &c->reply_rpc.in)) {
+    if (yo->reply_rpc.path) {
+        if (get_input(yo->reply_rpc.path, NULL, &yo->reply_rpc.format, &yo->reply_rpc.in)) {
             return -1;
         }
     }
 
     for (int i = 0; i < argc - optind; i++) {
         LYS_INFORMAT format_schema = LYS_IN_UNKNOWN;
-        LYD_FORMAT format_data = c->data_in_format;
+        LYD_FORMAT format_data = yo->data_in_format;
 
-        if (get_input(argv[optind + i], &format_schema, &format_data, &in)) {
+        filepath = argv[optind + i];
+
+        if (!filepath) {
+            goto error;
+        }
+        if (get_input(filepath, &format_schema, &format_data, &in)) {
             goto error;
         }
 
@@ -420,33 +321,33 @@
             const char **features;
 
             /* parse the input */
-            if (parse_schema_path(argv[optind + i], &dir, &module)) {
+            if (parse_schema_path(filepath, &dir, &module)) {
                 goto error;
             }
 
             mod = NULL;
-            if (c->yang_lib_file) {
+            if (yo->yang_lib_file) {
                 /* just get the module, it should already be parsed */
-                mod = ly_ctx_get_module_implemented(c->ctx, module);
+                mod = ly_ctx_get_module_implemented(*ctx, module);
             }
             if (!mod) {
                 /* add temporarily also the path of the module itself */
-                if (ly_ctx_set_searchdir(c->ctx, dir) == LY_EEXIST) {
+                if (ly_ctx_set_searchdir(*ctx, dir) == LY_EEXIST) {
                     path_unset = 0;
                 }
 
                 /* get features list for this module */
-                if (!c->schema_features.count) {
+                if (!yo->schema_features.count) {
                     features = all_features;
                 } else {
-                    get_features(&c->schema_features, module, &features);
+                    get_features(&yo->schema_features, module, &features);
                 }
 
                 /* parse module */
-                ret = lys_parse(c->ctx, in, format_schema, features, &mod);
-                ly_ctx_unset_searchdir_last(c->ctx, path_unset);
+                ret = lys_parse(*ctx, in, format_schema, features, &mod);
+                ly_ctx_unset_searchdir_last(*ctx, path_unset);
                 if (ret) {
-                    YLMSG_E("Parsing schema module \"%s\" failed.\n", argv[optind + i]);
+                    YLMSG_E("Parsing schema module \"%s\" failed.\n", filepath);
                     goto error;
                 }
             }
@@ -457,15 +358,15 @@
             free(module);
             module = NULL;
 
-            if (c->schema_out_format || c->feature_param_format) {
+            if (yo->schema_out_format || yo->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]);
+                if (ly_set_add(&yo->schema_modules, (void *)mod, 1, NULL)) {
+                    YLMSG_E("Storing parsed schema module (%s) for print failed.\n", filepath);
                     goto error;
                 }
             }
         } else if (format_data) {
-            if (!fill_cmdline_file(&c->data_inputs, in, argv[optind + i], format_data)) {
+            if (!fill_cmdline_file(&yo->data_inputs, in, filepath, format_data)) {
                 goto error;
             }
             in = NULL;
@@ -476,11 +377,11 @@
     }
 
     /* check that all specified features were applied, apply now if possible */
-    while ((sf = get_features_not_applied(&c->schema_features))) {
+    while ((sf = get_features_not_applied(&yo->schema_features))) {
         /* try to find implemented or the latest revision of this module */
-        mod = ly_ctx_get_module_implemented(c->ctx, sf->mod_name);
+        mod = ly_ctx_get_module_implemented(*ctx, sf->mod_name);
         if (!mod) {
-            mod = ly_ctx_get_module_latest(c->ctx, sf->mod_name);
+            mod = ly_ctx_get_module_latest(*ctx, sf->mod_name);
         }
         if (!mod) {
             YLMSG_E("Specified features not applied, module \"%s\" not loaded.\n", sf->mod_name);
@@ -512,7 +413,7 @@
  * 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;
 
@@ -554,10 +455,10 @@
     uint8_t data_type_set = 0;
     uint32_t temp_lo = 0;
 
-    c->ctx_options = YL_DEFAULT_CTX_OPTIONS;
-    c->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
-    c->data_validate_options = YL_DEFAULT_DATA_VALIDATE_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
@@ -600,28 +501,28 @@
 
         case 'f': /* --format */
             if (!strcasecmp(optarg, "yang")) {
-                c->schema_out_format = LYS_OUT_YANG;
-                c->data_out_format = 0;
+                yo->schema_out_format = LYS_OUT_YANG;
+                yo->data_out_format = 0;
             } else if (!strcasecmp(optarg, "yin")) {
-                c->schema_out_format = LYS_OUT_YIN;
-                c->data_out_format = 0;
+                yo->schema_out_format = LYS_OUT_YIN;
+                yo->data_out_format = 0;
             } else if (!strcasecmp(optarg, "info")) {
-                c->schema_out_format = LYS_OUT_YANG_COMPILED;
-                c->data_out_format = 0;
+                yo->schema_out_format = LYS_OUT_YANG_COMPILED;
+                yo->data_out_format = 0;
             } else if (!strcasecmp(optarg, "tree")) {
-                c->schema_out_format = LYS_OUT_TREE;
-                c->data_out_format = 0;
+                yo->schema_out_format = LYS_OUT_TREE;
+                yo->data_out_format = 0;
             } else if (!strcasecmp(optarg, "xml")) {
-                c->schema_out_format = 0;
-                c->data_out_format = LYD_XML;
+                yo->schema_out_format = 0;
+                yo->data_out_format = LYD_XML;
             } else if (!strcasecmp(optarg, "json")) {
-                c->schema_out_format = 0;
-                c->data_out_format = LYD_JSON;
+                yo->schema_out_format = 0;
+                yo->data_out_format = LYD_JSON;
             } else if (!strcasecmp(optarg, "lyb")) {
-                c->schema_out_format = 0;
-                c->data_out_format = LYD_LYB;
+                yo->schema_out_format = 0;
+                yo->data_out_format = LYD_LYB;
             } else if (!strcasecmp(optarg, "feature-param")) {
-                c->feature_param_format = 1;
+                yo->feature_param_format = 1;
             } else {
                 YLMSG_E("Unknown output format %s\n", optarg);
                 help(1);
@@ -631,11 +532,11 @@
 
         case 'I': /* --in-format */
             if (!strcasecmp(optarg, "xml")) {
-                c->data_in_format = LYD_XML;
+                yo->data_in_format = LYD_XML;
             } else if (!strcasecmp(optarg, "json")) {
-                c->data_in_format = LYD_JSON;
+                yo->data_in_format = LYD_JSON;
             } else if (!strcasecmp(optarg, "lyb")) {
-                c->data_in_format = LYD_LYB;
+                yo->data_in_format = LYD_LYB;
             } else {
                 YLMSG_E("Unknown input format %s\n", optarg);
                 help(1);
@@ -655,7 +556,7 @@
                 return -1;
             }
 
-            if (searchpath_strcat(&c->searchpaths, optarg)) {
+            if (searchpath_strcat(&yo->searchpaths, optarg)) {
                 YLMSG_E("Storing searchpath failed.\n");
                 return -1;
             }
@@ -664,54 +565,54 @@
         } /* case 'p' */
 
         case 'D': /* --disable-search */
-            if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
+            if (yo->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;
+            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 {
-                c->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+                yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
             }
             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;
+            if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
+                yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
+                yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
             } else {
-                c->ctx_options |= LY_CTX_REF_IMPLEMENTED;
+                yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
             }
             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 */
@@ -721,24 +622,24 @@
             }
 
             if (!strcasecmp(optarg, "config")) {
-                c->data_parse_options |= LYD_PARSE_NO_STATE;
-                c->data_validate_options |= LYD_VALIDATE_NO_STATE;
+                yo->data_parse_options |= LYD_PARSE_NO_STATE;
+                yo->data_validate_options |= LYD_VALIDATE_NO_STATE;
             } else if (!strcasecmp(optarg, "get")) {
-                c->data_parse_options |= LYD_PARSE_ONLY;
+                yo->data_parse_options |= LYD_PARSE_ONLY;
             } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config") || !strcasecmp(optarg, "edit")) {
-                c->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+                yo->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
             } else if (!strcasecmp(optarg, "rpc")) {
-                c->data_type = LYD_TYPE_RPC_YANG;
+                yo->data_type = LYD_TYPE_RPC_YANG;
             } else if (!strcasecmp(optarg, "nc-rpc")) {
-                c->data_type = LYD_TYPE_RPC_NETCONF;
+                yo->data_type = LYD_TYPE_RPC_NETCONF;
             } else if (!strcasecmp(optarg, "reply")) {
-                c->data_type = LYD_TYPE_REPLY_YANG;
+                yo->data_type = LYD_TYPE_REPLY_YANG;
             } else if (!strcasecmp(optarg, "nc-reply")) {
-                c->data_type = LYD_TYPE_REPLY_NETCONF;
+                yo->data_type = LYD_TYPE_REPLY_NETCONF;
             } else if (!strcasecmp(optarg, "notif")) {
-                c->data_type = LYD_TYPE_NOTIF_YANG;
+                yo->data_type = LYD_TYPE_NOTIF_YANG;
             } else if (!strcasecmp(optarg, "nc-notif")) {
-                c->data_type = LYD_TYPE_NOTIF_NETCONF;
+                yo->data_type = LYD_TYPE_NOTIF_NETCONF;
             } else if (!strcasecmp(optarg, "data")) {
                 /* default option */
             } else {
@@ -752,13 +653,13 @@
 
         case 'd': /* --default */
             if (!strcasecmp(optarg, "all")) {
-                c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
+                yo->data_print_options = (yo->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;
+                yo->data_print_options = (yo->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;
+                yo->data_print_options = (yo->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;
+                yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
             } else {
                 YLMSG_E("Unknown default mode %s\n", optarg);
                 help(1);
@@ -767,26 +668,26 @@
             break;
 
         case 'E': /* --data-xpath */
-            if (ly_set_add(&c->data_xpath, optarg, 0, NULL)) {
+            if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
                 YLMSG_E("Storing XPath \"%s\" failed.\n", 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) {
+            if (yo->out) {
                 YLMSG_E("Only a single output can be specified.\n");
                 return -1;
             } else {
-                if (ly_out_new_filepath(optarg, &c->out)) {
+                if (ly_out_new_filepath(optarg, &yo->out)) {
                     YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
                     return -1;
                 }
@@ -794,36 +695,36 @@
             break;
 
         case 'O': /* --operational */
-            if (c->data_operational.path) {
+            if (yo->data_operational.path) {
                 YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
                 return -1;
             }
-            c->data_operational.path = optarg;
+            yo->data_operational.path = optarg;
             break;
 
         case 'R': /* --reply-rpc */
-            if (c->reply_rpc.path) {
+            if (yo->reply_rpc.path) {
                 YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.\n");
                 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;
 
         case 'X': /* --extended-leafref */
-            c->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
+            yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
             break;
 
 #ifndef NDEBUG
@@ -862,102 +763,102 @@
     }
 
     /* 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");
         return 1;
     }
 
-    if (c->data_xpath.count) {
-        c->data_merge = 1;
+    if (yo->data_xpath.count) {
+        yo->data_merge = 1;
     }
-    if (c->data_xpath.count && (c->schema_out_format || c->data_out_format)) {
+    if (yo->data_xpath.count && (yo->schema_out_format || yo->data_out_format)) {
         YLMSG_E("The --format option cannot be combined with --data-xpath option.\n");
         return -1;
     }
-    if (c->data_xpath.count && (c->data_print_options & LYD_PRINT_WD_MASK)) {
+    if (yo->data_xpath.count && (yo->data_print_options & LYD_PRINT_WD_MASK)) {
         YLMSG_E("The --default option cannot be combined with --data-xpath option.\n");
         return -1;
     }
 
-    if (c->data_merge) {
-        if (c->data_type || (c->data_parse_options & 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 */
             YLMSG_W("The --merge option has effect only for 'data' and 'config' TYPEs\n");
-            c->data_merge = 0;
+            yo->data_merge = 0;
         } else {
             /* postpone validation after the merge of all the input data */
-            c->data_parse_options |= LYD_PARSE_ONLY;
+            yo->data_parse_options |= LYD_PARSE_ONLY;
         }
     }
 
-    if (c->data_operational.path && !c->data_type) {
+    if (yo->data_operational.path && !yo->data_type) {
         YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.\n");
-        c->data_operational.path = NULL;
+        yo->data_operational.path = NULL;
     }
 
-    if (c->reply_rpc.path && (c->data_type != LYD_TYPE_REPLY_NETCONF)) {
+    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.\n");
-        c->data_operational.path = NULL;
-    } else if (!c->reply_rpc.path && (c->data_type == LYD_TYPE_REPLY_NETCONF)) {
+        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.\n");
         return -1;
     }
 
-    if ((c->schema_out_format != LYS_OUT_TREE) && c->line_length) {
+    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.\n");
     }
 
-    if (!c->out && (c->data_out_format == LYD_LYB)) {
+    if (!yo->out && (yo->data_out_format == LYD_LYB)) {
         YLMSG_E("The LYB format requires the -o option specified.\n");
         return -1;
     }
     /* default output stream */
-    if (!c->out) {
-        if (ly_out_new_file(stdout, &c->out)) {
+    if (!yo->out) {
+        if (ly_out_new_file(stdout, &yo->out)) {
             YLMSG_E("Unable to set stdout as output.\n");
             return -1;
         }
     }
 
-    if (c->schema_out_format == LYS_OUT_TREE) {
+    if (yo->schema_out_format == LYS_OUT_TREE) {
         /* print tree from lysc_nodes */
-        c->ctx_options |= LY_CTX_SET_PRIV_PARSED;
+        yo->ctx_options |= LY_CTX_SET_PRIV_PARSED;
     }
 
     /* process input files provided as standalone command line arguments,
      * schema modules are parsed and inserted into the context,
      * data files are just checked and prepared into internal structures for further processing */
-    ret = fill_context_inputs(argc, argv, c);
+    ret = fill_context_inputs(argc, argv, yo, ctx);
     if (ret) {
         return ret;
     }
 
     /* the second batch of checks */
-    if (c->schema_print_options && !c->schema_out_format) {
+    if (yo->schema_print_options && !yo->schema_out_format) {
         YLMSG_W("Schema printer options specified, but the schema output format is missing.\n");
     }
-    if (c->schema_parse_options && !c->schema_modules.count) {
+    if (yo->schema_parse_options && !yo->schema_modules.count) {
         YLMSG_W("Schema parser options specified, but no schema input file provided.\n");
     }
-    if (c->data_print_options && !c->data_out_format) {
+    if (yo->data_print_options && !yo->data_out_format) {
         YLMSG_W("data printer options specified, but the data output format is missing.\n");
     }
-    if (((c->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || c->data_type) && !c->data_inputs.count) {
+    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.\n");
     }
 
-    if (c->schema_node_path) {
+    if (yo->schema_node_path) {
         /* turn off logging so that the message is not repeated */
         ly_temp_log_options(&temp_lo);
         /* search operation input */
-        c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 0);
-        if (!c->schema_node) {
+        yo->schema_node = lys_find_path(*ctx, NULL, yo->schema_node_path, 0);
+        if (!yo->schema_node) {
             /* restore logging so an error may be displayed */
             ly_temp_log_options(NULL);
             /* search operation output */
-            c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 1);
-            if (!c->schema_node) {
+            yo->schema_node = lys_find_path(*ctx, NULL, yo->schema_node_path, 1);
+            if (!yo->schema_node) {
                 YLMSG_E("Invalid schema path.\n");
                 return -1;
             }
@@ -971,7 +872,8 @@
 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;
@@ -979,7 +881,7 @@
     /* 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;
     }
@@ -989,54 +891,54 @@
 
     /* do the required job - parse, validate, print */
 
-    if (c.list) {
+    if (yo.list) {
         /* print the list of schemas */
-        ret = print_list(c.out, c.ctx, c.data_out_format);
+        ret = print_list(yo.out, ctx, yo.data_out_format);
         goto cleanup;
     } else {
-        if (c.feature_param_format) {
-            for (u = 0; u < c.schema_modules.count; u++) {
-                if (collect_features(c.schema_modules.objs[u], &set)) {
+        if (yo.feature_param_format) {
+            for (u = 0; u < yo.schema_modules.count; u++) {
+                if (collect_features(yo.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)) {
+                if (generate_features_output(yo.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, c.line_length, c.schema_print_options);
+            ly_print(yo.out, "%s\n", features_output);
+        } else if (yo.schema_out_format) {
+            if (yo.schema_node) {
+                ret = lys_print_node(yo.out, yo.schema_node, yo.schema_out_format, yo.line_length, yo.schema_print_options);
                 if (ret) {
-                    YLMSG_E("Unable to print schema node %s.\n", c.schema_node_path);
+                    YLMSG_E("Unable to print schema node %s.\n", yo.schema_node_path);
                     goto cleanup;
                 }
-            } else if (c.submodule) {
-                const struct lysp_submodule *submod = ly_ctx_get_submodule_latest(c.ctx, c.submodule);
+            } else if (yo.submodule) {
+                const struct lysp_submodule *submod = ly_ctx_get_submodule_latest(ctx, yo.submodule);
 
                 if (!submod) {
-                    YLMSG_E("Unable to find submodule %s.\n", c.submodule);
+                    YLMSG_E("Unable to find submodule %s.\n", yo.submodule);
                     goto cleanup;
                 }
 
-                ret = lys_print_submodule(c.out, submod, c.schema_out_format, c.line_length, c.schema_print_options);
+                ret = lys_print_submodule(yo.out, submod, yo.schema_out_format, yo.line_length, yo.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 (u = 0; u < yo.schema_modules.count; ++u) {
+                    ret = lys_print_module(yo.out, (struct lys_module *)yo.schema_modules.objs[u], yo.schema_out_format,
+                            yo.line_length, yo.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 ((yo.schema_out_format == LYS_OUT_TREE) && (u + 1 < yo.schema_modules.count)) {
+                        ly_print(yo.out, "\n");
                     }
                     if (ret) {
-                        YLMSG_E("Unable to print module %s.\n", ((struct lys_module *)c.schema_modules.objs[u])->name);
+                        YLMSG_E("Unable to print module %s.\n", ((struct lys_module *)yo.schema_modules.objs[u])->name);
                         goto cleanup;
                     }
                 }
@@ -1044,10 +946,10 @@
         }
 
         /* 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,
-                    &c.data_xpath);
+        if (yo.data_inputs.size) {
+            ret = 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);
             if (ret) {
                 goto cleanup;
             }
@@ -1056,7 +958,8 @@
 
 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/yl_opt.c b/tools/lint/yl_opt.c
new file mode 100644
index 0000000..1e5eb39
--- /dev/null
+++ b/tools/lint/yl_opt.c
@@ -0,0 +1,187 @@
+/**
+ * @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"
+
+struct cmdline_file *
+fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
+{
+    struct cmdline_file *rec;
+
+    rec = malloc(sizeof *rec);
+    if (!rec) {
+        YLMSG_E("Allocating memory for data file information failed.\n");
+        return NULL;
+    }
+    rec->in = in;
+    rec->path = path;
+    rec->format = format;
+
+    if (set && ly_set_add(set, rec, 1, NULL)) {
+        free(rec);
+        YLMSG_E("Storing data file information failed.\n");
+        return NULL;
+    }
+
+    return rec;
+}
+
+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, free_features);
+    ly_set_erase(&yo->schema_modules, NULL);
+    free(yo->features_output);
+
+    /* 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;
+}
+
+void
+free_cmdline(char *argv[])
+{
+    if (argv) {
+        free(argv[0]);
+        free(argv);
+    }
+}
+
+int
+parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
+{
+    int count;
+    char **vector;
+    char *ptr;
+    char qmark = 0;
+
+    assert(cmdline);
+    assert(argc_p);
+    assert(argv_p);
+
+    /* init */
+    optind = 0; /* reinitialize getopt() */
+    count = 1;
+    vector = malloc((count + 1) * sizeof *vector);
+    vector[0] = strdup(cmdline);
+
+    /* command name */
+    strtok(vector[0], " ");
+
+    /* arguments */
+    while ((ptr = strtok(NULL, " "))) {
+        size_t len;
+        void *r;
+
+        len = strlen(ptr);
+
+        if (qmark) {
+            /* still in quotated text */
+            /* remove NULL termination of the previous token since it is not a token,
+             * but a part of the quotation string */
+            ptr[-1] = ' ';
+
+            if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+                /* end of quotation */
+                qmark = 0;
+                /* shorten the argument by the terminating quotation mark */
+                ptr[len - 1] = '\0';
+            }
+            continue;
+        }
+
+        /* another token in cmdline */
+        ++count;
+        r = realloc(vector, (count + 1) * sizeof *vector);
+        if (!r) {
+            YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
+            free(vector);
+            return -1;
+        }
+        vector = r;
+        vector[count - 1] = ptr;
+
+        if ((ptr[0] == '"') || (ptr[0] == '\'')) {
+            /* remember the quotation mark to identify end of quotation */
+            qmark = ptr[0];
+
+            /* move the remembered argument after the quotation mark */
+            ++vector[count - 1];
+
+            /* check if the quotation is terminated within this token */
+            if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+                /* end of quotation */
+                qmark = 0;
+                /* shorten the argument by the terminating quotation mark */
+                ptr[len - 1] = '\0';
+            }
+        }
+    }
+    vector[count] = NULL;
+
+    *argc_p = count;
+    *argv_p = vector;
+
+    return 0;
+}
diff --git a/tools/lint/yl_opt.h b/tools/lint/yl_opt.h
new file mode 100644
index 0000000..ad185a9
--- /dev/null
+++ b/tools/lint/yl_opt.h
@@ -0,0 +1,171 @@
+/**
+ * @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;
+    const struct lysc_node *schema_node;
+    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;
+    char *features_output;
+
+    /*
+     * 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 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_ */