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/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;
 }