]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
script: fix backward compatibility for options after non-option args
authorKarel Zak <kzak@redhat.com>
Tue, 7 Apr 2026 13:20:04 +0000 (15:20 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 7 Apr 2026 15:00:52 +0000 (17:00 +0200)
The commit 7268e79b added "+" to the getopt_long() options string to
support the "--" separator for specifying commands. The "+" prefix
makes getopt stop processing at the first non-option argument, which
breaks the traditional "script file -c command" usage.

Fix this by pre-scanning argv for "--" before getopt, separating the
command arguments, and removing the "+" prefix to restore GNU getopt
argument permutation.

Addresses: 7268e79bc5365034a6e5b38ac5d9bf635e2dafc2
Reported-by: Chris Hofstaedtler <zeha@debian.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
term-utils/script.c

index 037e2fcabfff710d700111b2abbff3c000b772c3..0a1773e9e823e62ad811dc647fb48ae040fdc98a 100644 (file)
@@ -770,6 +770,8 @@ int main(int argc, char **argv)
        int ch, format = 0, caught_signal = 0, rc = 0, echo = 1;
        const char *outfile = NULL, *infile = NULL;
        const char *timingfile = NULL, *shell = NULL;
+       char **cmd_argv = NULL;
+       int cmd_argc = 0;
 
        enum { FORCE_OPTION = CHAR_MAX + 1 };
 
@@ -815,7 +817,21 @@ int main(int argc, char **argv)
 
        ctl.isterm = isatty(STDIN_FILENO);
 
-       while ((ch = getopt_long(argc, argv, "+aB:c:eE:fI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) {
+       /*
+        * Pre-scan for "--" separator to split argv into options+outfile
+        * and command parts. This allows getopt to permute arguments
+        * before "--" (backward compatible with "script file -c cmd").
+        */
+       for (ch = 1; ch < argc; ch++) {
+               if (strcmp(argv[ch], "--") == 0) {
+                       cmd_argv = argv + ch + 1;
+                       cmd_argc = argc - ch - 1;
+                       argc = ch;
+                       break;
+               }
+       }
+
+       while ((ch = getopt_long(argc, argv, "aB:c:eE:fI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) {
 
                err_exclusive_options(ch, longopts, excl, excl_st);
 
@@ -895,9 +911,10 @@ int main(int argc, char **argv)
        argv += optind;
 
        /*
-        * Note that "--" separates non-option arguments. The script can be
-        * used with an <outfile> name as well as with a <command>. The
-        * <outfile> is always before "--" and <command> is always after "--".
+        * The "--" separator splits non-option arguments into <outfile>
+        * (before "--") and <command> (after "--"). The "--" and command
+        * arguments have been already separated by the pre-scan above.
+        *
         * Possible combinations are:
         *
         * script [options] <outfile>
@@ -907,8 +924,7 @@ int main(int argc, char **argv)
 
        /* default if no --log-* specified */
        if (!outfile && !infile) {
-               /* if argv[0] is not after --, use argv[0] as outfile */
-               if (argc > 0 && strcmp(argv[-1], "--") != 0) {
+               if (argc > 0) {
                        outfile = argv[0];
                        argc--;
                        argv++;
@@ -921,26 +937,19 @@ int main(int argc, char **argv)
                log_associate(&ctl, &ctl.out, outfile, SCRIPT_FMT_RAW);
        }
 
-       /* skip -- to non-option arguments */
-       if (argc > 0 && strcmp(argv[0], "--") == 0) {
-               argc--;
-               argv++;
-       }
-
-       /* concat non-option arguments as command */
-       if (argc > 0 && strcmp(argv[-1], "--") == 0) {
+       /* concat arguments after "--" as command */
+       if (cmd_argc > 0) {
                if (ctl.command != NULL) {
-                       warnx(_("option --command and a command after '--' cannot be combined"));
+                       warnx(_("option --command and '-- program' are mutually exclusive"));
                        errtryhelp(EXIT_FAILURE);
                }
 
-               ctl.command = ul_strv_join(argv, " ");
+               ctl.command = ul_strv_join(cmd_argv, " ");
                if (!ctl.command)
                        errx(EXIT_FAILURE, _("failed to concatenate arguments"));
 
                ctl.command_norm = xstrdup(ctl.command);
                ul_strrep(ctl.command_norm, '\n', ' ');
-               argc = 0;
        }
 
        if (argc > 0) {