yanglint REFACTOR reflect changes in libyang 2.0 in yanglint
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 00abfa3..94011b4 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -3,7 +3,7 @@
  * @author Radek Krejci <rkrejci@cesnet.cz>
  * @brief libyang's yanglint tool - noninteractive code
  *
- * Copyright (c) 2015-2018 CESNET, z.s.p.o.
+ * Copyright (c) 2020 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -14,1106 +14,782 @@
 
 #define _GNU_SOURCE
 
+#include <errno.h>
+#include <getopt.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <getopt.h>
-#include <errno.h>
-#include <libgen.h>
-#include <sys/stat.h>
 #include <string.h>
 #include <strings.h>
+#include <sys/stat.h>
 
 #include "libyang.h"
 
+#include "common.h"
 #include "tools/config.h"
 
-#include "commands.h"
+/**
+ * @brief Context structure to hold and pass variables in a structured form.
+ */
+struct context {
+    /* libyang context for the run */
+    struct ly_ctx *ctx;
 
+    /* prepared output (--output option or stdout by default) */
+    struct ly_out *out;
 
-volatile uint8_t verbose = 0;
+    struct ly_set searchpaths;
 
-#if 0
-/* from commands.c */
-int print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
-#endif
+    /* options flags */
+    uint8_t list;        /* -l option to print list of schemas */
 
-void
+    /*
+     * schema
+     */
+    /* set schema modules' features via --feature 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;
+
+    /* value of --format in case of schema format */
+    LYS_OUTFORMAT schema_out_format;
+
+    /*
+     * data
+     */
+    /* various options based on --type option */
+    uint8_t data_type; /* values taken from LYD_VALIDATE_OP and extended by 0 for standard data tree */
+    uint32_t data_parse_options;
+    uint32_t data_validate_options;
+    uint32_t data_print_options;
+
+    /* flag for --merge option */
+    uint8_t data_merge;
+
+    /* value of --format in case of data format */
+    LYD_FORMAT data_out_format;
+
+    /* input data files (struct cmdline_file *) */
+    struct ly_set data_inputs;
+
+    /* the request files for reply data (struct cmdline_file *)
+     * In case the data_type is PARSE_REPLY, the parsing function requires information about the request for this reply.
+     * One way to provide the request is a data file containing the full RPC/Action request which will be parsed.
+     * Alternatively, it can be set as the Path of the requested RPC/Action and in that case it is stored in
+     * data_request_paths.
+     */
+    struct ly_set data_requests;
+    /* An alternative way of providing requests for parsing data replies, instead of providing full
+     * request in a data file, only the Path of the requested RPC/Action is provided and stored as
+     * const char *. Note that the number of items in the set must be 1 (1 applies to all) or equal to
+     * data_inputs_count (1 to 1 mapping).
+     */
+    struct ly_set data_request_paths;
+
+    /* storage for --operational */
+    struct cmdline_file data_operational;
+};
+
+static void
+erase_context(struct context *c)
+{
+    /* data */
+    ly_set_erase(&c->data_inputs, free_cmdline_file);
+    ly_set_erase(&c->data_requests, free_cmdline_file);
+    ly_set_erase(&c->data_request_paths, NULL);
+    ly_in_free(c->data_operational.in, 1);
+
+    /* schema */
+    ly_set_erase(&c->schema_features, free_features);
+    ly_set_erase(&c->schema_modules, NULL);
+
+    /* context */
+    ly_set_erase(&c->searchpaths, NULL);
+
+    ly_out_free(c->out, NULL,  0);
+    ly_ctx_destroy(c->ctx, NULL);
+}
+
+static void
+version(void)
+{
+    printf("yanglint %s\n", PROJECT_VERSION);
+}
+
+static void
 help(int shortout)
 {
-    fprintf(stdout, "Usage:\n");
-    fprintf(stdout, "    yanglint [options] [-f { yang | yin | tree | tree-rfc | info}] <file>...\n");
-    fprintf(stdout, "        Validates the YANG module in <file>, and all its dependencies.\n\n");
-    fprintf(stdout, "    yanglint [options] [-f { xml | json }] <schema>... <file>...\n");
-    fprintf(stdout, "        Validates the YANG modeled data in <file> according to the <schema>.\n\n");
-    fprintf(stdout, "    yanglint\n");
-    fprintf(stdout, "        Starts interactive mode with more features.\n\n");
+
+    printf("Usage:\n"
+            "    yanglint [Options] [-f { yang | yin | info}] <schema>...\n"
+            "        Validates the YANG module in <schema>, and all its dependencies.\n\n"
+            "    yanglint [Options] [-f { xml | json }] <schema>... <file> [<request>]...\n"
+            "        Validates the YANG modeled data in <file> according to the <schema>.\n\n"
+            "    yanglint\n"
+            "        Starts interactive mode with more features.\n\n");
 
     if (shortout) {
         return;
     }
-    fprintf(stdout, "Options:\n"
-        "  -h, --help            Show this help message and exit.\n"
-        "  -v, --version         Show version number and exit.\n"
-        "  -V, --verbose         Show verbose messages, can be used multiple times to\n"
-        "                        increase verbosity.\n"
+    printf("Options:\n"
+            "  -h, --help    Show this help message and exit.\n"
+            "  -v, --version Show version number and exit.\n"
+            "  -V, --verbose Show verbose messages, can be used multiple times to\n"
+            "                increase verbosity.\n"
 #ifndef NDEBUG
-        "  -G GROUPS, --debug=GROUPS\n"
-        "                        Enable printing of specific debugging message group\n"
-        "                        (nothing will be printed unless verbosity is set to debug):\n"
-        "                        <group>[,<group>]* (dict, yang, yin, xpath, diff)\n\n"
+            "  -G GROUPS, --debug=GROUPS\n"
+            "                Enable printing of specific debugging message group\n"
+            "                (nothing will be printed unless verbosity is set to debug):\n"
+            "                <group>[,<group>]* (dict, xpath)\n\n"
 #endif
-        "  -p PATH, --path=PATH  Search path for schema (YANG/YIN) modules. The option can be used multiple times.\n"
-        "                        Current working directory and path of the module being added is used implicitly.\n\n"
-        "  -D, --disable-searchdir\n"
-        "                        Do not implicitly search in CWD for schema modules. If specified a second time,\n"
-        "                        do not even search the module directory (all modules must be explicitly specified).\n\n"
-        "  -s, --strict          Strict data parsing (do not skip unknown data),\n"
-        "                        has no effect for schemas.\n\n"
-        "  -m, --merge           Merge input data files into a single tree and validate at once,\n"
-        "                        has no effect for the auto, rpc, rpcreply and notif TYPEs.\n\n"
-        "  -f FORMAT, --format=FORMAT\n"
-        "                        Convert to FORMAT. Supported formats: \n"
-        "                        yang, yin, tree and info for schemas,\n"
-        "                        xml, json for data.\n"
-        "  -a, --auto            Modify the xml output by adding envelopes for autodetection.\n\n"
-        "  -i, --allimplemented  Make all the imported modules implemented.\n\n"
-        "  -l, --list            Print info about the loaded schemas in ietf-yang-library format,\n"
-        "                        the -f option applies here to specify data encoding.\n"
-        "                        (i - imported module, I - implemented module)\n\n"
-        "  -o OUTFILE, --output=OUTFILE\n"
-        "                        Write the output to OUTFILE instead of stdout.\n\n"
-        "  -F FEATURES, --features=FEATURES\n"
-        "                        Features to support, default all.\n"
-        "                        <modname>:[<feature>,]*\n\n"
-        "  -d MODE, --default=MODE\n"
-        "                        Print data with default values, according to the MODE\n"
-        "                        (to print attributes, ietf-netconf-with-defaults model\n"
-        "                        must be loaded):\n"
-        "        all             - Add missing default nodes.\n"
-        "        all-tagged      - Add missing default nodes and mark all the default\n"
-        "                          nodes with the attribute.\n"
-        "        trim            - Remove all nodes with a default value.\n"
-        "        implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
-        "  -t TYPE, --type=TYPE\n"
-        "                        Specify data tree type in the input data file:\n"
-        "        auto            - Resolve data type (one of the following) automatically\n"
-        "                          (as pyang does) - applicable only on XML input data.\n"
-        "        data            - Complete datastore with status data (default type).\n"
-        "        config          - Configuration datastore (without status data).\n"
-        "        get             - Result of the NETCONF <get> operation.\n"
-        "        getconfig       - Result of the NETCONF <get-config> operation.\n"
-        "        edit            - Content of the NETCONF <edit-config> operation.\n"
-        "        rpc             - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
-        "        rpcreply        - Reply to the RPC. The input data <file>s are expected in pairs - each RPC reply\n"
-        "                          input data <file> must be followed by the origin RPC input data <file> for the reply.\n"
-        "                          The same rule of pairing applies also in case of 'auto' TYPE and input data file\n"
-        "                          containing RPC reply.\n"
-        "        notif           - Notification instance (content of the <notification> element without <eventTime>.\n\n"
-        "  -O FILE, --operational=FILE\n"
-        "                        - Optional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the FILE contains running\n"
-        "                          configuration datastore and state data (operational datastore) referenced from\n"
-        "                          the RPC/Notification. The same data apply to all input data <file>s. Note that\n"
-        "                          the file is validated as 'data' TYPE. Special value '!' can be used as FILE argument\n"
-        "                          to ignore the external references.\n\n"
-        "  -y YANGLIB_PATH       - Path to a yang-library data describing the initial context.\n\n"
-        "Tree output specific options:\n"
-        "  --tree-help           - Print help on tree symbols and exit.\n"
-        "  --tree-print-groupings\n"
-        "                        Print top-level groupings in a separate section.\n"
-        "  --tree-print-uses     - Print uses nodes instead the resolved grouping nodes.\n"
-        "  --tree-no-leafref-target\n"
-        "                        Do not print target nodes of leafrefs.\n"
-        "  --tree-path=SCHEMA_PATH\n"
-        "                        Print only the specified subtree.\n"
-        "  --tree-line-length=LINE_LENGTH\n"
-        "                        Wrap lines if longer than the specified length (it is not a strict limit, longer lines\n"
-        "                        can often appear).\n\n"
-        "Info output specific options:\n"
-        "  -P INFOPATH, --info-path=INFOPATH\n"
-        "                        - Schema path with full module names used as node's prefixes, the path identify the root\n"
-        "                          node of the subtree to print information about.\n"
-        "  --single-node         - Print information about a single node instead of a subtree."
-        "\n");
+
+            "  -d MODE, --default=MODE\n"
+            "                Print data with default values, according to the MODE\n"
+            "                (to print attributes, ietf-netconf-with-defaults model\n"
+            "                must be loaded):\n"
+            "      all             - Add missing default nodes.\n"
+            "      all-tagged      - Add missing default nodes and mark all the default\n"
+            "                        nodes with the attribute.\n"
+            "      trim            - Remove all nodes with a default value.\n"
+            "      implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
+
+            "  -D, --disable-searchdir\n"
+            "                Do not implicitly search in current working directory for\n"
+            "                schema modules. If specified a second time, do not even\n"
+            "                search in the module directory (all modules must be \n"
+            "                explicitly specified).\n\n"
+
+            "  -p PATH, --path=PATH\n"
+            "                Search path for schema (YANG/YIN) modules. The option can be\n"
+            "                used multiple times. The current working directory and the\n"
+            "                path of the module being added is used implicitly.\n\n"
+
+            "  -F FEATURES, --features=FEATURES\n"
+            "                Features to support, default all.\n"
+            "                <modname>:[<feature>,]*\n\n"
+
+            "  -i, --makeimplemented\n"
+            "                Make the imported modules \"referenced\" from any loaded\n"
+            "                module also implemented. If specified a second time, all the\n"
+            "                modules are set implemented.\n\n"
+
+            "  -l, --list    Print info about the loaded schemas.\n"
+            "                (i - imported module, I - implemented module)\n"
+            "                In case the -f option with data encoding is specified,\n"
+            "                the list is printed as ietf-yang-library data.\n\n"
+
+            "  -o OUTFILE, --output=OUTFILE\n"
+            "                Write the output to OUTFILE instead of stdout.\n\n"
+
+            "  -f FORMAT, --format=FORMAT\n"
+            "                Convert input into FORMAT. Supported formats: \n"
+            "                yang, yin, tree and info for schemas,\n"
+            "                xml, json for data.\n\n"
+
+            "  -P PATH, --schema-node=PATH\n"
+            "                Print only the specified subtree of the schema.\n"
+            "                The PATH is the XPath subset mentioned in documentation as\n"
+            "                the Path format. The option can be combined with --single-node\n"
+            "                option to print information only about the specified node.\n"
+            "  -q, --single-node\n"
+            "                Supplement to the --schema-node option to print information\n"
+            "                only about a single node specified as PATH argument.\n\n"
+
+            "  -s, --strict  Strict data parsing (do not skip unknown data), has no effect\n"
+            "                for schemas.\n\n"
+
+            "  -e, --present Validate only with the schema modules whose data actually\n"
+            "                exist in the provided input data files. Takes effect only\n"
+            "                with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
+            "                mandatory nodes from modules which data are not present in the\n"
+            "                provided input data files.\n\n"
+
+            "  -t TYPE, --type=TYPE\n"
+            "                Specify data tree type in the input data file(s):\n"
+            "        data          - Complete datastore with status data (default type).\n"
+            "        config        - Configuration datastore (without status data).\n"
+            "        get           - Result of the NETCONF <get> operation.\n"
+            "        getconfig     - Result of the NETCONF <get-config> operation.\n"
+            "        edit          - Content of the NETCONF <edit-config> operation.\n"
+            "        rpc           - Content of the NETCONF <rpc> message, defined as YANG's\n"
+            "                        RPC/Action input statement.\n"
+            "        reply         - Reply to the RPC/Action. Besides the reply itself,\n"
+            "                        yanglint(1) requires information about the request for\n"
+            "                        the reply. The request (RPC/Action) can be provided as\n"
+            "                        the --request option or as another input data <file>\n"
+            "                        provided right after the reply data <file> and\n"
+            "                        containing complete RPC/Action for the reply.\n"
+            "        notif         - Notification instance (content of the <notification>\n"
+            "                        element without <eventTime>).\n"
+            "  -r PATH, --request=PATH\n"
+            "                The alternative way of providing request information for the\n"
+            "                '--type=reply'. The PATH is the XPath subset described in\n"
+            "                documentation as Path format. It is required to point to the\n"
+            "                RPC or Action in the schema which is supposed to be a request\n"
+            "                for the reply(ies) being parsed from the input data files.\n"
+            "                In case of multiple input data files, the 'request' option can\n"
+            "                be set once for all the replies or multiple times each for the\n"
+            "                respective input data file.\n\n"
+
+            "  -O FILE, --operational=FILE\n"
+            "                Provide optional data to extend validation of the 'rpc',\n"
+            "                'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
+            "                the :running configuration datastore and state data\n"
+            "                (operational datastore) referenced from the RPC/Notification.\n\n"
+
+            "  -m, --merge   Merge input data files into a single tree and validate at\n"
+            "                once.The option has effect only for 'data' and 'config' TYPEs.\n\n"
+
+#if 0
+            "  -y YANGLIB_PATH       - Path to a yang-library data describing the initial context.\n\n"
+            "Tree output specific options:\n"
+            "  --tree-help           - Print help on tree symbols and exit.\n"
+            "  --tree-print-groupings\n"
+            "                        Print top-level groupings in a separate section.\n"
+            "  --tree-print-uses     - Print uses nodes instead the resolved grouping nodes.\n"
+            "  --tree-no-leafref-target\n"
+            "                        Do not print target nodes of leafrefs.\n"
+            "  --tree-path=SCHEMA_PATH\n"
+            "                        Print only the specified subtree.\n"
+            "  --tree-line-length=LINE_LENGTH\n"
+            "                        Wrap lines if longer than the specified length (it is not a strict limit, longer lines\n"
+            "                        can often appear).\n\n"
+#endif
+            "\n");
 }
 
-void
-tree_help(void)
-{
-    fprintf(stdout, "Each node is printed as:\n\n");
-    fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
-                    "  <status> is one of:\n"
-                    "    + for current\n"
-                    "    x for deprecated\n"
-                    "    o for obsolete\n\n"
-                    "  <flags> is one of:\n"
-                    "    rw for configuration data\n"
-                    "    ro for status data\n"
-                    "    -x for RPCs\n"
-                    "    -n for Notification\n\n"
-                    "  <name> is the name of the node\n"
-                    "    (<name>) means that the node is a choice node\n"
-                    "    :(<name>) means that the node is a case node\n\n"
-                    "    if the node is augmented into the tree from another module,\n"
-                    "    it is printed with the module name as <module-name>:<name>.\n\n"
-                    "  <opts> is one of:\n"
-                    "    ? for an optional leaf or choice\n"
-                    "    ! for a presence container\n"
-                    "    * for a leaf-list or list\n"
-                    "    [<keys>] for a list's keys\n\n"
-                    "  <type> is the name of the type for leafs and leaf-lists\n"
-                    "    If there is a default value defined, it is printed within\n"
-                    "    angle brackets <default-value>.\n"
-                    "    If the type is a leafref, the type is printed as -> TARGET`\n\n"
-                    "  <if-features> is the list of features this node depends on,\n"
-                    "    printed within curly brackets and a question mark {...}?\n\n");
-}
-
-void
-version(void)
-{
-    fprintf(stdout, "yanglint %s\n", PROJECT_VERSION);
-}
-
-void
+static void
 libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
 {
     char *levstr;
 
-    if (level <= verbose) {
-        switch(level) {
-        case LY_LLERR:
-            levstr = "err :";
-            break;
-        case LY_LLWRN:
-            levstr = "warn:";
-            break;
-        case LY_LLVRB:
-            levstr = "verb:";
-            break;
-        default:
-            levstr = "dbg :";
-            break;
-        }
-        if (path) {
-            fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
-        } else {
-            fprintf(stderr, "%s %s\n", levstr, msg);
-        }
+    switch (level) {
+    case LY_LLERR:
+        levstr = "err :";
+        break;
+    case LY_LLWRN:
+        levstr = "warn:";
+        break;
+    case LY_LLVRB:
+        levstr = "verb:";
+        break;
+    default:
+        levstr = "dbg :";
+        break;
+    }
+    if (path) {
+        fprintf(stderr, "libyang %s %s (%s)\n", levstr, msg, path);
+    } else {
+        fprintf(stderr, "libyang %s %s\n", levstr, msg);
     }
 }
 
-/*
- * return:
- * 0 - error
- * 1 - schema format
- * 2 - data format
+static int
+fill_context_inputs(int argc, char *argv[], struct context *c)
+{
+    struct ly_in *in;
+    uint8_t request_expected = 0;
+
+    /* process the operational content if any */
+    if (c->data_operational.path) {
+        if (get_input(c->data_operational.path, NULL, &c->data_operational.format, &c->data_operational.in)) {
+            return -1;
+        }
+    }
+
+    for (int i = 0; i < argc - optind; i++) {
+        LYS_INFORMAT format_schema = LYS_IN_UNKNOWN;
+        LYD_FORMAT format_data = LYD_UNKNOWN;
+        in = NULL;
+
+        if (get_input(argv[optind + i], &format_schema, &format_data, &in)) {
+            return -1;
+        }
+
+        if (format_schema) {
+            LY_ERR ret;
+            uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
+            char *dir, *module;
+            const char *fall = "*";
+            const char **features = &fall;
+            const struct lys_module *mod;
+
+            if (parse_schema_path(argv[optind + i], &dir, &module)) {
+                return -1;
+            }
+
+            /* add temporarily also the path of the module itself */
+            if (ly_ctx_set_searchdir(c->ctx, dir) == LY_EEXIST) {
+                path_unset = 0;
+            }
+
+            /* get features list for this module */
+            get_features(&c->schema_features, module, &features);
+
+            /* temporary cleanup */
+            free(dir);
+            free(module);
+
+            ret = lys_parse(c->ctx, in, format_schema, features, &mod);
+            ly_ctx_unset_searchdir_last(c->ctx, path_unset);
+            ly_in_free(in, 1);
+            if (ret) {
+                YLMSG_E("Processing schema module from %s failed.\n", argv[optind + i]);
+                return -1;
+            }
+
+            if (c->schema_out_format) {
+                /* modules 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]);
+                    return -1;
+                }
+            }
+        } else if (request_expected) {
+            if (fill_cmdline_file(&c->data_requests, in, argv[optind + i], format_data)) {
+                ly_in_free(in, 1);
+                return -1;
+            }
+
+            request_expected = 0;
+        } else if (format_data) {
+            struct cmdline_file *rec;
+
+            rec = fill_cmdline_file(&c->data_inputs, in, argv[optind + i], format_data);
+            if (!rec) {
+                ly_in_free(in, 1);
+                return -1;
+            }
+
+            if ((c->data_type == LYD_VALIDATE_OP_REPLY) && !c->data_request_paths.count) {
+                /* requests for the replies are expected in another input file */
+                if (++i == argc - optind) {
+                    /* there is no such file */
+                    YLMSG_E("Missing request input file for the reply input file %s.\n", rec->path);
+                    return -1;
+                }
+
+                request_expected = 1;
+            }
+        }
+    }
+
+    if (request_expected) {
+        YLMSG_E("Missing request input file for the reply input file %s.\n",
+                ((struct cmdline_file *)c->data_inputs.objs[c->data_inputs.count - 1])->path);
+        return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief Process command line options and store the settings into the context.
+ *
+ * return -1 in case of error;
+ * return 0 in case of success and ready to process
+ * return 1 in case of success, but expect to exit.
  */
 static int
-get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
+fill_context(int argc, char *argv[], struct context *c)
 {
-    char *ptr;
-    LYS_INFORMAT informat_s;
-    LYD_FORMAT informat_d;
+    int ret;
 
-    /* get the file format */
-    if ((ptr = strrchr(filename, '.')) != NULL) {
-        ++ptr;
-        if (!strcmp(ptr, "yang")) {
-            informat_s = LYS_IN_YANG;
-            informat_d = 0;
-        } else if (!strcmp(ptr, "yin")) {
-            informat_s = LYS_IN_YIN;
-            informat_d = 0;
-        } else if (!strcmp(ptr, "xml")) {
-            informat_s = 0;
-            informat_d = LYD_XML;
-        } else if (!strcmp(ptr, "json")) {
-            informat_s = 0;
-            informat_d = LYD_JSON;
-        } else {
-            fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
-            return 0;
-        }
-    } else {
-        fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
-        return 0;
-    }
-
-    if (data) {
-        (*data) = informat_d;
-    }
-
-    if (schema) {
-        (*schema) = informat_s;
-    }
-
-    if (informat_s) {
-        return 1;
-    } else {
-        return 2;
-    }
-}
-
-int
-main_ni(int argc, char* argv[])
-{
-    int ret = EXIT_FAILURE;
-    int opt, opt_index = 0, i, featsize = 0;
+    int opt, opt_index;
     struct option options[] = {
-#if 0
-        {"auto",             no_argument,       NULL, 'a'},
-#endif
         {"default",          required_argument, NULL, 'd'},
+        {"disable-searchdir", no_argument,      NULL, 'D'},
+        {"present",          no_argument,       NULL, 'e'},
         {"format",           required_argument, NULL, 'f'},
         {"features",         required_argument, NULL, 'F'},
-#if 0
-        {"tree-print-groupings", no_argument,   NULL, 'g'},
-        {"tree-print-uses",  no_argument,       NULL, 'u'},
-        {"tree-no-leafref-target", no_argument, NULL, 'n'},
-        {"tree-path",        required_argument, NULL, 'P'},
-        {"tree-line-length", required_argument, NULL, 'L'},
+#ifndef NDEBUG
+        {"debug",            required_argument, NULL, 'G'},
 #endif
         {"help",             no_argument,       NULL, 'h'},
-#if 0
-        {"tree-help",        no_argument,       NULL, 'H'},
-#endif
-        {"allimplemented",   no_argument,       NULL, 'i'},
-        {"disable-cwd-search", no_argument,     NULL, 'D'},
+        {"makeimplemented",  no_argument,       NULL, 'i'},
         {"list",             no_argument,       NULL, 'l'},
-#if 0
         {"merge",            no_argument,       NULL, 'm'},
-#endif
         {"output",           required_argument, NULL, 'o'},
-        {"path",             required_argument, NULL, 'p'},
-        {"info-path",        required_argument, NULL, 'P'},
-#if 0
-        {"running",          required_argument, NULL, 'r'},
         {"operational",      required_argument, NULL, 'O'},
-#endif
+        {"path",             required_argument, NULL, 'p'},
+        {"schema-node",      required_argument, NULL, 'P'},
         {"single-node",      no_argument,       NULL, 'q'},
+        {"request",          required_argument, NULL, 'r'},
         {"strict",           no_argument,       NULL, 's'},
         {"type",             required_argument, NULL, 't'},
         {"version",          no_argument,       NULL, 'v'},
         {"verbose",          no_argument,       NULL, 'V'},
-#ifndef NDEBUG
-        {"debug",            required_argument, NULL, 'G'},
-#endif
-        {NULL,               required_argument, NULL, 'y'},
         {NULL,               0,                 NULL, 0}
     };
-    struct ly_out *out = NULL;
-    struct ly_ctx *ctx = NULL;
-    const struct lys_module *mod;
-    LYS_OUTFORMAT outformat_s = 0;
-    LYS_INFORMAT informat_s;
-    LYD_FORMAT informat_d, outformat_d = 0;
-#if 0
-    LYD_FORMAT ylformat = 0;
-#endif
-    struct ly_set *searchpaths = NULL;
-    const char *outtarget_s = NULL;
-    char **feat = NULL, *ptr, *featlist, *dir;
-    struct stat st;
-    uint32_t u;
-    int options_ctx = LY_CTX_NO_YANGLIBRARY, list = 0, outoptions_s = 0, outline_length_s = 0;
-    int autodetection = 0, options_parser = 0, merge = 0;
-    const char *oper_file = NULL;
-    int options_dflt = 0;
-#if 0
-    ly_bool envelope = 0;
-    const char *envelope_s = NULL;
-    char *ylpath = NULL;
-    struct lyxml_elem *iter, *elem;
-    struct *subroot, *next, *node;
-#endif
-    struct lyd_node *tree = NULL;
-    struct dataitem {
-        const char *filename;
-        struct lyd_node *tree;
-        struct dataitem *next;
-        LYD_FORMAT format;
-        int flags;
-    } *data = NULL, *data_item, *data_prev = NULL;
-    struct ly_set *mods = NULL;
-    void *p;
 
-    opterr = 0;
+    uint16_t options_ctx = 0;
+    uint8_t data_type_set = 0;
+
 #ifndef NDEBUG
-    while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:O:st:vVG:y:", options, &opt_index)) != -1)
+    while ((opt = getopt_long(argc, argv, "d:Df:F:hilmo:P:qr:st:vV", options, &opt_index)) != -1) {
 #else
-    while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:O:st:vVy:", options, &opt_index)) != -1)
+    while ((opt = getopt_long(argc, argv, "d:Df:F:G:hilmo:P:qr:st:vV", options, &opt_index)) != -1) {
 #endif
-    {
         switch (opt) {
-#if 0
-        }
-        case 'a':
-            envelope = 1;
-            break;
-#endif
-        case 'd':
-            if (!strcmp(optarg, "all")) {
-                options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
-            } else if (!strcmp(optarg, "all-tagged")) {
-                options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
-            } else if (!strcmp(optarg, "trim")) {
-                options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
-            } else if (!strcmp(optarg, "implicit-tagged")) {
-                options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
+        case 'd': /* --default */
+            if (!strcasecmp(optarg, "all")) {
+                c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
+            } else if (!strcasecmp(optarg, "all-tagged")) {
+                c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
+            } else if (!strcasecmp(optarg, "trim")) {
+                c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
+            } else if (!strcasecmp(optarg, "implicit-tagged")) {
+                c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
             } else {
-                fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
+                YLMSG_E("Unknown default mode %s\n", optarg);
                 help(1);
-                goto cleanup;
+                return -1;
             }
             break;
-        case 'f':
-            if (!strcasecmp(optarg, "yang")) {
-                outformat_s = LYS_OUT_YANG;
-                outformat_d = 0;
-#if 0
-            } else if (!strcasecmp(optarg, "tree")) {
-                outformat_s = LYS_OUT_TREE;
-                outformat_d = 0;
-            } else if (!strcasecmp(optarg, "tree-rfc")) {
-                outformat_s = LYS_OUT_TREE;
-                outoptions_s |= LYS_OUTOPT_TREE_RFC;
-                outformat_d = 0;
-#endif
-            } else if (!strcasecmp(optarg, "yin")) {
-                outformat_s = LYS_OUT_YIN;
-                outformat_d = 0;
-            } else if (!strcasecmp(optarg, "info")) {
-                outformat_s = LYS_OUT_YANG_COMPILED;
-                outformat_d = 0;
-            } else if (!strcasecmp(optarg, "xml")) {
-                outformat_s = 0;
-                outformat_d = LYD_XML;
-            } else if (!strcasecmp(optarg, "json")) {
-                outformat_s = 0;
-                outformat_d = LYD_JSON;
-            } else {
-                fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
-                help(1);
-                goto cleanup;
-            }
-            break;
-        case 'F':
-            featsize++;
-            if (!feat) {
-                p = malloc(sizeof *feat);
-            } else {
-                p = realloc(feat, featsize * sizeof *feat);
-            }
-            if (!p) {
-                fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
-                goto cleanup;
-            }
-            feat = p;
-            feat[featsize - 1] = strdup(optarg);
-            ptr = strchr(feat[featsize - 1], ':');
-            if (!ptr) {
-                fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
-                goto cleanup;
-            }
-            *ptr = '\0';
 
-            break;
-#if 0
-        case 'g':
-            outoptions_s |= LYS_OUTOPT_TREE_GROUPING;
-            break;
-        case 'u':
-            outoptions_s |= LYS_OUTOPT_TREE_USES;
-            break;
-        case 'n':
-            outoptions_s |= LYS_OUTOPT_TREE_NO_LEAFREF;
-            break;
-#endif
-        case 'P':
-            outtarget_s = optarg;
-            break;
-#if 0
-        case 'L':
-            outline_length_s = atoi(optarg);
-            break;
-#endif
-        case 'h':
-            help(0);
-            ret = EXIT_SUCCESS;
-            goto cleanup;
-#if 0
-        case 'H':
-            tree_help();
-            ret = EXIT_SUCCESS;
-            goto cleanup;
-#endif
-        case 'i':
-            options_ctx |= LY_CTX_ALL_IMPLEMENTED;
-            break;
-        case 'D':
+        case 'D': /* --disable-search */
             if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
-                fprintf(stderr, "yanglint error: -D specified too many times.\n");
-                goto cleanup;
-            } else if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
+                YLMSG_W("The -D option specified too many times.\n");
+            }
+            if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
                 options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
                 options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
             } else {
                 options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
             }
             break;
-        case 'l':
-            list = 1;
-            break;
-#if 0
-        case 'm':
-            merge = 1;
-            break;
-#endif
-        case 'o':
-            if (out) {
-                if (ly_out_filepath(out, optarg) != NULL) {
-                    fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
-                    goto cleanup;
-                }
-            } else {
-                if (ly_out_new_filepath(optarg, &out)) {
-                    fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
-                    goto cleanup;
-                }
-            }
-            break;
-        case 'p':
+
+        case 'p': { /* --path */
+            struct stat st;
+
             if (stat(optarg, &st) == -1) {
-                fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
-                goto cleanup;
+                YLMSG_E("Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
+                return -1;
             }
             if (!S_ISDIR(st.st_mode)) {
-                fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
-                goto cleanup;
+                YLMSG_E("Provided search path is not a directory.\n");
+                return -1;
             }
-            if (!searchpaths) {
-                if (ly_set_new(&searchpaths)) {
-                    fprintf(stderr, "yanglint error: Preparing storage for searchpaths failed.\n");
-                    goto cleanup;
+
+            if (ly_set_add(&c->searchpaths, optarg, 0, NULL)) {
+                YLMSG_E("Storing searchpath failed.\n");
+                return -1;
+            }
+
+            break;
+        } /* case 'p' */
+
+        case 'i': /* --makeimplemented */
+            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 'F': /* --features */
+            if (parse_features(optarg, &c->schema_features)) {
+                return -1;
+            }
+            break;
+
+        case 'l': /* --list */
+            c->list = 1;
+            break;
+
+        case 'o': /* --output */
+            if (c->out) {
+                YLMSG_E("Only a single output can be specified.\n");
+                return -1;
+            } else {
+                if (ly_out_new_filepath(optarg, &c->out)) {
+                    YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
+                    return -1;
                 }
             }
-            if (ly_set_add(searchpaths, optarg, 0, NULL)) {
-                fprintf(stderr, "yanglint error: Storing searchpath failed.\n");
-                goto cleanup;
-            }
             break;
-#if 0
-        case 'O':
-            if (oper_file || (options_parser & LYD_OPT_NOEXTDEPS)) {
-                fprintf(stderr, "yanglint error: The operational datastore (-O) cannot be set multiple times.\n");
-                goto cleanup;
-            }
-            if (optarg[0] == '!') {
-                /* ignore extenral dependencies to the operational datastore */
-                options_parser |= LYD_OPT_NOEXTDEPS;
+
+        case 'f': /* --format */
+            if (!strcasecmp(optarg, "yang")) {
+                c->schema_out_format = LYS_OUT_YANG;
+                c->data_out_format = 0;
+            } else if (!strcasecmp(optarg, "yin")) {
+                c->schema_out_format = LYS_OUT_YIN;
+                c->data_out_format = 0;
+            } else if (!strcasecmp(optarg, "info")) {
+                c->schema_out_format = LYS_OUT_YANG_COMPILED;
+                c->data_out_format = 0;
+            } else if (!strcasecmp(optarg, "tree")) {
+                c->schema_out_format = LYS_OUT_TREE;
+                c->data_out_format = 0;
+            } else if (!strcasecmp(optarg, "xml")) {
+                c->schema_out_format = 0;
+                c->data_out_format = LYD_XML;
+            } else if (!strcasecmp(optarg, "json")) {
+                c->schema_out_format = 0;
+                c->data_out_format = LYD_JSON;
             } else {
-                /* external file with the operational datastore */
-                oper_file = optarg;
-            }
-            break;
-#endif
-        case 's':
-            options_parser |= LYD_PARSE_STRICT;
-            break;
-        case 't':
-            if (!strcmp(optarg, "auto")) {
-                autodetection = 1;
-            /*} else if (!strcmp(optarg, "config")) {
-                options_parser |= LYD_OPT_CONFIG;
-            } else if (!strcmp(optarg, "get")) {
-                options_parser |= LYD_OPT_GET;
-            } else if (!strcmp(optarg, "getconfig")) {
-                options_parser |= LYD_OPT_GETCONFIG;
-            } else if (!strcmp(optarg, "edit")) {
-                options_parser |= LYD_OPT_EDIT;*/
-            } else if (!strcmp(optarg, "data")) {
-                /* no options */
-            } else {
-                fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
+                YLMSG_E("Unknown output format %s\n", optarg);
                 help(1);
-                goto cleanup;
+                return -1;
             }
             break;
-        case 'v':
-            version();
-            ret = EXIT_SUCCESS;
-            goto cleanup;
-        case 'V':
-            verbose++;
+
+        case 'P': /* --schema-node */
+            c->schema_node_path = optarg;
             break;
+
+        case 'q': /* --single-node */
+            c->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
+            break;
+
+        case 's': /* --strict */
+            c->data_parse_options |= LYD_PARSE_STRICT;
+            break;
+
+        case 'e': /* --present */
+            c->data_validate_options |= LYD_VALIDATE_PRESENT;
+            break;
+
+        case 't': /* --type */
+            if (data_type_set) {
+                YLMSG_E("The data type (-t) cannot be set multiple times.\n");
+                return -1;
+            }
+
+            if (!strcasecmp(optarg, "config")) {
+                c->data_parse_options |= LYD_PARSE_NO_STATE;
+            } else if (!strcasecmp(optarg, "get")) {
+                c->data_parse_options |= LYD_PARSE_ONLY;
+            } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config")) {
+                c->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+            } else if (!strcasecmp(optarg, "edit")) {
+                c->data_parse_options |= LYD_PARSE_ONLY;
+            } else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
+                c->data_type = LYD_VALIDATE_OP_RPC;
+            } else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
+                c->data_type = LYD_VALIDATE_OP_REPLY;
+            } else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
+                c->data_type = LYD_VALIDATE_OP_NOTIF;
+            } else if (!strcasecmp(optarg, "data")) {
+                /* default option */
+            } else {
+                YLMSG_E("Unknown data tree type %s\n", optarg);
+                help(1);
+                return -1;
+            }
+
+            data_type_set = 1;
+            break;
+
+        case 'O': /* --operational */
+            if (c->data_operational.path) {
+                YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
+                return -1;
+            }
+            c->data_operational.path = optarg;
+            break;
+
+        case 'r': /* --request */
+            if (ly_set_add(&c->data_request_paths, optarg, 0, NULL)) {
+                YLMSG_E("Storing request path failed.\n");
+                return -1;
+            }
+            break;
+
+        case 'm': /* --merge */
+            c->data_merge = 1;
+            break;
+
+        case 'h': /* --help */
+            help(0);
+            return 1;
+
+        case 'v': /* --version */
+            version();
+            return 1;
+
+        case 'V': { /* --verbose */
+            LY_LOG_LEVEL verbosity = ly_log_level(LY_LLERR);
+            ly_log_level(verbosity);
+
+            if (verbosity < LY_LLDBG) {
+                ly_log_level(verbosity + 1);
+            }
+            break;
+        } /* case 'V' */
+
 #ifndef NDEBUG
-        case 'G':
-            u = 0;
-            ptr = optarg;
+        case 'G': { /* --debug */
+            uint32_t dbg_groups = 0;
+            const char *ptr = optarg;
+
             while (ptr[0]) {
-                if (!strncmp(ptr, "dict", 4)) {
-                    u |= LY_LDGDICT;
+                if (!strncasecmp(ptr, "dict", 4)) {
+                    dbg_groups |= LY_LDGDICT;
                     ptr += 4;
-                } else if (!strncmp(ptr, "xpath", 5)) {
-                    u |= LY_LDGXPATH;
+                } else if (!strncasecmp(ptr, "xpath", 5)) {
+                    dbg_groups |= LY_LDGXPATH;
                     ptr += 5;
                 }
 
                 if (ptr[0]) {
                     if (ptr[0] != ',') {
-                        fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
-                        goto cleanup;
+                        YLMSG_E("Unknown debug group string \"%s\"\n", optarg);
+                        return -1;
                     }
                     ++ptr;
                 }
             }
-            ly_log_dbg_groups(u);
+            ly_log_dbg_groups(dbg_groups);
             break;
+        } /* case 'G' */
 #endif
-#if 0
-        case 'y':
-            ptr = strrchr(optarg, '.');
-            if (ptr) {
-                ptr++;
-                if (!strcmp(ptr, "xml")) {
-                    ylformat = LYD_XML;
-                } else if (!strcmp(ptr, "json")) {
-                    ylformat = LYD_JSON;
-                } else {
-                    fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
-                    goto cleanup;
-                }
-            } else {
-                fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
-                goto cleanup;
-            }
-            ylpath = optarg;
-            break;
-#endif
-        default:
-            help(1);
-            if (optopt) {
-                fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
-            } else {
-                fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
-            }
-            goto cleanup;
+        } /* switch */
+    }
+
+    /* libyang context */
+    if (ly_ctx_new(NULL, options_ctx, &c->ctx)) {
+        YLMSG_E("Unable to create libyang context.\n");
+        return -1;
+    }
+    for (uint32_t u = 0; u < c->searchpaths.count; ++u) {
+        ly_ctx_set_searchdir(c->ctx, c->searchpaths.objs[u]);
+    }
+
+    /* additional checks for the options combinations */
+    if (!c->list && (optind >= argc)) {
+        help(1);
+        YLMSG_E("Missing <schema> to process.\n");
+        return 1;
+    }
+
+    if (c->data_merge) {
+        if (c->data_type || (c->data_parse_options & LYD_PARSE_ONLY)) {
+            /* switch off the option, incompatible input data type */
+            c->data_merge = 0;
+        } else {
+            /* postpone validation after the merge of all the input data */
+            c->data_parse_options |= LYD_PARSE_ONLY;
         }
     }
 
-    /* check options compatibility */
-    if (!list && optind >= argc) {
-        help(1);
-        fprintf(stderr, "yanglint error: missing <file> to process\n");
-        goto cleanup;
+    if (c->data_operational.path && !c->data_type) {
+        YLMSG_E("Operational datastore takes effect only with RPCs/Actions/Replies/Notifications input data types.\n");
+        c->data_operational.path = NULL;
     }
-    if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
-        /* we have multiple schemas to be printed as YIN or YANG */
-        fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
-        goto cleanup;
-    }
-    if (outoptions_s || outtarget_s || outline_length_s) {
-#if 0
-        if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
-            /* we have --tree-print-grouping with other output format than tree */
-            fprintf(stderr,
-                    "yanglint warning: --tree options take effect only in case of the tree output format.\n");
+
+    /* default output stream */
+    if (!c->out) {
+        if (ly_out_new_file(stdout, &c->out)) {
+            YLMSG_E("Unable to set stdout as output.\n");
+            return -1;
         }
     }
-    if (merge) {
-        if (autodetection || (options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF))) {
-            fprintf(stderr, "yanglint warning: merging not allowed, ignoring option -m.\n");
-            merge = 0;
-        } else {
-            /* first, files will be parsed as trusted to allow missing data, then the data trees will be merged
-             * and the result will be validated */
-            options_parser |= LYD_OPT_TRUSTED;
+
+    /* process input files provided as standalone command line arguments,
+     * schema modules are parsed and inserted into the context,
+     * data files are just checked and prepared into internal structures for further processing */
+    ret = fill_context_inputs(argc, argv, c);
+    if (ret) {
+        return ret;
+    }
+
+    /* the second batch of checks */
+    if (c->schema_print_options && !c->schema_out_format) {
+        YLMSG_W("Schema printer options specified, but the schema output format is missing.\n");
+    }
+    if (c->schema_parse_options && !c->schema_modules.count) {
+        YLMSG_W("Schema parser options specified, but no schema input file provided.\n");
+    }
+    if (c->data_print_options && !c->data_out_format) {
+        YLMSG_W("data printer options specified, but the data output format is missing.\n");
+    }
+    if ((c->data_parse_options || c->data_type) && !c->data_inputs.count) {
+        YLMSG_W("Data parser options specified, but no data input file provided.\n");
+    }
+
+    if (c->schema_node_path) {
+        c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 0);
+        if (!c->schema_node) {
+            c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 1);
+
+            if (!c->schema_node) {
+                YLMSG_E("Invalid schema path.\n");
+                return -1;
+            }
         }
-#endif
     }
-    if (!outformat_d && options_dflt) {
-        /* we have options for printing default nodes, but data output not specified */
-        fprintf(stderr, "yanglint warning: default mode is ignored when not printing data.\n");
+
+    if (c->data_type == LYD_VALIDATE_OP_REPLY) {
+        if (check_request_paths(c->ctx, &c->data_request_paths, &c->data_inputs)) {
+            return -1;
+        }
     }
-#if 0
-    if (outformat_s && (options_parser || autodetection)) {
-        /* we have options for printing data tree, but output is schema */
-        fprintf(stderr, "yanglint warning: data parser options are ignored when printing schema.\n");
-    }
-    if (oper_file && (!autodetection && !(options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)))) {
-        fprintf(stderr, "yanglint warning: operational datastore applies only to RPCs or Notifications.\n");
-        /* ignore operational datastore file */
-        oper_file = NULL;
-    }
-    if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
-        /* add option to ignore ietf-yang-library data for implicit data type */
-        options_parser |= LYD_OPT_DATA_NO_YANGLIB;
-    }
-#endif
+
+    return 0;
+}
+
+int
+main_ni(int argc, char *argv[])
+{
+    int ret = EXIT_SUCCESS, r;
+    struct context c = {0};
 
     /* set callback for printing libyang messages */
     ly_set_log_clb(libyang_verbclb, 1);
-#if 0
-    /* create libyang context */
-    if (ylpath) {
-        ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat, options_ctx);
-    } else {
-#else
-    {
-#endif
-        ly_ctx_new(NULL, options_ctx, &ctx);
+
+    r = fill_context(argc, argv, &c);
+    if (r < 0) {
+        ret = EXIT_FAILURE;
     }
-    if (!ctx) {
+    if (r) {
         goto cleanup;
     }
 
-    /* set searchpaths */
-    if (searchpaths) {
-        for (u = 0; u < searchpaths->count; u++) {
-            ly_ctx_set_searchdir(ctx, (const char*)searchpaths->objs[u]);
+    /* do the required job - parse, validate, print */
+
+    if (c.list) {
+        /* print the list of schemas */
+        print_list(c.out, c.ctx, c.data_out_format);
+    } else if (c.schema_out_format) {
+        if (c.schema_node) {
+            ret = lys_print_node(c.out, c.schema_node, c.schema_out_format, 0, c.schema_print_options);
+            if (ret) {
+                YLMSG_E("Unable to print schema node %s.\n", c.schema_node_path);
+                goto cleanup;
+            }
+        } else {
+            for (uint32_t 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, 0,
+                        c.schema_print_options);
+                if (ret) {
+                    YLMSG_E("Unable to print module %s.\n", ((struct lys_module *)c.schema_modules.objs[u])->name);
+                    goto cleanup;
+                }
+            }
         }
-    }
-
-    /* derefered setting of verbosity in libyang after context initiation */
-    ly_log_level(verbose);
-
-    if (ly_set_new(&mods)) {
-        fprintf(stderr, "yanglint error: Preparing storage for the parsed modules failed.\n");
-        goto cleanup;
-    }
-
-
-    /* divide input files */
-    for (i = 0; i < argc - optind; i++) {
-        /* get the file format */
-        if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
+    } else if (c.data_out_format) {
+        if (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.data_inputs, &c.data_request_paths, &c.data_requests, NULL)) {
             goto cleanup;
         }
-
-        if (informat_s) {
-            /* load/validate schema */
-            int unset_path = 1;
-
-            if (verbose >= 2) {
-                fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
-            }
-
-            /* add temporarily also the path of the module itself */
-            dir = strdup(argv[optind + i]);
-            if (ly_ctx_set_searchdir(ctx, ptr = dirname(dir)) == LY_EEXIST) {
-                unset_path = 0;
-            }
-            lys_parse_path(ctx, argv[optind + i], informat_s, &mod);
-            ly_ctx_unset_searchdir_last(ctx, unset_path);
-            free(dir);
-            if (!mod) {
-                goto cleanup;
-            }
-            if (ly_set_add(mods, (void *)mod, 0, NULL)) {
-                fprintf(stderr, "yanglint error: Storing parsed module for further processing failed.\n");
-                goto cleanup;
-            }
-        } else {
-            if (autodetection && informat_d != LYD_XML) {
-                /* data file content autodetection is possible only for XML input */
-                fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
-                goto cleanup;
-            }
-
-            /* remember data filename and its format */
-            if (!data) {
-                data = data_item = malloc(sizeof *data);
-            } else {
-                for (data_item = data; data_item->next; data_item = data_item->next);
-                data_item->next = malloc(sizeof *data_item);
-                data_item = data_item->next;
-            }
-            data_item->filename = argv[optind + i];
-            data_item->format = informat_d;
-            data_item->flags = options_parser;
-            data_item->tree = NULL;
-            data_item->next = NULL;
-        }
     }
-    if (outformat_d && !data && !list) {
-        fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
-        goto cleanup;
-    }
-
-    /* enable specified features, if not specified, all the module's features are enabled */
-    u = 4; /* skip internal libyang modules */
-    while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
-        if (!mod->implemented) {
-            continue;
-        }
-
-        for (i = 0; i < featsize; i++) {
-            if (!strcmp(feat[i], mod->name)) {
-                /* parse features spec */
-                featlist = strdup(feat[i] + strlen(feat[i]) + 1);
-                ptr = NULL;
-                while((ptr = strtok(ptr ? NULL : featlist, ","))) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
-                    }
-                    /*if (lys_feature_enable(mod, ptr)) {
-                        fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
-                    }*/
-                }
-                free(featlist);
-                break;
-            }
-        }
-        if (i == featsize) {
-            if (verbose >= 2) {
-                fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
-            }
-            //lys_feature_enable(mod, "*");
-        }
-    }
-    if (!out && (outformat_s || data)) {
-        ly_out_new_file(stdout, &out);
-    }
-    /* convert (print) to FORMAT */
-    if (outformat_s) {
-        if (outtarget_s) {
-            const struct lysc_node *node = lys_find_path(ctx, NULL, outtarget_s, 0);
-            if (node) {
-                lys_print_node(out, node, outformat_s, outline_length_s, outoptions_s);
-            } else {
-                fprintf(stderr, "yanglint error: The requested schema node \"%s\" does not exists.\n", outtarget_s);
-            }
-        } else {
-            for (u = 0; u < mods->count; u++) {
-                if (u) {
-                    ly_print(out, "\n");
-                }
-                lys_print_module(out, (struct lys_module *)mods->objs[u], outformat_s, outline_length_s, outoptions_s);
-            }
-        }
-    } else if (data) {
-
-        /* prepare operational datastore when specified for RPC/Notification */
-        if (oper_file) {
-            struct ly_in *in;
-            tree = NULL;
-
-            if (ly_in_new_filepath(oper_file, 0, &in)) {
-                fprintf(stderr, "yanglint error: Unable to open an operational data file \"%s\".\n", oper_file);
-                goto cleanup;
-            }
-            if (lyd_parse_data(ctx, in, 0, LYD_PARSE_ONLY, 0, &tree) || !tree) {
-                fprintf(stderr, "yanglint error: Failed to parse the operational datastore file for RPC/Notification validation.\n");
-                ly_in_free(in, 0);
-                goto cleanup;
-            }
-            ly_in_free(in, 0);
-        }
-
-        for (data_item = data, data_prev = NULL; data_item; data_prev = data_item, data_item = data_item->next) {
-            /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
-#if 0
-            if (autodetection) {
-                /* erase option not covered by LYD_OPT_TYPEMASK, but used according to the type */
-                options_parser &= ~LYD_OPT_DATA_NO_YANGLIB;
-                /* automatically detect data type from the data top level */
-                data_item->xml = lyxml_parse_path(ctx, data_item->filename, 0);
-                if (!data_item->xml) {
-                    fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
-                    goto cleanup;
-                }
-
-                /* NOTE: namespace is ignored to simplify usage of this feature */
-                if (!strcmp(data_item->xml->name, "data")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
-                    data_item->type = LYD_OPT_DATA;
-                } else if (!strcmp(data_item->xml->name, "config")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
-                    data_item->type = LYD_OPT_CONFIG;
-                } else if (!strcmp(data_item->xml->name, "get-reply")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
-                    data_item->type = LYD_OPT_GET;
-                } else if (!strcmp(data_item->xml->name, "get-config-reply")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
-                    data_item->type = LYD_OPT_GETCONFIG;
-                } else if (!strcmp(data_item->xml->name, "edit-config")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
-                    data_item->type = LYD_OPT_EDIT;
-                } else if (!strcmp(data_item->xml->name, "rpc")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
-                    data_item->type = LYD_OPT_RPC;
-                } else if (!strcmp(data_item->xml->name, "rpc-reply")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
-                    }
-
-                    data_item->type = LYD_OPT_RPCREPLY;
-                    if (!data_item->next || (data_prev && !data_prev->tree)) {
-                        fprintf(stderr, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
-                        goto cleanup;
-                    }
-
-                    continue;
-                } else if (!strcmp(data_item->xml->name, "notification")) {
-                    if (verbose >= 2) {
-                        fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
-                    }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
-                    data_item->type = LYD_OPT_NOTIF;
-
-                    /* ignore eventTime element if present */
-                    while (data_item->xml->child && !strcmp(data_item->xml->child->name, "eventTime")) {
-                        lyxml_free(ctx, data_item->xml->child);
-                    }
-                } else {
-                    fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
-                            data_item->xml->name);
-                    goto cleanup;
-                }
-
-                data_item->tree = lyd_parse_xml(ctx, &data_item->xml->child, options_parser, oper);
-                if (data_prev && data_prev->type == LYD_OPT_RPCREPLY) {
-parse_reply:
-                    /* check result of the RPC parsing, we are going to do another parsing in this step */
-                    if (ly_errno) {
-                        goto cleanup;
-                    }
-
-                    /* check that we really have RPC for the reply */
-                    if (data_item->type != LYD_OPT_RPC) {
-                        fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_prev->filename);
-                        goto cleanup;
-                    }
-
-                    if (data_prev->format == LYD_XML) {
-                        /* ignore <ok> and <rpc-error> elements if present */
-                        u = 0;
-                        LY_TREE_FOR_SAFE(data_prev->xml->child, iter, elem) {
-                            if (!strcmp(data_prev->xml->child->name, "ok")) {
-                                if (u) {
-                                    /* rpc-error or ok already present */
-                                    u = 0x8; /* error flag */
-                                } else {
-                                    u = 0x1 | 0x4; /* <ok> flag with lyxml_free() flag */
-                                }
-                            } else if (!strcmp(data_prev->xml->child->name, "rpc-error")) {
-                                if (u && (u & 0x1)) {
-                                    /* ok already present, rpc-error can be present multiple times */
-                                    u = 0x8; /* error flag */
-                                } else {
-                                    u = 0x2 | 0x4; /* <rpc-error> flag with lyxml_free() flag */
-                                }
-                            }
-
-                            if (u == 0x8) {
-                                fprintf(stderr, "yanglint error: Invalid RPC reply (%s) content.\n", data_prev->filename);
-                                goto cleanup;
-                            } else if (u & 0x4) {
-                                lyxml_free(ctx, data_prev->xml->child);
-                                u &= ~0x4; /* unset lyxml_free() flag */
-                            }
-                        }
-
-                        /* finally, parse RPC reply from the previous step */
-                        data_prev->tree = lyd_parse_xml(ctx, &data_prev->xml->child,
-                                                        (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
-                    } else { /* LYD_JSON */
-                        data_prev->tree = lyd_parse_path(ctx, data_prev->filename, data_item->format,
-                                                         (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
-                    }
-                }
-            } else if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
-                if (data_prev && !data_prev->tree) {
-                    /* now we should have RPC for the preceding RPC reply */
-                    data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format,
-                                                     (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC, oper);
-                    data_item->type = LYD_OPT_RPC;
-                    goto parse_reply;
-                } else {
-                    /* now we have RPC reply which will be parsed in next step together with its RPC */
-                    if (!data_item->next) {
-                        fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
-                        goto cleanup;
-                    }
-                    if (data_item->format == LYD_XML) {
-                        /* create rpc-reply container to unify handling with autodetection */
-                        data_item->xml = calloc(1, sizeof *data_item->xml);
-                        if (!data_item->xml) {
-                            fprintf(stderr, "yanglint error: Memory allocation failed failed.\n");
-                            goto cleanup;
-                        }
-                        data_item->xml->name = lydict_insert(ctx, "rpc-reply", 9);
-                        data_item->xml->prev = data_item->xml;
-                        data_item->xml->child = lyxml_parse_path(ctx, data_item->filename, LYXML_PARSE_MULTIROOT | LYXML_PARSE_NOMIXEDCONTENT);
-                        if (data_item->xml->child) {
-                            data_item->xml->child->parent = data_item->xml;
-                        }
-                    }
-                    continue;
-                }
-            } else {
-#else
-            {
-#endif
-                /* TODO optimize use of ly_in in the loop */
-                struct ly_in *in;
-                if (ly_in_new_filepath(data_item->filename, 0, &in)) {
-                    fprintf(stderr, "yanglint error: input data file \"%s\".\n", data_item->filename);
-                    goto cleanup;
-                }
-                if (lyd_parse_data(ctx, in, 0, options_parser, LYD_VALIDATE_PRESENT, &data_item->tree)) {
-                    fprintf(stderr, "yanglint error: Failed to parse input data file \"%s\".\n", data_item->filename);
-                    ly_in_free(in, 0);
-                    goto cleanup;
-                }
-                ly_in_free(in, 0);
-            }
-#if 0
-            if (merge && data != data_item) {
-                if (!data->tree) {
-                    data->tree = data_item->tree;
-                } else if (data_item->tree) {
-                    /* merge results */
-                    if (lyd_merge(data->tree, data_item->tree, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
-                        fprintf(stderr, "yanglint error: merging multiple data trees failed.\n");
-                        goto cleanup;
-                    }
-                }
-                data_item->tree = NULL;
-            }
-#endif
-        }
-#if 0
-        if (merge) {
-            /* validate the merged data tree, do not trust the input, invalidate all the data first */
-            LY_TREE_FOR(data->tree, subroot) {
-                LY_TREE_DFS_BEGIN(subroot, next, node) {
-                    node->validity = LYD_VAL_OK;
-                    switch (node->schema->nodetype) {
-                    case LYS_LEAFLIST:
-                    case LYS_LEAF:
-                        if (((struct lys_node_leaf *)node->schema)->type.base == LY_TYPE_LEAFREF) {
-                            node->validity |= LYD_VAL_LEAFREF;
-                        }
-                        break;
-                    case LYS_LIST:
-                        node->validity |= LYD_VAL_UNIQUE;
-                        /* falls through */
-                    case LYS_CONTAINER:
-                    case LYS_NOTIF:
-                    case LYS_RPC:
-                    case LYS_ACTION:
-                        node->validity |= LYD_VAL_MAND;
-                        break;
-                    default:
-                        break;
-                    }
-                    LY_TREE_DFS_END(subroot, next, node)
-                }
-            }
-            if (lyd_validate(&data->tree, options_parser & ~LYD_OPT_TRUSTED, ctx)) {
-                goto cleanup;
-            }
-        }
-#endif
-        /* print only if data output format specified */
-        if (outformat_d) {
-            for (data_item = data; data_item; data_item = data_item->next) {
-                if (!merge && verbose >= 2) {
-                    fprintf(stdout, "File %s:\n", data_item->filename);
-                }
-#if 0
-                if (outformat_d == LYD_XML && envelope) {
-                    switch (data_item->type) {
-                    case LYD_OPT_DATA:
-                        envelope_s = "data";
-                        break;
-                    case LYD_OPT_CONFIG:
-                        envelope_s = "config";
-                        break;
-                    case LYD_OPT_GET:
-                        envelope_s = "get-reply";
-                        break;
-                    case LYD_OPT_GETCONFIG:
-                        envelope_s = "get-config-reply";
-                        break;
-                    case LYD_OPT_EDIT:
-                        envelope_s = "edit-config";
-                        break;
-                    case LYD_OPT_RPC:
-                        envelope_s = "rpc";
-                        break;
-                    case LYD_OPT_RPCREPLY:
-                        envelope_s = "rpc-reply";
-                        break;
-                    case LYD_OPT_NOTIF:
-                        envelope_s = "notification";
-                        break;
-                    }
-                    fprintf(out, "<%s>\n", envelope_s);
-                    if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
-                        /* action */
-                        fprintf(out, "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n");
-                    }
-                }
-#endif
-                lyd_print_all(out, data_item->tree, outformat_d, options_dflt);
-#if 0
-                if (envelope_s) {
-                    if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
-                        fprintf(out, "</action>\n");
-                    }
-                    fprintf(out, "</%s>\n", envelope_s);
-                }
-                if (merge) {
-                    /* stop after first item */
-                    break;
-                }
-#endif
-            }
-        }
-    }
-#if 0
-    if (list) {
-        print_list(out, ctx, outformat_d);
-    }
-#endif
-
-    ret = EXIT_SUCCESS;
 
 cleanup:
-    ly_set_free(mods, NULL);
-    ly_set_free(searchpaths, NULL);
-    for (i = 0; i < featsize; i++) {
-        free(feat[i]);
-    }
-    free(feat);
-    for (; data; data = data_item) {
-        data_item = data->next;
-        lyd_free_all(data->tree);
-        free(data);
-    }
-    ly_ctx_destroy(ctx, NULL);
+    /* cleanup */
+    erase_context(&c);
 
-    ly_out_free(out, NULL, 1);
     return ret;
 }