hooks: first operand after -- is still the sed script when no script option was given

Greptile P1: sedScriptParts stopped at -- and discarded the remaining
operands, so 'sed -- <script>' skipped script collection entirely. GNU sed
4.9 (verified): with no -e/--expression anywhere, the first operand after --
is the script and an s///e in it still executes.

The operands after -- are now collected as positionals, so the existing
first-positional-becomes-script rule applies. Operands after the script stay
file names (data). 2 regression tests pin both sides. Suite: 812/812.
This commit is contained in:
chenhz01
2026-10-11 03:55:27 +08:00
parent 95964ee623
commit 3af000e143
2 changed files with 12 additions and 1 deletions
+8 -1
View File
@@ -649,7 +649,14 @@ function sedScriptParts(words, budget) {
while (i < words.length) {
const raw = words[i].value;
budget.spend(raw.length + 1);
if (raw === '--') break; // remaining operands are files (data)
if (raw === '--') {
// Options end here, but the operands do not: with no script option
// given, the first operand after -- is still the script (GNU sed 4.9
// verified). Keep collecting them.
i++;
while (i < words.length) { positionals.push(words[i].value); budget.spend(words[i].value.length + 1); i++; }
break;
}
if (raw === '--expression') {
if (!words[i + 1]) return null;
scripts.push(words[i + 1].value); i += 2; continue;
+4
View File
@@ -2172,6 +2172,10 @@ const sedCases = [
['later -e after a positional script operand is scanned', "sed 's/x/y/' -e 'e git commit --no-verify'", 2],
['later --expression= after positionals is scanned', "sed 's/x/y/' --expression='s/x/git commit --no-verify/e'", 2],
['-- ends options: later tokens are file operands', "sed -e 's/x/y/' -- 'git commit --no-verify'", 0],
// With no script option before --, the first operand after -- is still the
// script (Greptile P1 on this PR, GNU sed 4.9 verified).
['first operand after -- is still the script', "printf x | sed -- 's/x/git commit --no-verify/e'", 2],
['file operands after the -- script stay data', "sed -- 's/x/y/' 'git commit --no-verify'", 0],
// GNU sed decodes escapes in exec-bearing fragments before execution
// (Greptile P1 findings on this PR): decoding that surface faithfully is out
// of scope, so escaped fragments are conservatively blocked instead.