yanglint CHANGE handling --running option

- change order of <rpc-file> and <reply-file> input files when parsing
  RPC reply. It allows to use rpcreply TYPE, not only the auto TYPE
- -r with optional argument is problematic due to portability, instead
  use special value '!' to ignore external references
- unify options in interactive and non-interactive modes
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index ff4040c..8c44f8f 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -88,15 +88,16 @@
         "        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. This is just a virtual TYPE, for parsing replies, 'auto' must be\n"
-        "                          used since the data <file>s are expected in pairs - first <file> is expected as\n"
-        "                          'rpc' TYPE, the second <file> is expected as reply to the previous RPC.\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."
+        "                          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"
-        "  -r [FILE], --running[=FILE]\n"
-        "                        - Optional parameter for 'rpc' and 'notif' TYPEs, the FILE contains running\n"
+        "  -r FILE, --running=FILE\n"
+        "                        - Optional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the FILE contains running\n"
         "                          configuration datastore data referenced from the RPC/Notification. The same data\n"
         "                          apply to all input data <file>s. Note that the file is validated as 'data' TYPE.\n"
-        "                          If the option is used without the FILE argument, the external references are ignored.\n\n"
+        "                          Special value '!' can be used as FILE argument 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"
@@ -236,7 +237,7 @@
         {"allimplemented",   no_argument,       NULL, 'i'},
         {"output",           required_argument, NULL, 'o'},
         {"path",             required_argument, NULL, 'p'},
-        {"running",          optional_argument, NULL, 'r'},
+        {"running",          required_argument, NULL, 'r'},
         {"strict",           no_argument,       NULL, 's'},
         {"version",          no_argument,       NULL, 'v'},
         {"verbose",          no_argument,       NULL, 'V'},
@@ -258,25 +259,25 @@
     char **feat = NULL, *ptr, *featlist, *ylpath = NULL, *dir;
     struct stat st;
     uint32_t u;
-    int options_dflt = 0, options_parser = 0, options_allimplemented = 0, envelope = 0;
+    int options_dflt = 0, options_parser = 0, options_allimplemented = 0, envelope = 0, autodetection = 0;
     struct dataitem {
         const char *filename;
-        LYD_FORMAT format;
-        int type;
+        struct lyxml_elem *xml;
         struct lyd_node *tree;
         struct dataitem *next;
+        LYD_FORMAT format;
+        int type;
     } *data = NULL, *data_item, *data_prev = NULL;
     struct ly_set *mods = NULL;
     struct lyd_node *running = NULL;
-    struct lyxml_elem *xml = NULL;
     void *p;
     int index = 0;
 
     opterr = 0;
 #ifndef NDEBUG
-    while ((opt = getopt_long(argc, argv, "ad:f:F:ghHio:p:r::st:vVG:y:", options, &opt_index)) != -1)
+    while ((opt = getopt_long(argc, argv, "ad:f:F:ghHio:p:r:st:vVG:y:", options, &opt_index)) != -1)
 #else
-    while ((opt = getopt_long(argc, argv, "ad:f:F:ghHio:p:r::st:vVy:", options, &opt_index)) != -1)
+    while ((opt = getopt_long(argc, argv, "ad:f:F:ghHio:p:r:st:vVy:", options, &opt_index)) != -1)
 #endif
     {
         switch (opt) {
@@ -384,12 +385,12 @@
                 fprintf(stderr, "yanglint error: The running datastore (-r) cannot be set multiple times.\n");
                 goto cleanup;
             }
-            if (optarg) {
-                /* external file with the running datastore */
-                running_file = optarg;
-            } else {
+            if (optarg[0] == '!') {
                 /* ignore extenral dependencies to the running datastore */
                 options_parser |= LYD_OPT_NOEXTDEPS;
+            } else {
+                /* external file with the running datastore */
+                running_file = optarg;
             }
             break;
         case 's':
@@ -397,7 +398,8 @@
             break;
         case 't':
             if (!strcmp(optarg, "auto")) {
-                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK);
+                autodetection = 1;
             } else if (!strcmp(optarg, "config")) {
                 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
             } else if (!strcmp(optarg, "get")) {
@@ -495,9 +497,6 @@
         fprintf(stderr, "yanglint error: missing <file> to process\n");
         goto cleanup;;
     }
-    if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
-
-    }
     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");
@@ -516,12 +515,12 @@
         /* 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 (outformat_s && options_parser) {
+    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 (running_file && !(options_parser & (LYD_OPT_RPC | LYD_OPT_NOTIF))) {
-        fprintf(stderr, "yanglint warning: running datastor applies only to RPCs or Notifications.\n");
+    if (running_file && (!autodetection && !(options_parser & (LYD_OPT_RPC | LYD_OPT_NOTIF)))) {
+        fprintf(stderr, "yanglint warning: running datastore applies only to RPCs or Notifications.\n");
         /* ignore running datastore file */
         running_file = NULL;
     }
@@ -584,7 +583,7 @@
             }
             ly_set_add(mods, (void *)mod, 0);
         } else {
-            if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK && informat_d != LYD_XML) {
+            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;
@@ -602,6 +601,7 @@
             data_item->format = informat_d;
             data_item->type = options_parser & LYD_OPT_TYPEMASK;
             data_item->tree = NULL;
+            data_item->xml = NULL;
             data_item->next = NULL;
         }
     }
@@ -665,62 +665,65 @@
 
         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 ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
+            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 */
-                xml = lyxml_parse_path(ctx, data_item->filename, 0);
-                if (!xml) {
+                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(xml->name, "data")) {
+                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(xml->name, "config")) {
+                } 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(xml->name, "get-reply")) {
+                } 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(xml->name, "get-config-reply")) {
+                } 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(xml->name, "edit-config")) {
+                } 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(xml->name, "rpc")) {
+                } 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(xml->name, "rpc-reply")) {
+                } else if (!strcmp(data_item->xml->name, "rpc-reply")) {
                     if (verbose >= 2) {
                         fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
                     }
-                    if (!data_prev || data_prev->type != LYD_OPT_RPC) {
-                        fprintf(stderr, "RPC reply (%s) must be coupled with the original RPC, see help.\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;
                     }
-                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
-                    data_item->type = LYD_OPT_RPCREPLY;
-                } else if (!strcmp(xml->name, "notification")) {
+                    continue;
+                } else if (!strcmp(data_item->xml->name, "notification")) {
                     if (verbose >= 2) {
                         fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
                     }
@@ -728,21 +731,46 @@
                     data_item->type = LYD_OPT_NOTIF;
 
                     /* ignore eventTime element if present */
-                    while (xml->child && !strcmp(xml->child->name, "eventTime")) {
-                        lyxml_free(ctx, xml->child);
+                    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",
-                            xml->name);
+                            data_item->xml->name);
                     goto cleanup;
                 }
 
-                if (data_item->type == LYD_OPT_RPCREPLY) {
-                    data_item->tree = lyd_parse_xml(ctx, &xml->child, options_parser, data_prev->tree, running);
-                } else {
-                    data_item->tree = lyd_parse_xml(ctx, &xml->child, options_parser, running);
+                data_item->tree = lyd_parse_xml(ctx, &data_item->xml->child, options_parser, running);
+                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, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
+                        goto cleanup;
+                    }
+
+                    /* finally, parse RPC reply from the previous step */
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
+                    data_prev->tree = lyd_parse_xml(ctx, &data_prev->xml->child, options_parser, data_item->tree, running);
                 }
-                options_parser |= LYD_OPT_TYPEMASK;
+            } 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_xml(ctx, &data_item->xml->child, options_parser, running);
+                    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, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
+                        goto cleanup;
+                    }
+                    continue;
+                }
             } else {
                 data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format, options_parser, running);
             }
@@ -816,10 +844,10 @@
     free(feat);
     for (; data; data = data_item) {
         data_item = data->next;
+        lyxml_free(ctx, data->xml);
         lyd_free_withsiblings(data->tree);
         free(data);
     }
-    lyxml_free(ctx, xml);
     ly_ctx_destroy(ctx, NULL);
 
     return ret;