From: Karel Zak Date: Tue, 7 Apr 2026 13:20:04 +0000 (+0200) Subject: script: fix backward compatibility for options after non-option args X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=70507ab9eaed10b8dd77b77d4ea25c11ee726bed;p=thirdparty%2Futil-linux.git script: fix backward compatibility for options after non-option args 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 Signed-off-by: Karel Zak --- diff --git a/term-utils/script.c b/term-utils/script.c index 037e2fcab..0a1773e9e 100644 --- a/term-utils/script.c +++ b/term-utils/script.c @@ -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 name as well as with a . The - * is always before "--" and is always after "--". + * The "--" separator splits non-option arguments into + * (before "--") and (after "--"). The "--" and command + * arguments have been already separated by the pre-scan above. + * * Possible combinations are: * * script [options] @@ -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) {