@poe-platform/safe-bash 0.1.749 → 0.1.751
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/safe-bash/chunks/{chunk-4GK4FAT6.js → chunk-637A2MLA.js} +8 -6
- package/dist/safe-bash/chunks/{chunk-MUWJPSGN.js → chunk-B7IBQSD4.js} +7 -5
- package/dist/safe-bash/chunks/{chunk-BQEH4DKJ.js → chunk-ILHNFAZE.js} +198 -45
- package/dist/safe-bash/chunks/{src-YBSQFQZO.js → src-XXKXVO7T.js} +2 -2
- package/dist/safe-bash/commands/basic.js +2 -2
- package/dist/safe-bash/commands/pr/index.browser.js +1 -1
- package/dist/safe-bash/commands/time-env/shared.d.ts +1 -0
- package/dist/safe-bash/commands/time-env/shared.js +6 -5
- package/dist/safe-bash/commands/time-env/sleep.js +6 -3
- package/dist/safe-bash/core.browser.js +206 -452
- package/dist/safe-bash/shell/conditional.js +1 -2
- package/dist/safe-bash/shell/pattern.js +6 -1
- package/dist/safe-bash/shell/runtime.d.ts +1 -7
- package/dist/safe-bash/shell/runtime.js +262 -635
- package/dist/safe-bash-command-ssconvert/index.js +197 -44
- package/package.json +2 -2
|
@@ -9439,7 +9439,8 @@ export class Runtime {
|
|
|
9439
9439
|
}
|
|
9440
9440
|
return !rem.associative && rem.references === 1;
|
|
9441
9441
|
}
|
|
9442
|
-
|
|
9442
|
+
// Unquoted patterns may need the asynchronous matcher after values change.
|
|
9443
|
+
return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || (e.right.parts.length > 0 && e.right.parts.every(p => p.quoted)));
|
|
9443
9444
|
}
|
|
9444
9445
|
if (e.kind === "not")
|
|
9445
9446
|
return checkCondNoFile(e.operand);
|
|
@@ -9795,10 +9796,19 @@ export class Runtime {
|
|
|
9795
9796
|
// Matching publishes BASH_REMATCH. A sibling may still require fallback,
|
|
9796
9797
|
// so only a standalone match can be attempted without replaying writes.
|
|
9797
9798
|
if (expr.operator === "=~")
|
|
9798
|
-
return depth === 0 && this.extractSimpleErePattern(expr.right, rawState) !== undefined;
|
|
9799
|
+
return !requireTotal && depth === 0 && this.extractSimpleErePattern(expr.right, rawState) !== undefined;
|
|
9799
9800
|
if (expr.operator === "==" || expr.operator === "=" || expr.operator === "!=") {
|
|
9800
9801
|
if (expr.right.parts.length > 0 && expr.right.parts.every(p => p.quoted))
|
|
9801
9802
|
return true;
|
|
9803
|
+
if (requireTotal) {
|
|
9804
|
+
// Variable patterns can change during the loop. Only admit a static
|
|
9805
|
+
// pattern whose synchronous matcher is guaranteed to return a result.
|
|
9806
|
+
if (!expr.right.parts.every((p, idx) => p.kind === "text" && !p.byteValue && (p.quoted || idx > 0 || !p.value.startsWith("~"))))
|
|
9807
|
+
return false;
|
|
9808
|
+
const pattern = expr.right.parts.map(p => p.kind === "text" ? (p.quoted ? p.value.replace(/[\\*?[\]()]/g, "\\$&") : p.value) : "").join("");
|
|
9809
|
+
const work = { remaining: 4096, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
|
|
9810
|
+
return tryMatchesPatternSync(pattern, "", work, false, true) !== undefined;
|
|
9811
|
+
}
|
|
9802
9812
|
if (expr.right.parts.length > 0 && expr.right.parts.every((p, idx) => (p.kind === "text" && !p.byteValue && (p.quoted || idx > 0 || !p.value.startsWith("~"))) || (p.kind === "variable" && !p.indirect && !p.prefixNames && !p.length && !p.substring && !p.transform && p.operator === undefined && getArraySelector(p) === undefined)))
|
|
9803
9813
|
return true;
|
|
9804
9814
|
return false;
|
|
@@ -9987,7 +9997,10 @@ export class Runtime {
|
|
|
9987
9997
|
return undefined;
|
|
9988
9998
|
const syncTouched = this._syncArithRawWriteOnly ? this._syncArithTouched : undefined;
|
|
9989
9999
|
const lTrim = left.trim() || "0";
|
|
9990
|
-
|
|
10000
|
+
// Preserve all signed 64-bit literal digits instead of rounding through Number.
|
|
10001
|
+
let lNum = lTrim.length > 13 && /^-?[0-9]{1,19}$/.test(lTrim)
|
|
10002
|
+
? BigInt.asIntN(64, BigInt(lTrim))
|
|
10003
|
+
: resolveSimpleArithOperand(lTrim, rawState, monitor, store, syncTouched);
|
|
9991
10004
|
if (lNum === undefined) {
|
|
9992
10005
|
const lExpr = tryEvalSimpleExpandedArith(lTrim, rawState, monitor, store, syncTouched);
|
|
9993
10006
|
if (lExpr !== undefined)
|
|
@@ -9996,7 +10009,9 @@ export class Runtime {
|
|
|
9996
10009
|
if (lNum === undefined)
|
|
9997
10010
|
return undefined;
|
|
9998
10011
|
const rTrim = right.trim() || "0";
|
|
9999
|
-
let rNum =
|
|
10012
|
+
let rNum = rTrim.length > 13 && /^-?[0-9]{1,19}$/.test(rTrim)
|
|
10013
|
+
? BigInt.asIntN(64, BigInt(rTrim))
|
|
10014
|
+
: resolveSimpleArithOperand(rTrim, rawState, monitor, store, syncTouched);
|
|
10000
10015
|
if (rNum === undefined) {
|
|
10001
10016
|
const rExpr = tryEvalSimpleExpandedArith(rTrim, rawState, monitor, store, syncTouched);
|
|
10002
10017
|
if (rExpr !== undefined)
|
|
@@ -10004,6 +10019,10 @@ export class Runtime {
|
|
|
10004
10019
|
}
|
|
10005
10020
|
if (rNum === undefined)
|
|
10006
10021
|
return undefined;
|
|
10022
|
+
if (typeof lNum !== typeof rNum) {
|
|
10023
|
+
lNum = BigInt(lNum);
|
|
10024
|
+
rNum = BigInt(rNum);
|
|
10025
|
+
}
|
|
10007
10026
|
let ok;
|
|
10008
10027
|
switch (expr.operator) {
|
|
10009
10028
|
case "-eq":
|
|
@@ -13025,7 +13044,6 @@ export class Runtime {
|
|
|
13025
13044
|
rawState.substitutionStatus = 0;
|
|
13026
13045
|
if (rawState.variables._ !== undefined)
|
|
13027
13046
|
delete rawState.variables._;
|
|
13028
|
-
rawState.lastArgument = fnArgs.length > 0 ? fnArgs[fnArgs.length - 1] : w0Plain;
|
|
13029
13047
|
const positional = rawState.positional;
|
|
13030
13048
|
let fnArgsBytes = 0;
|
|
13031
13049
|
for (let i = 0; i < fnArgs.length; i++)
|
|
@@ -13075,6 +13093,7 @@ export class Runtime {
|
|
|
13075
13093
|
positionalSetVersion,
|
|
13076
13094
|
savedPositionals: resumeSavedPos,
|
|
13077
13095
|
callerLoopDepth,
|
|
13096
|
+
lastArgument: fnArgs.at(-1) ?? w0Plain,
|
|
13078
13097
|
listIndex: bodyRes.listIndex,
|
|
13079
13098
|
pipelineIndex: bodyRes.pipelineIndex,
|
|
13080
13099
|
});
|
|
@@ -13139,6 +13158,7 @@ export class Runtime {
|
|
|
13139
13158
|
this.setSyncPipeStatusCell(activeExisting, statusStr);
|
|
13140
13159
|
activeStore.changed(monitor.chargeInternal(syncPipeStatusCharge, syncPipeStatusTickets), "PIPESTATUS");
|
|
13141
13160
|
}
|
|
13161
|
+
rawState.lastArgument = fnArgs.at(-1) ?? w0Plain;
|
|
13142
13162
|
rawState.status = exitStatus;
|
|
13143
13163
|
if (exitStatus !== 0 && !ignored && rawState.errexit)
|
|
13144
13164
|
throw new Flow("exit", exitStatus);
|
|
@@ -13237,7 +13257,6 @@ export class Runtime {
|
|
|
13237
13257
|
rawState.substitutionStatus = 0;
|
|
13238
13258
|
if (rawState.variables._ !== undefined)
|
|
13239
13259
|
delete rawState.variables._;
|
|
13240
|
-
rawState.lastArgument = lastCmdArg;
|
|
13241
13260
|
rawState.depth++;
|
|
13242
13261
|
let evalStatus;
|
|
13243
13262
|
try {
|
|
@@ -13247,6 +13266,7 @@ export class Runtime {
|
|
|
13247
13266
|
rawState.depth--;
|
|
13248
13267
|
}
|
|
13249
13268
|
if (typeof evalStatus === "number") {
|
|
13269
|
+
rawState.lastArgument = lastCmdArg;
|
|
13250
13270
|
if (cachedUnit.script.lists.length === 0) {
|
|
13251
13271
|
const activeStore = monitor.store;
|
|
13252
13272
|
const activeExisting = activeStore?.get("PIPESTATUS");
|
|
@@ -13586,6 +13606,11 @@ export class Runtime {
|
|
|
13586
13606
|
continue;
|
|
13587
13607
|
}
|
|
13588
13608
|
if (part.kind === "variable") {
|
|
13609
|
+
// Unsubscripted array references expand element zero through the normal
|
|
13610
|
+
// evaluator. fastValueWord cannot guarantee that expansion throughout
|
|
13611
|
+
// a loop, so reject it before executing any loop effects.
|
|
13612
|
+
if (stateMonitor(rawState)?.store?.get(part.name))
|
|
13613
|
+
return false;
|
|
13589
13614
|
// These expansions can require multiple fields or asynchronous quoting
|
|
13590
13615
|
// as loop state changes. Decide before the loop produces any effects.
|
|
13591
13616
|
if (this._syncLoopFnCheckDepth > 0 && (part.substring || getArraySelector(part)?.kind === "element"))
|
|
@@ -13736,25 +13761,12 @@ export class Runtime {
|
|
|
13736
13761
|
if (list.terminator || list.pipelines.length !== 1)
|
|
13737
13762
|
return false;
|
|
13738
13763
|
const p = list.pipelines[0];
|
|
13739
|
-
|
|
13764
|
+
// Pipeline buffers can overflow after variable or stage expansion.
|
|
13765
|
+
// A synchronous loop cannot resume after earlier body effects, so keep
|
|
13766
|
+
// bounded pipelines on the normal substitution execution path.
|
|
13767
|
+
if (p.negate || p.commands.length !== 1)
|
|
13740
13768
|
return false;
|
|
13741
13769
|
let cmd = p.commands[0];
|
|
13742
|
-
const hasSingleStdinRedir = cmd.kind === "simple" &&
|
|
13743
|
-
cmd.redirects.length === 1 &&
|
|
13744
|
-
cmd.redirects[0].operator === "<" &&
|
|
13745
|
-
(cmd.redirects[0].descriptor === undefined || cmd.redirects[0].descriptor === 0) &&
|
|
13746
|
-
!cmd.redirects[0].move &&
|
|
13747
|
-
!cmd.redirects[0].document &&
|
|
13748
|
-
cmd.redirects[0].target.plain !== undefined &&
|
|
13749
|
-
!cmd.redirects[0].target.plain.startsWith("-") &&
|
|
13750
|
-
cmd.redirects[0].target.plain !== "/dev/stdin";
|
|
13751
|
-
const hasSingleHereStringRedir = cmd.kind === "simple" &&
|
|
13752
|
-
cmd.redirects.length === 1 &&
|
|
13753
|
-
cmd.redirects[0].operator === "<<<" &&
|
|
13754
|
-
(cmd.redirects[0].descriptor === undefined || cmd.redirects[0].descriptor === 0) &&
|
|
13755
|
-
!cmd.redirects[0].move &&
|
|
13756
|
-
!cmd.redirects[0].document &&
|
|
13757
|
-
this.isPureSyncValueWord(cmd.redirects[0].target, rawState);
|
|
13758
13770
|
// Loop substitutions require a real subshell: header admission can
|
|
13759
13771
|
// decline at runtime, and induction writes also change export attributes.
|
|
13760
13772
|
if (cmd.kind === "for" || cmd.kind === "arithmetic-for" || cmd.kind === "while")
|
|
@@ -13762,7 +13774,7 @@ export class Runtime {
|
|
|
13762
13774
|
if (p.commands.length === 1 && (cmd.kind === "if" || cmd.kind === "case") && this.isPureSyncSubIfOrCase(cmd, rawState)) {
|
|
13763
13775
|
continue;
|
|
13764
13776
|
}
|
|
13765
|
-
if (cmd.kind !== "simple" ||
|
|
13777
|
+
if (cmd.kind !== "simple" || cmd.redirects.length > 0 || cmd.words.length === 0)
|
|
13766
13778
|
return false;
|
|
13767
13779
|
let w0Plain = cmd.words[0].plain;
|
|
13768
13780
|
// Range-aware tr and Buffer-free 76-col base64 are handled below.
|
|
@@ -13777,10 +13789,10 @@ export class Runtime {
|
|
|
13777
13789
|
fnBody.body.lists[0].pipelines[0].commands.length === 1 &&
|
|
13778
13790
|
fnBody.body.lists[0].pipelines[0].commands[0].kind === "simple" &&
|
|
13779
13791
|
fnBody.body.lists[0].pipelines[0].commands[0].redirects.length === 0 &&
|
|
13780
|
-
cmd.words.slice(1).every(w => this.isPureSyncValueWord(w, rawState))) {
|
|
13792
|
+
cmd.words.slice(1).every(w => this.isPureSyncValueWord(w, rawState) && w.parts.length > 0 && w.parts.every(part => part.quoted || (part.kind === "text" && part.value.length > 0 && !part.value.includes(" ") && !part.value.includes("\t") && !part.value.includes("\n") && !part.value.includes("{") && !hasGlobOrEscape(part.value, true))))) {
|
|
13781
13793
|
const innerCmd = fnBody.body.lists[0].pipelines[0].commands[0];
|
|
13782
13794
|
const innerW0 = innerCmd.words[0]?.plain;
|
|
13783
|
-
if (innerW0 && !rawState.functions.has(innerW0) && !rawState.extensions?.builtins.has(innerW0)) {
|
|
13795
|
+
if (innerW0 && (innerW0 === "echo" || innerW0 === "printf" || innerW0 === "dirname" || innerW0 === "basename" || innerW0 === "pwd" || innerW0 === "command" || innerW0 === "type" || innerW0 === "seq") && !rawState.functions.has(innerW0) && !rawState.extensions?.builtins.has(innerW0)) {
|
|
13784
13796
|
cmd = innerCmd;
|
|
13785
13797
|
w0Plain = innerW0;
|
|
13786
13798
|
}
|
|
@@ -13796,143 +13808,23 @@ export class Runtime {
|
|
|
13796
13808
|
return false;
|
|
13797
13809
|
}
|
|
13798
13810
|
if ((w0Plain === "command" && cmd.words[1]?.plain === "-v") || (w0Plain === "type" && cmd.words[1]?.plain === "-t")) {
|
|
13799
|
-
|
|
13800
|
-
cmd.words.length === 3 &&
|
|
13801
|
-
!rawState.functions.has(w0Plain) &&
|
|
13802
|
-
!rawState.extensions?.builtins.has(w0Plain) &&
|
|
13803
|
-
this.isPureSyncValueWord(cmd.words[2], rawState) &&
|
|
13804
|
-
((cmd.words[2].plain !== undefined && this.tryResolveSyncDiscovery(w0Plain === "command" ? "name" : "kind", cmd.words[2].plain, rawState) !== undefined) ||
|
|
13805
|
-
(this._isMemoryBackingFs && this.backingFs?.capabilities?.permissions !== true))) {
|
|
13806
|
-
continue;
|
|
13807
|
-
}
|
|
13811
|
+
// PATH entries can change after admission, making discovery decline.
|
|
13808
13812
|
return false;
|
|
13809
13813
|
}
|
|
13810
|
-
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
w0Plain === "awk" ||
|
|
13815
|
-
w0Plain === "grep" ||
|
|
13816
|
-
w0Plain === "sed" ||
|
|
13817
|
-
w0Plain === "cut" ||
|
|
13818
|
-
w0Plain === "wc" ||
|
|
13819
|
-
w0Plain === "sort" ||
|
|
13820
|
-
w0Plain === "uniq" ||
|
|
13821
|
-
w0Plain === "tr" ||
|
|
13822
|
-
(hasSingleHereStringRedir && (w0Plain === "base64" || w0Plain === "rev" || w0Plain === "tac" || w0Plain === "nl" || w0Plain === "paste" || w0Plain === "numfmt"));
|
|
13823
|
-
const def = w0Plain ? (this.commands.get(w0Plain) ?? ((w0Plain === "rev" || w0Plain === "tac" || w0Plain === "nl" || w0Plain === "paste" || w0Plain === "jq") ? this.getExternalCommand(w0Plain) : undefined)) : undefined;
|
|
13824
|
-
if (!w0Plain || (w0Plain !== "printf" && w0Plain !== "echo" && w0Plain !== "dirname" && w0Plain !== "basename" && w0Plain !== "seq" && !isFileCommand) || !def || rawState.functions.has(w0Plain) || rawState.extensions?.builtins.has(w0Plain))
|
|
13814
|
+
// File and here-string evaluators can decline on mutable input. Keep
|
|
13815
|
+
// these on the normal path, which can handle arbitrary input sizes.
|
|
13816
|
+
const def = w0Plain ? this.commands.get(w0Plain) : undefined;
|
|
13817
|
+
if (!w0Plain || (w0Plain !== "printf" && w0Plain !== "echo" && w0Plain !== "dirname" && w0Plain !== "basename" && w0Plain !== "seq") || !def || rawState.functions.has(w0Plain) || rawState.extensions?.builtins.has(w0Plain))
|
|
13825
13818
|
return false;
|
|
13826
|
-
if ((
|
|
13827
|
-
return false;
|
|
13828
|
-
if (w0Plain === "printf" ? def.execute !== printfCommand.execute : w0Plain === "echo" ? !defaultEchoExecutors.has(def.execute) : (w0Plain === "seq" || w0Plain === "rev" || w0Plain === "tac" || w0Plain === "nl" || w0Plain === "paste" || w0Plain === "jq") ? (!builtInDirectContextExecutors.has(def.execute) && customRegisteredCommands.has(def.execute)) : !builtInDirectContextExecutors.has(def.execute))
|
|
13819
|
+
if (w0Plain === "printf" ? def.execute !== printfCommand.execute : w0Plain === "echo" ? !defaultEchoExecutors.has(def.execute) : w0Plain === "seq" ? (!builtInDirectContextExecutors.has(def.execute) && customRegisteredCommands.has(def.execute)) : !builtInDirectContextExecutors.has(def.execute))
|
|
13829
13820
|
return false;
|
|
13830
13821
|
if (!cmd.words.slice(1).every(w => this.isPureSyncValueWord(w, rawState)))
|
|
13831
13822
|
return false;
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
13836
|
-
|
|
13837
|
-
if (!hasSingleHereStringRedir) {
|
|
13838
|
-
const fPlain = hasSingleStdinRedir ? cmd.redirects[0].target.plain : cmd.words[cmd.words.length - 1]?.plain;
|
|
13839
|
-
if (!fPlain || fPlain.startsWith("-") || fPlain === "/dev/stdin")
|
|
13840
|
-
return false;
|
|
13841
|
-
const vCheck = this.tryReadMemoryFileViewSync(resolvePath(rawState.cwd, fPlain), false);
|
|
13842
|
-
if (!vCheck || vCheck.byteLength > 16384 || vCheck.includes(0))
|
|
13843
|
-
return false;
|
|
13844
|
-
}
|
|
13845
|
-
const opWords = (hasSingleStdinRedir || hasSingleHereStringRedir) ? cmd.words.slice(1) : cmd.words.slice(1, -1);
|
|
13846
|
-
const opCount = opWords.length;
|
|
13847
|
-
const op0 = opWords[0]?.plain;
|
|
13848
|
-
const op1 = opWords[1]?.plain;
|
|
13849
|
-
if (w0Plain === "cat" || w0Plain === "rev" || w0Plain === "tac") {
|
|
13850
|
-
if (opCount !== 0)
|
|
13851
|
-
return false;
|
|
13852
|
-
}
|
|
13853
|
-
else {
|
|
13854
|
-
if (p.commands.length !== 1 && !hasSingleHereStringRedir)
|
|
13855
|
-
return false;
|
|
13856
|
-
if (w0Plain === "head" || w0Plain === "tail") {
|
|
13857
|
-
const validHT = opCount === 0 ||
|
|
13858
|
-
(opCount === 1 && op0 !== undefined && (/^-(?:n|c|--lines=|--bytes=)?[0-9]{1,5}$/.test(op0) || /^--(?:lines|bytes)=[0-9]{1,5}$/.test(op0) || (w0Plain === "tail" && /^(?:-n|--lines=)\+[0-9]{1,5}$/.test(op0)) || (w0Plain === "head" && /^(?:-n|--lines=)-[0-9]{1,5}$/.test(op0)))) ||
|
|
13859
|
-
(opCount === 2 && ((op0 === "-n" && op1 !== undefined && (/^[0-9]{1,5}$/.test(op1) || (w0Plain === "tail" && /^\+[0-9]{1,5}$/.test(op1)) || (w0Plain === "head" && /^-[0-9]{1,5}$/.test(op1)))) || (op0 === "-c" && op1 !== undefined && /^[0-9]{1,5}$/.test(op1))));
|
|
13860
|
-
if (!validHT)
|
|
13861
|
-
return false;
|
|
13862
|
-
}
|
|
13863
|
-
const plainOps = [];
|
|
13864
|
-
for (let oi = 0; oi < opCount; oi++) {
|
|
13865
|
-
const ow = opWords[oi];
|
|
13866
|
-
const pw = ow.plain ?? (ow.parts.length > 0 && ow.parts.every(pt => pt.kind === "text") ? ow.parts.map(pt => pt.value).join("") : undefined);
|
|
13867
|
-
if (pw === undefined)
|
|
13868
|
-
break;
|
|
13869
|
-
plainOps.push(pw);
|
|
13870
|
-
}
|
|
13871
|
-
if (w0Plain === "cut") {
|
|
13872
|
-
// Probe non-ASCII input: loop data can change after admission.
|
|
13873
|
-
if (plainOps.length !== opCount || this.evalSyncCut(["é"], plainOps, byteLocale(rawState.variables)) === undefined)
|
|
13874
|
-
return false;
|
|
13875
|
-
}
|
|
13876
|
-
else if (w0Plain === "sed") {
|
|
13877
|
-
if (plainOps.length !== opCount || this.evalSyncSed([], plainOps) === undefined)
|
|
13878
|
-
return false;
|
|
13879
|
-
}
|
|
13880
|
-
else if (w0Plain === "grep") {
|
|
13881
|
-
if (plainOps.length !== opCount || this.evalSyncGrep([], plainOps, Boolean(rawState.errexit)) === undefined)
|
|
13882
|
-
return false;
|
|
13883
|
-
}
|
|
13884
|
-
else if (w0Plain === "awk") {
|
|
13885
|
-
const input = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
|
|
13886
|
-
if (plainOps.length !== opCount || input === undefined)
|
|
13887
|
-
return false;
|
|
13888
|
-
const lines = input.length === 0 ? [] : input.split("\n");
|
|
13889
|
-
if (input.endsWith("\n"))
|
|
13890
|
-
lines.pop();
|
|
13891
|
-
if (this.evalSyncAwk(lines, plainOps) === undefined)
|
|
13892
|
-
return false;
|
|
13893
|
-
}
|
|
13894
|
-
else if (w0Plain === "jq") {
|
|
13895
|
-
if (plainOps.length !== opCount)
|
|
13896
|
-
return false;
|
|
13897
|
-
const jqIn = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
|
|
13898
|
-
if (jqIn === undefined || this.evalSyncJq(jqIn, plainOps) === undefined)
|
|
13899
|
-
return false;
|
|
13900
|
-
}
|
|
13901
|
-
else if (w0Plain === "wc") {
|
|
13902
|
-
if (opCount !== 1 || this.normalizeSyncWcFlag(op0) === undefined)
|
|
13903
|
-
return false;
|
|
13904
|
-
}
|
|
13905
|
-
else if (w0Plain === "sort") {
|
|
13906
|
-
if (plainOps.length !== opCount || this.evalSyncSort([], plainOps, byteLocale(rawState.variables)) === undefined)
|
|
13907
|
-
return false;
|
|
13908
|
-
}
|
|
13909
|
-
else if (w0Plain === "uniq") {
|
|
13910
|
-
if (plainOps.length !== opCount || this.evalSyncUniq([], plainOps) === undefined)
|
|
13911
|
-
return false;
|
|
13912
|
-
}
|
|
13913
|
-
else if (w0Plain === "base64") {
|
|
13914
|
-
if (opCount > 1 || (opCount === 1 && op0 !== "-d" && op0 !== "--decode"))
|
|
13915
|
-
return false;
|
|
13916
|
-
}
|
|
13917
|
-
else if (w0Plain === "tr") {
|
|
13918
|
-
if (plainOps.length !== opCount || this.evalSyncTr("", plainOps) === undefined)
|
|
13919
|
-
return false;
|
|
13920
|
-
}
|
|
13921
|
-
else if (w0Plain === "nl") {
|
|
13922
|
-
if (plainOps.length !== opCount || this.evalSyncNl([], plainOps) === undefined)
|
|
13923
|
-
return false;
|
|
13924
|
-
}
|
|
13925
|
-
else if (w0Plain === "paste") {
|
|
13926
|
-
if (plainOps.length !== opCount || this.evalSyncPaste([], plainOps) === undefined)
|
|
13927
|
-
return false;
|
|
13928
|
-
}
|
|
13929
|
-
else if (w0Plain === "numfmt") {
|
|
13930
|
-
const nmCheck = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
|
|
13931
|
-
if (nmCheck === undefined || plainOps.length !== opCount || this.evalSyncNumfmt(nmCheck.split("\n"), plainOps) === undefined)
|
|
13932
|
-
return false;
|
|
13933
|
-
}
|
|
13934
|
-
}
|
|
13935
|
-
}
|
|
13823
|
+
// These substitutions consume one value per word. Unquoted expansions
|
|
13824
|
+
// can split, glob, or disappear, including after loop admission.
|
|
13825
|
+
if ((w0Plain === "echo" || w0Plain === "basename" || w0Plain === "dirname") &&
|
|
13826
|
+
!cmd.words.slice(1).every(w => w.parts.length > 0 && w.parts.every(part => part.quoted || (part.kind === "text" && part.value.length > 0 && !part.value.includes(" ") && !part.value.includes("\t") && !part.value.includes("\n") && !part.value.includes("{") && !hasGlobOrEscape(part.value, true)))))
|
|
13827
|
+
return false;
|
|
13936
13828
|
if (w0Plain === "printf") {
|
|
13937
13829
|
if (!this.isSyncPrintfCallOk(cmd, 1, rawState, this._activePrintfInductionName))
|
|
13938
13830
|
return false;
|
|
@@ -13972,138 +13864,18 @@ export class Runtime {
|
|
|
13972
13864
|
return false;
|
|
13973
13865
|
}
|
|
13974
13866
|
else if (w0Plain === "seq") {
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
if (w.parts.length === 1) {
|
|
13979
|
-
const p0 = w.parts[0];
|
|
13980
|
-
if (p0.kind === "arithmetic")
|
|
13981
|
-
return true;
|
|
13982
|
-
if (p0.kind === "variable" && !p0.indirect && !p0.prefixNames && !p0.substring && !p0.transform && p0.operator === undefined && getArraySelector(p0) === undefined) {
|
|
13983
|
-
const cur = rawState.variables[p0.name];
|
|
13984
|
-
return cur !== undefined && /^-?[0-9]{1,7}$/.test(cur);
|
|
13985
|
-
}
|
|
13986
|
-
}
|
|
13867
|
+
// Literal operands cannot change after admission. Arithmetic and
|
|
13868
|
+
// variables use the normal path, where seq can report invalid input.
|
|
13869
|
+
if (cmd.words.length < 2 || cmd.words.length > 4)
|
|
13987
13870
|
return false;
|
|
13988
|
-
|
|
13989
|
-
if (
|
|
13871
|
+
const operands = cmd.words.slice(1).map(w => w.plain);
|
|
13872
|
+
if (!operands.every(value => value !== undefined && Number.isInteger(Number(value)) && String(Number(value)) === value && Math.abs(Number(value)) <= 9999999))
|
|
13990
13873
|
return false;
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13874
|
+
const first = operands.length === 1 ? 1 : Number(operands[0]);
|
|
13875
|
+
const increment = operands.length === 3 ? Number(operands[1]) : 1;
|
|
13876
|
+
const last = Number(operands[operands.length - 1]);
|
|
13877
|
+
if (increment === 0 || Math.abs((last - first) / increment) > 1024)
|
|
13994
13878
|
return false;
|
|
13995
|
-
if (w0Plain === "printf") {
|
|
13996
|
-
if (!this.isSyncPrintfCallOk(cmd, 1, rawState, this._activePrintfInductionName))
|
|
13997
|
-
return false;
|
|
13998
|
-
}
|
|
13999
|
-
for (let sIdx = 1; sIdx < p.commands.length; sIdx++) {
|
|
14000
|
-
const sCmd = p.commands[sIdx];
|
|
14001
|
-
if (sCmd.kind !== "simple" || sCmd.redirects.length !== 0 || sCmd.words.length === 0)
|
|
14002
|
-
return false;
|
|
14003
|
-
const sName = sCmd.words[0].plain;
|
|
14004
|
-
// Range-aware tr and Buffer-free 76-col base64 are handled in pipeline stages.
|
|
14005
|
-
if (!sName || (sName !== "cut" && sName !== "tr" && sName !== "sort" && sName !== "head" && sName !== "tail" && sName !== "wc" && sName !== "sed" && sName !== "uniq" && sName !== "rev" && sName !== "awk" && sName !== "grep" && sName !== "jq" && sName !== "base64" && sName !== "tac" && sName !== "nl" && sName !== "paste" && sName !== "numfmt"))
|
|
14006
|
-
return false;
|
|
14007
|
-
const sDef = this.commands.get(sName) ?? ((sName === "rev" || sName === "tac" || sName === "nl" || sName === "paste" || sName === "jq") ? this.getExternalCommand(sName) : undefined);
|
|
14008
|
-
if (!sDef || customRegisteredCommands.has(sDef.execute) || (sName !== "rev" && sName !== "tac" && sName !== "nl" && sName !== "paste" && sName !== "jq" && !builtInDirectContextExecutors.has(sDef.execute)))
|
|
14009
|
-
return false;
|
|
14010
|
-
if (rawState.functions.has(sName) || rawState.extensions?.builtins.has(sName))
|
|
14011
|
-
return false;
|
|
14012
|
-
const sArgWords = sCmd.words.slice(1);
|
|
14013
|
-
const sPlainArgs = [];
|
|
14014
|
-
let allPlainStageArgs = true;
|
|
14015
|
-
for (let k = 0; k < sArgWords.length; k++) {
|
|
14016
|
-
const sw = sArgWords[k];
|
|
14017
|
-
const sp = sw.plain ?? (sw.parts.length > 0 && sw.parts.every(pt => pt.kind === "text") ? sw.parts.map(pt => pt.value).join("") : undefined);
|
|
14018
|
-
if (sp === undefined) {
|
|
14019
|
-
allPlainStageArgs = false;
|
|
14020
|
-
break;
|
|
14021
|
-
}
|
|
14022
|
-
sPlainArgs.push(sp);
|
|
14023
|
-
}
|
|
14024
|
-
if (!allPlainStageArgs)
|
|
14025
|
-
return false;
|
|
14026
|
-
if (sName === "sort") {
|
|
14027
|
-
if (this.evalSyncSort([], sPlainArgs, byteLocale(rawState.variables)) === undefined)
|
|
14028
|
-
return false;
|
|
14029
|
-
}
|
|
14030
|
-
else if (sName === "uniq") {
|
|
14031
|
-
if (this.evalSyncUniq([], sPlainArgs) === undefined)
|
|
14032
|
-
return false;
|
|
14033
|
-
}
|
|
14034
|
-
else if (sName === "wc") {
|
|
14035
|
-
if (sPlainArgs.length !== 1 || this.normalizeSyncWcFlag(sPlainArgs[0]) === undefined)
|
|
14036
|
-
return false;
|
|
14037
|
-
}
|
|
14038
|
-
else if (sName === "head" || sName === "tail") {
|
|
14039
|
-
const okHT = sPlainArgs.length === 0 || (sPlainArgs.length === 1 && (/^-(?:n|c)?[0-9]{1,5}$/.test(sPlainArgs[0]) || /^--(?:lines|bytes)=[0-9]{1,5}$/.test(sPlainArgs[0]) || (sName === "tail" && /^(?:-n|--lines=)\+[0-9]{1,5}$/.test(sPlainArgs[0])) || (sName === "head" && /^(?:-n|--lines=)-[0-9]{1,5}$/.test(sPlainArgs[0])))) || (sPlainArgs.length === 2 && ((sPlainArgs[0] === "-n" && (/^[0-9]{1,5}$/.test(sPlainArgs[1]) || (sName === "tail" && /^\+[0-9]{1,5}$/.test(sPlainArgs[1])) || (sName === "head" && /^-[0-9]{1,5}$/.test(sPlainArgs[1])))) || (sPlainArgs[0] === "-c" && /^[0-9]{1,5}$/.test(sPlainArgs[1]))));
|
|
14040
|
-
if (!okHT)
|
|
14041
|
-
return false;
|
|
14042
|
-
}
|
|
14043
|
-
else if (sName === "cut") {
|
|
14044
|
-
// Byte cuts may reject non-ASCII data on a later iteration.
|
|
14045
|
-
if (this.evalSyncCut(["é"], sPlainArgs, byteLocale(rawState.variables)) === undefined)
|
|
14046
|
-
return false;
|
|
14047
|
-
}
|
|
14048
|
-
else if (sName === "sed") {
|
|
14049
|
-
if (this.evalSyncSed([], sPlainArgs) === undefined)
|
|
14050
|
-
return false;
|
|
14051
|
-
}
|
|
14052
|
-
else if (sName === "awk") {
|
|
14053
|
-
if (sIdx !== 1)
|
|
14054
|
-
return false;
|
|
14055
|
-
const input = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
|
|
14056
|
-
if (input === undefined)
|
|
14057
|
-
return false;
|
|
14058
|
-
const lines = input.length === 0 ? [] : input.split("\n");
|
|
14059
|
-
if (input.endsWith("\n"))
|
|
14060
|
-
lines.pop();
|
|
14061
|
-
if (this.evalSyncAwk(lines, sPlainArgs) === undefined)
|
|
14062
|
-
return false;
|
|
14063
|
-
}
|
|
14064
|
-
else if (sName === "grep") {
|
|
14065
|
-
if (this.evalSyncGrep([], sPlainArgs, Boolean(rawState.errexit)) === undefined)
|
|
14066
|
-
return false;
|
|
14067
|
-
}
|
|
14068
|
-
else if (sName === "tr") {
|
|
14069
|
-
if (this.evalSyncTr("", sPlainArgs) === undefined)
|
|
14070
|
-
return false;
|
|
14071
|
-
}
|
|
14072
|
-
else if (sName === "jq") {
|
|
14073
|
-
if (sIdx !== 1)
|
|
14074
|
-
return false;
|
|
14075
|
-
const jqIn = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
|
|
14076
|
-
if (jqIn === undefined || this.evalSyncJq(jqIn, sPlainArgs) === undefined)
|
|
14077
|
-
return false;
|
|
14078
|
-
}
|
|
14079
|
-
else if (sName === "base64") {
|
|
14080
|
-
if (sPlainArgs.length !== 0)
|
|
14081
|
-
return false;
|
|
14082
|
-
}
|
|
14083
|
-
else if (sName === "tac") {
|
|
14084
|
-
if (sPlainArgs.length !== 0 || (w0Plain !== "echo" && !hasSingleHereStringRedir))
|
|
14085
|
-
return false;
|
|
14086
|
-
}
|
|
14087
|
-
else if (sName === "nl") {
|
|
14088
|
-
if ((w0Plain !== "echo" && !hasSingleHereStringRedir) || this.evalSyncNl([], sPlainArgs) === undefined)
|
|
14089
|
-
return false;
|
|
14090
|
-
}
|
|
14091
|
-
else if (sName === "paste") {
|
|
14092
|
-
if (this.evalSyncPaste([], sPlainArgs) === undefined)
|
|
14093
|
-
return false;
|
|
14094
|
-
}
|
|
14095
|
-
else if (sName === "numfmt") {
|
|
14096
|
-
if (sIdx !== 1)
|
|
14097
|
-
return false;
|
|
14098
|
-
const nmCheck = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
|
|
14099
|
-
if (nmCheck === undefined || this.evalSyncNumfmt(nmCheck.split("\n"), sPlainArgs) === undefined)
|
|
14100
|
-
return false;
|
|
14101
|
-
}
|
|
14102
|
-
else if (sName === "rev") {
|
|
14103
|
-
if (sPlainArgs.length !== 0)
|
|
14104
|
-
return false;
|
|
14105
|
-
}
|
|
14106
|
-
}
|
|
14107
13879
|
}
|
|
14108
13880
|
continue;
|
|
14109
13881
|
}
|
|
@@ -14137,11 +13909,11 @@ export class Runtime {
|
|
|
14137
13909
|
if (isNumOp && checkIntVars) {
|
|
14138
13910
|
const isGuaranteedIntWord = (w) => {
|
|
14139
13911
|
if (w.plain !== undefined)
|
|
14140
|
-
return /^-?[0-9]
|
|
13912
|
+
return /^-?[0-9]{1,16}$/.test(w.plain);
|
|
14141
13913
|
if (w.parts.length === 1) {
|
|
14142
13914
|
const p0 = w.parts[0];
|
|
14143
13915
|
if (p0.kind === "arithmetic")
|
|
14144
|
-
return
|
|
13916
|
+
return false;
|
|
14145
13917
|
if (p0.kind === "variable" && !p0.indirect && !p0.prefixNames && !p0.substring && !p0.transform && p0.operator === undefined && getArraySelector(p0) === undefined) {
|
|
14146
13918
|
if (script) {
|
|
14147
13919
|
for (const lst of script.lists) {
|
|
@@ -14149,14 +13921,14 @@ export class Runtime {
|
|
|
14149
13921
|
const c0 = pl.commands[0];
|
|
14150
13922
|
if (c0?.kind === "simple" && c0.words.length === 1) {
|
|
14151
13923
|
const asg = this.assignment(c0.words[0]);
|
|
14152
|
-
if (asg?.name === p0.name && (!asg.value.plain || !/^-?[0-9]
|
|
13924
|
+
if (asg?.name === p0.name && (!asg.value.plain || !/^-?[0-9]{1,16}$/.test(asg.value.plain)))
|
|
14153
13925
|
return false;
|
|
14154
13926
|
}
|
|
14155
13927
|
}
|
|
14156
13928
|
}
|
|
14157
13929
|
}
|
|
14158
13930
|
const cur = rawState.variables[p0.name];
|
|
14159
|
-
return cur === undefined || /^-?[0-9]
|
|
13931
|
+
return cur === undefined || /^-?[0-9]{1,16}$/.test(cur);
|
|
14160
13932
|
}
|
|
14161
13933
|
}
|
|
14162
13934
|
return false;
|
|
@@ -14646,7 +14418,8 @@ export class Runtime {
|
|
|
14646
14418
|
return false;
|
|
14647
14419
|
return this.isPureSyncValueWord(e.left, rawState) && this.getFastAnchoredEreRegex(e.right, rawState) !== undefined && !rawState.readonlyVariables?.has("BASH_REMATCH") && !rawState.exported.has("BASH_REMATCH") && !stateMonitor(rawState)?.hasOverlay("BASH_REMATCH") && !(store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH");
|
|
14648
14420
|
}
|
|
14649
|
-
|
|
14421
|
+
// A synchronous loop cannot hand off an unsupported pattern mid-iteration.
|
|
14422
|
+
return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || (e.right.parts.length > 0 && e.right.parts.every(p => p.quoted)));
|
|
14650
14423
|
}
|
|
14651
14424
|
if (e.kind === "not")
|
|
14652
14425
|
return checkLeaf(e.operand);
|
|
@@ -14807,6 +14580,8 @@ export class Runtime {
|
|
|
14807
14580
|
// Nested step execution does not implement redirects; use normal execution.
|
|
14808
14581
|
if (nested && cmd.redirects.length !== 0)
|
|
14809
14582
|
return false;
|
|
14583
|
+
if (cmd.words.length === 0)
|
|
14584
|
+
return false;
|
|
14810
14585
|
if (cmd.redirects.length === 1) {
|
|
14811
14586
|
const r0Check = cmd.redirects[0];
|
|
14812
14587
|
const w0DevPlain = cmd.words[0].plain;
|
|
@@ -14824,11 +14599,16 @@ export class Runtime {
|
|
|
14824
14599
|
(w0DevPlain === "pwd" || this.commands.has(w0DevPlain))) {
|
|
14825
14600
|
const devWordOk = (w) => this.isPureSyncValueWord(w, rawState) &&
|
|
14826
14601
|
w.parts.every(p => p.quoted || (p.kind === "text" && p.value.length > 0 && !p.value.includes(" ") && !p.value.includes("\t") && !p.value.includes("\n") && !p.value.includes("{") && !hasGlobOrEscape(p.value, true)));
|
|
14602
|
+
// Only a nonempty literal prefix proves an expansion cannot become an option.
|
|
14603
|
+
const devOperandOk = (w) => {
|
|
14604
|
+
const first = w.parts[0];
|
|
14605
|
+
return first?.kind === "text" && first.value.length > 0 && !first.value.startsWith("-") && devWordOk(w);
|
|
14606
|
+
};
|
|
14827
14607
|
const devShapeOk = (w0DevPlain === "pwd" && cmd.words.length === 1) ||
|
|
14828
|
-
((w0DevPlain === "dirname" || w0DevPlain === "echo") && cmd.words.length === 2 &&
|
|
14829
|
-
(w0DevPlain === "basename" && ((cmd.words.length === 2 &&
|
|
14830
|
-
(cmd.words.length === 3 &&
|
|
14831
|
-
(cmd.words.length === 4 && cmd.words[1].plain === "-s" && cmd.words[2].plain !== undefined &&
|
|
14608
|
+
((w0DevPlain === "dirname" || w0DevPlain === "echo") && cmd.words.length === 2 && devOperandOk(cmd.words[1])) ||
|
|
14609
|
+
(w0DevPlain === "basename" && ((cmd.words.length === 2 && devOperandOk(cmd.words[1])) ||
|
|
14610
|
+
(cmd.words.length === 3 && devOperandOk(cmd.words[1]) && devWordOk(cmd.words[2])) ||
|
|
14611
|
+
(cmd.words.length === 4 && cmd.words[1].plain === "-s" && cmd.words[2].plain !== undefined && devOperandOk(cmd.words[3]))));
|
|
14832
14612
|
if (devShapeOk)
|
|
14833
14613
|
continue;
|
|
14834
14614
|
}
|
|
@@ -15480,22 +15260,13 @@ export class Runtime {
|
|
|
15480
15260
|
? whilePrintfInd
|
|
15481
15261
|
: (forPrintfInd && this.canSyncPrintfInductionBody(command.body, rawState, forPrintfInd) ? forPrintfInd : undefined));
|
|
15482
15262
|
const prevActivePrintfInd = this._activePrintfInductionName;
|
|
15483
|
-
const prevActiveLoopScript = this._activeLoopBodyScript;
|
|
15484
|
-
const prevActiveLoopCond = this._activeLoopCondScript;
|
|
15485
|
-
const prevActiveLoopVar = this._activeLoopVarName;
|
|
15486
15263
|
this._activePrintfInductionName = printfInductionName;
|
|
15487
|
-
this._activeLoopBodyScript = command.body;
|
|
15488
|
-
this._activeLoopCondScript = command.kind === "while" ? command.condition : undefined;
|
|
15489
|
-
this._activeLoopVarName = command.kind === "for" ? command.name : (printfInductionName ?? (whilePrintfInd || undefined));
|
|
15490
15264
|
let bodySyncOk = false;
|
|
15491
15265
|
try {
|
|
15492
15266
|
bodySyncOk = !(guestArrays(rawState) && (guestArrays(rawState).watches.size > 0 || this.budget.limits.maxExpansionBytes < 65536)) && this.canSyncLoopBody(command.body, rawState, io, false, printfInductionName);
|
|
15493
15267
|
}
|
|
15494
15268
|
finally {
|
|
15495
15269
|
this._activePrintfInductionName = prevActivePrintfInd;
|
|
15496
|
-
this._activeLoopBodyScript = prevActiveLoopScript;
|
|
15497
|
-
this._activeLoopCondScript = prevActiveLoopCond;
|
|
15498
|
-
this._activeLoopVarName = prevActiveLoopVar;
|
|
15499
15270
|
}
|
|
15500
15271
|
if (!bodySyncOk) {
|
|
15501
15272
|
command._skipTrySyncLoop = true;
|
|
@@ -16101,7 +15872,7 @@ export class Runtime {
|
|
|
16101
15872
|
!monitor.hasOverlay("BASH_REMATCH") &&
|
|
16102
15873
|
!(store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH"));
|
|
16103
15874
|
}
|
|
16104
|
-
return this.isPureSyncValueWord(e.left, rawState) && (e.operator === "==" || e.operator === "=" || e.operator === "!="
|
|
15875
|
+
return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || (e.right.parts.length > 0 && e.right.parts.every(p => p.quoted)));
|
|
16105
15876
|
}
|
|
16106
15877
|
if (e.kind === "not")
|
|
16107
15878
|
return checkWhileCond(e.operand, depth + 1);
|
|
@@ -16432,7 +16203,7 @@ export class Runtime {
|
|
|
16432
16203
|
store.epoch = restEpoch;
|
|
16433
16204
|
return loopStatus;
|
|
16434
16205
|
}
|
|
16435
|
-
if (!isShellIdentifier(command.name) || command.name === "OPTIND" || command.name.includes("[") || store?.get(command.name) || (command.words && (command.words.length === 0 || command.words.length > 64)) || rawState.braceexpand === false) {
|
|
16206
|
+
if (!isShellIdentifier(command.name) || command.name === "OPTIND" || controlNames.has(command.name) || command.name === "LC_COLLATE" || command.name.includes("[") || store?.get(command.name) || (command.words && (command.words.length === 0 || command.words.length > 64)) || rawState.braceexpand === false) {
|
|
16436
16207
|
return undefined;
|
|
16437
16208
|
}
|
|
16438
16209
|
const activeValScope = io[valueScope];
|
|
@@ -16931,187 +16702,6 @@ export class Runtime {
|
|
|
16931
16702
|
return pos <= 128 ? fastRedirectScratchViews[pos] : fastRedirectScratchBytes.subarray(0, pos);
|
|
16932
16703
|
}
|
|
16933
16704
|
_activePrintfInductionName = undefined;
|
|
16934
|
-
_activeLoopBodyScript = undefined;
|
|
16935
|
-
_activeLoopCondScript = undefined;
|
|
16936
|
-
_activeLoopVarName = undefined;
|
|
16937
|
-
isVarUnmutatedInScript(varName, script) {
|
|
16938
|
-
for (const list of script.lists) {
|
|
16939
|
-
for (const pipeline of list.pipelines) {
|
|
16940
|
-
for (const cmd of pipeline.commands) {
|
|
16941
|
-
if (cmd.kind === "arithmetic") {
|
|
16942
|
-
if (cmd.expression.hasMutation && cmd.expression.source.includes(varName))
|
|
16943
|
-
return false;
|
|
16944
|
-
continue;
|
|
16945
|
-
}
|
|
16946
|
-
if (cmd.kind === "if") {
|
|
16947
|
-
for (const br of cmd.branches) {
|
|
16948
|
-
if (!this.isVarUnmutatedInScript(varName, br.condition) || !this.isVarUnmutatedInScript(varName, br.body))
|
|
16949
|
-
return false;
|
|
16950
|
-
}
|
|
16951
|
-
if (cmd.otherwise && !this.isVarUnmutatedInScript(varName, cmd.otherwise))
|
|
16952
|
-
return false;
|
|
16953
|
-
continue;
|
|
16954
|
-
}
|
|
16955
|
-
if (cmd.kind === "case") {
|
|
16956
|
-
for (const cl of cmd.clauses) {
|
|
16957
|
-
if (!this.isVarUnmutatedInScript(varName, cl.body))
|
|
16958
|
-
return false;
|
|
16959
|
-
}
|
|
16960
|
-
continue;
|
|
16961
|
-
}
|
|
16962
|
-
if (cmd.kind === "for") {
|
|
16963
|
-
if (cmd.name === varName || !this.isVarUnmutatedInScript(varName, cmd.body))
|
|
16964
|
-
return false;
|
|
16965
|
-
continue;
|
|
16966
|
-
}
|
|
16967
|
-
if (cmd.kind === "arithmetic-for") {
|
|
16968
|
-
if (cmd.expressions.some(e => e && e.hasMutation && e.source.includes(varName)) || !this.isVarUnmutatedInScript(varName, cmd.body))
|
|
16969
|
-
return false;
|
|
16970
|
-
continue;
|
|
16971
|
-
}
|
|
16972
|
-
if (cmd.kind === "while") {
|
|
16973
|
-
if (!this.isVarUnmutatedInScript(varName, cmd.condition) || !this.isVarUnmutatedInScript(varName, cmd.body))
|
|
16974
|
-
return false;
|
|
16975
|
-
continue;
|
|
16976
|
-
}
|
|
16977
|
-
if (cmd.kind === "group" || cmd.kind === "subshell") {
|
|
16978
|
-
if (!this.isVarUnmutatedInScript(varName, cmd.body))
|
|
16979
|
-
return false;
|
|
16980
|
-
continue;
|
|
16981
|
-
}
|
|
16982
|
-
if (cmd.kind !== "simple")
|
|
16983
|
-
return false;
|
|
16984
|
-
for (const w of cmd.words) {
|
|
16985
|
-
const arrAss = getArrayAssignment(w);
|
|
16986
|
-
if (arrAss && arrAss.name === varName)
|
|
16987
|
-
return false;
|
|
16988
|
-
const p0 = w.parts[0];
|
|
16989
|
-
if (p0?.kind === "text" && !p0.quoted && (p0.value.startsWith(varName + "=") || p0.value.startsWith(varName + "+=") || p0.value.startsWith(varName + "[")))
|
|
16990
|
-
return false;
|
|
16991
|
-
}
|
|
16992
|
-
const w0 = cmd.words[0]?.plain;
|
|
16993
|
-
if (w0 === "read" || w0 === "unset" || w0 === "declare" || w0 === "typeset" || w0 === "local" || w0 === "export" || w0 === "mapfile" || w0 === "readarray") {
|
|
16994
|
-
if (w0 === "read" && varName === "REPLY")
|
|
16995
|
-
return false;
|
|
16996
|
-
if (cmd.words.slice(1).some(w => (w.plain ?? "").includes(varName)))
|
|
16997
|
-
return false;
|
|
16998
|
-
}
|
|
16999
|
-
if (w0 === "printf" && cmd.words[1]?.plain === "-v" && (cmd.words[2]?.plain ?? "").startsWith(varName))
|
|
17000
|
-
return false;
|
|
17001
|
-
for (const w of [...cmd.words, ...cmd.redirects.map(r => r.target)]) {
|
|
17002
|
-
for (const pt of w.parts) {
|
|
17003
|
-
if (pt.kind === "arithmetic" && pt.expression.hasMutation && pt.expression.source.includes(varName))
|
|
17004
|
-
return false;
|
|
17005
|
-
if (pt.kind === "substitution" && !this.isVarUnmutatedInScript(varName, pt.script))
|
|
17006
|
-
return false;
|
|
17007
|
-
}
|
|
17008
|
-
}
|
|
17009
|
-
}
|
|
17010
|
-
}
|
|
17011
|
-
}
|
|
17012
|
-
return true;
|
|
17013
|
-
}
|
|
17014
|
-
isVarUnmutatedInActiveLoop(varName) {
|
|
17015
|
-
if (varName === this._activeLoopVarName || varName === this._activePrintfInductionName)
|
|
17016
|
-
return false;
|
|
17017
|
-
if (this._activeLoopCondScript && !this.isVarUnmutatedInScript(varName, this._activeLoopCondScript))
|
|
17018
|
-
return false;
|
|
17019
|
-
if (this._activeLoopBodyScript && !this.isVarUnmutatedInScript(varName, this._activeLoopBodyScript))
|
|
17020
|
-
return false;
|
|
17021
|
-
return true;
|
|
17022
|
-
}
|
|
17023
|
-
isFileReadOnlyScript(script, rawState) {
|
|
17024
|
-
const visit = (value) => {
|
|
17025
|
-
if (value === null || typeof value !== "object")
|
|
17026
|
-
return true;
|
|
17027
|
-
if (Array.isArray(value))
|
|
17028
|
-
return value.every(visit);
|
|
17029
|
-
const node = value;
|
|
17030
|
-
if (typeof node.operator === "string" && "target" in node && !["<", "<<<", "<<", "<<-"].includes(node.operator))
|
|
17031
|
-
return false;
|
|
17032
|
-
if (node.kind === "simple") {
|
|
17033
|
-
const cmd = value;
|
|
17034
|
-
// Unknown commands (including shell functions) cannot promise read-only I/O.
|
|
17035
|
-
const name = cmd.words[0]?.plain;
|
|
17036
|
-
if (name === undefined && !cmd.words.every(word => this.assignment(word) || getArrayAssignment(word)))
|
|
17037
|
-
return false;
|
|
17038
|
-
if (name !== undefined && (hasShellFunction(rawState, name) || !["jq", "cat", "echo", "printf", "test", "[", ":", "true", "false"].includes(name)))
|
|
17039
|
-
return false;
|
|
17040
|
-
}
|
|
17041
|
-
return Object.values(node).every(visit);
|
|
17042
|
-
};
|
|
17043
|
-
return visit(script);
|
|
17044
|
-
}
|
|
17045
|
-
extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState) {
|
|
17046
|
-
const w0 = cmd.words[0]?.plain;
|
|
17047
|
-
const resolveInvariantWord = (w) => {
|
|
17048
|
-
if (w.plain !== undefined)
|
|
17049
|
-
return w.plain;
|
|
17050
|
-
if (w.parts.length > 0 && w.parts.every(pt => pt.kind === "text"))
|
|
17051
|
-
return w.parts.map(pt => pt.value).join("");
|
|
17052
|
-
if (w.parts.length === 1 && w.parts[0].kind === "variable") {
|
|
17053
|
-
const vp = w.parts[0];
|
|
17054
|
-
if (!vp.indirect && !vp.operator && !vp.substring && !vp.transform && !vp.length && !vp.prefixNames && getArraySelector(vp) === undefined && this.isVarUnmutatedInActiveLoop(vp.name)) {
|
|
17055
|
-
const val = rawState.variables[vp.name];
|
|
17056
|
-
if (typeof val === "string")
|
|
17057
|
-
return val;
|
|
17058
|
-
}
|
|
17059
|
-
}
|
|
17060
|
-
return undefined;
|
|
17061
|
-
};
|
|
17062
|
-
if (w0 === "jq" || w0 === "numfmt" || w0 === "awk") {
|
|
17063
|
-
if (hasSingleHereStringRedir) {
|
|
17064
|
-
const resolved = resolveInvariantWord(cmd.redirects[0].target);
|
|
17065
|
-
return resolved !== undefined ? resolved + "\n" : undefined;
|
|
17066
|
-
}
|
|
17067
|
-
if (this._fileWrites?.size || this._outputFiles?.size)
|
|
17068
|
-
return undefined;
|
|
17069
|
-
if ([this._activeLoopBodyScript, this._activeLoopCondScript].some(script => script && !this.isFileReadOnlyScript(script, rawState)))
|
|
17070
|
-
return undefined;
|
|
17071
|
-
const fPlain = hasSingleStdinRedir ? cmd.redirects[0].target.plain : cmd.words[cmd.words.length - 1]?.plain;
|
|
17072
|
-
if (!fPlain || fPlain.startsWith("-") || fPlain === "/dev/stdin")
|
|
17073
|
-
return undefined;
|
|
17074
|
-
const vCheck = this.tryReadMemoryFileViewSync(resolvePath(rawState.cwd, fPlain), false);
|
|
17075
|
-
if (!vCheck || vCheck.byteLength > 16384 || vCheck.includes(0))
|
|
17076
|
-
return undefined;
|
|
17077
|
-
return sharedSyncPipeDecoder.decode(vCheck);
|
|
17078
|
-
}
|
|
17079
|
-
if (w0 === "cat") {
|
|
17080
|
-
if (hasSingleHereStringRedir) {
|
|
17081
|
-
const resolved = resolveInvariantWord(cmd.redirects[0].target);
|
|
17082
|
-
return resolved !== undefined ? resolved + "\n" : undefined;
|
|
17083
|
-
}
|
|
17084
|
-
if (this._fileWrites?.size || this._outputFiles?.size)
|
|
17085
|
-
return undefined;
|
|
17086
|
-
if ([this._activeLoopBodyScript, this._activeLoopCondScript].some(script => script && !this.isFileReadOnlyScript(script, rawState)))
|
|
17087
|
-
return undefined;
|
|
17088
|
-
const fPlain = hasSingleStdinRedir ? cmd.redirects[0].target.plain : cmd.words[1]?.plain;
|
|
17089
|
-
if (!fPlain || fPlain.startsWith("-") || fPlain === "/dev/stdin")
|
|
17090
|
-
return undefined;
|
|
17091
|
-
const vCheck = this.tryReadMemoryFileViewSync(resolvePath(rawState.cwd, fPlain), false);
|
|
17092
|
-
if (!vCheck || vCheck.byteLength > 16384 || vCheck.includes(0))
|
|
17093
|
-
return undefined;
|
|
17094
|
-
return sharedSyncPipeDecoder.decode(vCheck);
|
|
17095
|
-
}
|
|
17096
|
-
if (w0 === "echo" && cmd.words.length === 2) {
|
|
17097
|
-
const resolved = resolveInvariantWord(cmd.words[1]);
|
|
17098
|
-
if (resolved !== undefined && !resolved.startsWith("-"))
|
|
17099
|
-
return resolved + "\n";
|
|
17100
|
-
return undefined;
|
|
17101
|
-
}
|
|
17102
|
-
if (w0 === "printf" && cmd.words.length >= 2) {
|
|
17103
|
-
const args = [];
|
|
17104
|
-
for (let i = 1; i < cmd.words.length; i++) {
|
|
17105
|
-
const r = resolveInvariantWord(cmd.words[i]);
|
|
17106
|
-
if (r === undefined)
|
|
17107
|
-
return undefined;
|
|
17108
|
-
args.push(r);
|
|
17109
|
-
}
|
|
17110
|
-
const formatted = tryFastPrintf(args);
|
|
17111
|
-
return formatted;
|
|
17112
|
-
}
|
|
17113
|
-
return undefined;
|
|
17114
|
-
}
|
|
17115
16705
|
canSyncPrintfInductionBody(script, rawState, inductionName) {
|
|
17116
16706
|
for (const list of script.lists)
|
|
17117
16707
|
for (const pipeline of list.pipelines)
|
|
@@ -17123,7 +16713,24 @@ export class Runtime {
|
|
|
17123
16713
|
}
|
|
17124
16714
|
if (command.kind !== "simple" || hasShellFunction(rawState, command.words[0]?.plain ?? "") || command.words.some(word => getArrayAssignment(word) !== undefined))
|
|
17125
16715
|
return false;
|
|
17126
|
-
|
|
16716
|
+
let commandIndex = 0;
|
|
16717
|
+
while (commandIndex < command.words.length && this.assignment(command.words[commandIndex]))
|
|
16718
|
+
commandIndex++;
|
|
16719
|
+
const commandWord = command.words[commandIndex];
|
|
16720
|
+
if (!commandWord?.parts.every(part => part.kind === "text"))
|
|
16721
|
+
return false;
|
|
16722
|
+
const w0 = commandWord.parts.map(part => part.kind === "text" ? part.value : "").join("");
|
|
16723
|
+
// read targets and printf -v targets may be quoted or expanded. Both can
|
|
16724
|
+
// invalidate the integer assumption used by induction formatting.
|
|
16725
|
+
if (w0 === "read")
|
|
16726
|
+
return false;
|
|
16727
|
+
if (w0 === "printf") {
|
|
16728
|
+
const option = command.words[commandIndex + 1];
|
|
16729
|
+
if (!option?.parts.every(part => part.kind === "text"))
|
|
16730
|
+
return false;
|
|
16731
|
+
if (option.parts.map(part => part.kind === "text" ? part.value : "").join("") === "-v")
|
|
16732
|
+
return false;
|
|
16733
|
+
}
|
|
17127
16734
|
if (w0 === "let" || w0 === "declare" || w0 === "typeset" || w0 === "local" || w0 === "export" || w0 === "unset" || w0 === "mapfile" || w0 === "readarray")
|
|
17128
16735
|
return false;
|
|
17129
16736
|
if (inductionName !== undefined) {
|
|
@@ -17132,10 +16739,6 @@ export class Runtime {
|
|
|
17132
16739
|
return p0?.kind === "text" && !p0.quoted && (p0.value.startsWith(inductionName + "=") || p0.value.startsWith(inductionName + "+="));
|
|
17133
16740
|
}))
|
|
17134
16741
|
return false;
|
|
17135
|
-
if (w0 === "read" && (inductionName === "REPLY" || command.words.slice(1).some(w => w.plain === inductionName || w.plain === "-a" || w.plain === "-ra" || w.plain === "-ar")))
|
|
17136
|
-
return false;
|
|
17137
|
-
if (w0 === "printf" && command.words[1]?.plain === "-v" && command.words[2]?.plain === inductionName)
|
|
17138
|
-
return false;
|
|
17139
16742
|
}
|
|
17140
16743
|
for (const word of [...command.words, ...command.redirects.map(redirect => redirect.target)])
|
|
17141
16744
|
for (const part of word.parts) {
|
|
@@ -17414,6 +17017,7 @@ export class Runtime {
|
|
|
17414
17017
|
execSyncLocalDeclStep(decl, rawState, io, monitor, touched, line) {
|
|
17415
17018
|
const locals = (decl.kind === "local" || decl.kind === "declare") && rawState.locals.length > 0 ? rawState.locals[rawState.locals.length - 1] : undefined;
|
|
17416
17019
|
let lastArg = decl.kind;
|
|
17020
|
+
const values = decl.items.map(item => item.value === undefined ? undefined : this.fastValueWord(item.value, rawState, io, false, false, false, rawState.braceexpand !== false, 0, line));
|
|
17417
17021
|
for (let i = 0; i < decl.items.length; i++) {
|
|
17418
17022
|
const item = decl.items[i];
|
|
17419
17023
|
const isNewLocal = locals ? !locals.has(item.name) : false;
|
|
@@ -17431,7 +17035,7 @@ export class Runtime {
|
|
|
17431
17035
|
touched.add(item.name);
|
|
17432
17036
|
}
|
|
17433
17037
|
if (item.value !== undefined) {
|
|
17434
|
-
const val =
|
|
17038
|
+
const val = values[i];
|
|
17435
17039
|
if (item.append) {
|
|
17436
17040
|
const curRaw = rawState.variables[item.name];
|
|
17437
17041
|
const curStr = curRaw === undefined ? "" : (touched.has(item.name) ? curRaw : (monitor.values.get(item.name, curRaw) ?? curRaw));
|
|
@@ -17441,7 +17045,7 @@ export class Runtime {
|
|
|
17441
17045
|
rawState.variables[item.name] = val;
|
|
17442
17046
|
}
|
|
17443
17047
|
touched.add(item.name);
|
|
17444
|
-
lastArg = `${item.name}
|
|
17048
|
+
lastArg = `${item.name}${item.append ? "+=" : "="}${val}`;
|
|
17445
17049
|
}
|
|
17446
17050
|
else {
|
|
17447
17051
|
lastArg = item.name;
|
|
@@ -17540,7 +17144,28 @@ export class Runtime {
|
|
|
17540
17144
|
if (this._syncStdoutBatch.length >= 8192)
|
|
17541
17145
|
this.flushSyncStdoutBatch(io);
|
|
17542
17146
|
}
|
|
17147
|
+
runSyncDiscardStep(step, rawState, io) {
|
|
17148
|
+
this.budget.fileSystemOperation();
|
|
17149
|
+
const args = step.discardWords.map(word => this.fastValueWord(word, rawState, io, false, false, false, false, 0, step.line));
|
|
17150
|
+
const cmdName = step.cmd.words[0].plain;
|
|
17151
|
+
let output;
|
|
17152
|
+
if (cmdName === "pwd")
|
|
17153
|
+
output = rawState.variables.PWD ?? rawState.cwd;
|
|
17154
|
+
else if (cmdName === "echo")
|
|
17155
|
+
output = args[0];
|
|
17156
|
+
else if (cmdName === "dirname")
|
|
17157
|
+
output = this.evalSyncDirname(args);
|
|
17158
|
+
else
|
|
17159
|
+
output = this.evalSyncBasename(step.discardSuffix === undefined ? args : ["-s", step.discardSuffix, ...args]);
|
|
17160
|
+
const bytes = shellValueByteLength(output) + 1;
|
|
17161
|
+
if (this.budget.bytes + bytes > this.budget.maxOutputBytesSmi && bytes > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
17162
|
+
this.budget.fail("maxOutputBytes");
|
|
17163
|
+
this.budget.bytes += bytes;
|
|
17164
|
+
return args[args.length - 1] ?? cmdName;
|
|
17165
|
+
}
|
|
17543
17166
|
writeSyncRedirectStep(step, encoded, append, mode, rawState, io) {
|
|
17167
|
+
if (this.budget.bytes + encoded.byteLength > this.budget.maxOutputBytesSmi && encoded.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
17168
|
+
this.budget.fail("maxOutputBytes");
|
|
17544
17169
|
// The memory fast writer may retain its input; the encoder scratch is reused.
|
|
17545
17170
|
encoded = encoded.slice();
|
|
17546
17171
|
if (step.targetDirPrefix !== undefined && step.targetNamePrefix !== undefined) {
|
|
@@ -17704,31 +17329,7 @@ export class Runtime {
|
|
|
17704
17329
|
lastArg = pLastArg;
|
|
17705
17330
|
}
|
|
17706
17331
|
else if (step.isDiscardDevNull && step.discardWords !== undefined) {
|
|
17707
|
-
this.
|
|
17708
|
-
const dw = step.discardWords;
|
|
17709
|
-
const cmdName = step.cmd.words[0].plain;
|
|
17710
|
-
let evalFirst = "";
|
|
17711
|
-
let evalLast = cmdName;
|
|
17712
|
-
for (let wi = 0; wi < dw.length; wi++) {
|
|
17713
|
-
const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
|
|
17714
|
-
if (wi === 0)
|
|
17715
|
-
evalFirst = v;
|
|
17716
|
-
evalLast = v;
|
|
17717
|
-
}
|
|
17718
|
-
let outChars = 0;
|
|
17719
|
-
if (cmdName === "pwd")
|
|
17720
|
-
outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
|
|
17721
|
-
else if (cmdName === "echo")
|
|
17722
|
-
outChars = evalFirst.length + 1;
|
|
17723
|
-
else if (cmdName === "dirname")
|
|
17724
|
-
outChars = dirname(evalFirst).length + 1;
|
|
17725
|
-
else if (cmdName === "basename") {
|
|
17726
|
-
const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
|
|
17727
|
-
const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
|
|
17728
|
-
outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
|
|
17729
|
-
}
|
|
17730
|
-
this.budget.bytes += outChars;
|
|
17731
|
-
lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
|
|
17332
|
+
lastArg = this.runSyncDiscardStep(step, rawState, io);
|
|
17732
17333
|
}
|
|
17733
17334
|
else if (step.name !== undefined && step.value !== undefined) {
|
|
17734
17335
|
rawState.substitutionStatus = 0;
|
|
@@ -17806,6 +17407,10 @@ export class Runtime {
|
|
|
17806
17407
|
return check(tree);
|
|
17807
17408
|
}
|
|
17808
17409
|
canSyncErrorArithmeticStmt(expr, rawState) {
|
|
17410
|
+
// Expansion can introduce array, attributed, or control-variable references.
|
|
17411
|
+
// Their reads and writes require the full arithmetic evaluator.
|
|
17412
|
+
if (expr.source.includes("$"))
|
|
17413
|
+
return false;
|
|
17809
17414
|
if (expr.source.includes("/") || expr.source.includes("%") || expr.source.includes("<<") || expr.source.includes(">>") || expr.source.includes("**") || expr.source.includes("["))
|
|
17810
17415
|
return false;
|
|
17811
17416
|
try {
|
|
@@ -17968,19 +17573,34 @@ export class Runtime {
|
|
|
17968
17573
|
const prevFastSubPos = this._fastSubPositional;
|
|
17969
17574
|
rawState.positional = fnArgs;
|
|
17970
17575
|
this._fastSubPositional = fnArgs;
|
|
17971
|
-
const savedLocals =
|
|
17576
|
+
const savedLocals = [];
|
|
17577
|
+
const seenLocals = new Set();
|
|
17972
17578
|
try {
|
|
17973
|
-
for (let i = 0; i < fnCall.locals.length;
|
|
17974
|
-
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
17982
|
-
|
|
17983
|
-
|
|
17579
|
+
for (let i = 0; i < fnCall.locals.length;) {
|
|
17580
|
+
this.budget.tick();
|
|
17581
|
+
let end = i + 1;
|
|
17582
|
+
while (end < fnCall.locals.length && !fnCall.locals[end].isFirstInCmd)
|
|
17583
|
+
end++;
|
|
17584
|
+
// Expand the entire simple command before any of its bindings change.
|
|
17585
|
+
const values = fnCall.locals.slice(i, end).map(loc => loc.valueWord === undefined
|
|
17586
|
+
? undefined
|
|
17587
|
+
: this.fastValueWord(loc.valueWord, rawState, io, false, false, false, false, 0, step.line));
|
|
17588
|
+
const start = i;
|
|
17589
|
+
for (; i < end; i++) {
|
|
17590
|
+
const loc = fnCall.locals[i];
|
|
17591
|
+
const alreadyLocal = seenLocals.has(loc.name);
|
|
17592
|
+
if (!alreadyLocal) {
|
|
17593
|
+
savedLocals.push({ name: loc.name, val: rawState.variables[loc.name], wasTouched: touched.has(loc.name) });
|
|
17594
|
+
seenLocals.add(loc.name);
|
|
17595
|
+
}
|
|
17596
|
+
const value = values[i - start];
|
|
17597
|
+
if (value !== undefined) {
|
|
17598
|
+
rawState.variables[loc.name] = value;
|
|
17599
|
+
touched.add(loc.name);
|
|
17600
|
+
}
|
|
17601
|
+
else if (!alreadyLocal) {
|
|
17602
|
+
delete rawState.variables[loc.name];
|
|
17603
|
+
}
|
|
17984
17604
|
}
|
|
17985
17605
|
}
|
|
17986
17606
|
if (fnCall.steps.length > 0) {
|
|
@@ -17988,11 +17608,12 @@ export class Runtime {
|
|
|
17988
17608
|
}
|
|
17989
17609
|
else {
|
|
17990
17610
|
rawState.status = 0;
|
|
17991
|
-
onUpdate({ lastCmd: step.cmd, lastArg: fnArgs.length > 0 ? fnArgs[fnArgs.length - 1] : fnCall.name });
|
|
17992
17611
|
}
|
|
17612
|
+
rawState.lastArgument = fnArgs.at(-1) ?? fnCall.name;
|
|
17613
|
+
onUpdate({ lastCmd: step.cmd, lastArg: rawState.lastArgument });
|
|
17993
17614
|
}
|
|
17994
17615
|
finally {
|
|
17995
|
-
for (let i =
|
|
17616
|
+
for (let i = savedLocals.length - 1; i >= 0; i--) {
|
|
17996
17617
|
const saved = savedLocals[i];
|
|
17997
17618
|
if (saved.val !== undefined) {
|
|
17998
17619
|
rawState.variables[saved.name] = saved.val;
|
|
@@ -18268,31 +17889,7 @@ export class Runtime {
|
|
|
18268
17889
|
lastArg = pLastArg;
|
|
18269
17890
|
}
|
|
18270
17891
|
else if (step.isDiscardDevNull && step.discardWords !== undefined) {
|
|
18271
|
-
this.
|
|
18272
|
-
const dw = step.discardWords;
|
|
18273
|
-
const cmdName = step.cmd.words[0].plain;
|
|
18274
|
-
let evalFirst = "";
|
|
18275
|
-
let evalLast = cmdName;
|
|
18276
|
-
for (let wi = 0; wi < dw.length; wi++) {
|
|
18277
|
-
const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
|
|
18278
|
-
if (wi === 0)
|
|
18279
|
-
evalFirst = v;
|
|
18280
|
-
evalLast = v;
|
|
18281
|
-
}
|
|
18282
|
-
let outChars = 0;
|
|
18283
|
-
if (cmdName === "pwd")
|
|
18284
|
-
outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
|
|
18285
|
-
else if (cmdName === "echo")
|
|
18286
|
-
outChars = evalFirst.length + 1;
|
|
18287
|
-
else if (cmdName === "dirname")
|
|
18288
|
-
outChars = dirname(evalFirst).length + 1;
|
|
18289
|
-
else if (cmdName === "basename") {
|
|
18290
|
-
const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
|
|
18291
|
-
const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
|
|
18292
|
-
outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
|
|
18293
|
-
}
|
|
18294
|
-
this.budget.bytes += outChars;
|
|
18295
|
-
lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
|
|
17892
|
+
lastArg = this.runSyncDiscardStep(step, rawState, io);
|
|
18296
17893
|
}
|
|
18297
17894
|
else if (step.name !== undefined && step.value !== undefined) {
|
|
18298
17895
|
rawState.substitutionStatus = 0;
|
|
@@ -18448,31 +18045,7 @@ export class Runtime {
|
|
|
18448
18045
|
lastArg = pLastArg;
|
|
18449
18046
|
}
|
|
18450
18047
|
else if (step.isDiscardDevNull && step.discardWords !== undefined) {
|
|
18451
|
-
this.
|
|
18452
|
-
const dw = step.discardWords;
|
|
18453
|
-
const cmdName = step.cmd.words[0].plain;
|
|
18454
|
-
let evalFirst = "";
|
|
18455
|
-
let evalLast = cmdName;
|
|
18456
|
-
for (let wi = 0; wi < dw.length; wi++) {
|
|
18457
|
-
const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
|
|
18458
|
-
if (wi === 0)
|
|
18459
|
-
evalFirst = v;
|
|
18460
|
-
evalLast = v;
|
|
18461
|
-
}
|
|
18462
|
-
let outChars = 0;
|
|
18463
|
-
if (cmdName === "pwd")
|
|
18464
|
-
outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
|
|
18465
|
-
else if (cmdName === "echo")
|
|
18466
|
-
outChars = evalFirst.length + 1;
|
|
18467
|
-
else if (cmdName === "dirname")
|
|
18468
|
-
outChars = dirname(evalFirst).length + 1;
|
|
18469
|
-
else if (cmdName === "basename") {
|
|
18470
|
-
const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
|
|
18471
|
-
const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
|
|
18472
|
-
outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
|
|
18473
|
-
}
|
|
18474
|
-
this.budget.bytes += outChars;
|
|
18475
|
-
lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
|
|
18048
|
+
lastArg = this.runSyncDiscardStep(step, rawState, io);
|
|
18476
18049
|
}
|
|
18477
18050
|
else if (step.name !== undefined && step.value !== undefined) {
|
|
18478
18051
|
rawState.substitutionStatus = 0;
|
|
@@ -19407,8 +18980,6 @@ export class Runtime {
|
|
|
19407
18980
|
const rawState = stateMonitor(state)?.raw ?? state;
|
|
19408
18981
|
if (rawState.variables._ !== undefined)
|
|
19409
18982
|
delete rawState.variables._;
|
|
19410
|
-
rawState.lastArgument = wordValues[wordValues.length - 1] ?? w0Plain;
|
|
19411
|
-
state.lastArgument = rawState.lastArgument;
|
|
19412
18983
|
return await this.dispatchFastFunction(w0Plain, state.functions.get(w0Plain), wordValues.slice(1), state, originalIO);
|
|
19413
18984
|
}
|
|
19414
18985
|
}
|
|
@@ -20682,8 +20253,10 @@ export class Runtime {
|
|
|
20682
20253
|
const rawState = stateMonitor(state)?.raw ?? state;
|
|
20683
20254
|
if (!state._readOnlyStage && rawState.variables._ !== undefined)
|
|
20684
20255
|
delete rawState.variables._;
|
|
20685
|
-
|
|
20686
|
-
|
|
20256
|
+
if (!state.functions.has(words[0] ?? "") && !["eval", ".", "source"].includes(words[declarationIndex] ?? "")) {
|
|
20257
|
+
rawState.lastArgument = words.at(-1) ?? "";
|
|
20258
|
+
state.lastArgument = rawState.lastArgument;
|
|
20259
|
+
}
|
|
20687
20260
|
if (!words.length)
|
|
20688
20261
|
return state.substitutionStatus;
|
|
20689
20262
|
const args = words.slice(1);
|
|
@@ -20947,8 +20520,10 @@ export class Runtime {
|
|
|
20947
20520
|
const rawState = stateMonitor(state)?.raw ?? state;
|
|
20948
20521
|
if (rawState.variables._ !== undefined && !assignments.some(assignment => assignment.name === "_"))
|
|
20949
20522
|
delete rawState.variables._;
|
|
20950
|
-
|
|
20951
|
-
|
|
20523
|
+
if (!functionCommand && !["eval", ".", "source"].includes(words[declarationIndex] ?? "")) {
|
|
20524
|
+
rawState.lastArgument = words.at(-1) ?? "";
|
|
20525
|
+
state.lastArgument = rawState.lastArgument;
|
|
20526
|
+
}
|
|
20952
20527
|
}
|
|
20953
20528
|
if (!words.length)
|
|
20954
20529
|
return state.substitutionStatus;
|
|
@@ -21282,6 +20857,7 @@ export class Runtime {
|
|
|
21282
20857
|
}
|
|
21283
20858
|
if (hasThrown)
|
|
21284
20859
|
throw thrownError;
|
|
20860
|
+
state.lastArgument = pendingResume?.lastArgument ?? args.at(-1) ?? name;
|
|
21285
20861
|
return exitStatus;
|
|
21286
20862
|
}
|
|
21287
20863
|
finally {
|
|
@@ -21801,6 +21377,7 @@ export class Runtime {
|
|
|
21801
21377
|
}
|
|
21802
21378
|
if (outcome.kind === "throw")
|
|
21803
21379
|
throw outcome.reason;
|
|
21380
|
+
state.lastArgument = context.args.at(-1) ?? context.command;
|
|
21804
21381
|
return outcome.value;
|
|
21805
21382
|
}
|
|
21806
21383
|
if (selectedKind === "builtin" && !(state.externalInvocation && this.commands.has(context.command))) {
|
|
@@ -21817,10 +21394,16 @@ export class Runtime {
|
|
|
21817
21394
|
}
|
|
21818
21395
|
if (context.command === "command" || context.command === "builtin" || context.command === "type")
|
|
21819
21396
|
return { exitCode: await this.discoveryBuiltin(context, state, io, assignments, defaultPath) };
|
|
21820
|
-
if (context.command === "." || context.command === "source")
|
|
21821
|
-
|
|
21822
|
-
|
|
21823
|
-
return { exitCode
|
|
21397
|
+
if (context.command === "." || context.command === "source") {
|
|
21398
|
+
const exitCode = await this.sourceBuiltin(context, state, { ...io, ...context }, special);
|
|
21399
|
+
state.lastArgument = context.args.at(-1) ?? context.command;
|
|
21400
|
+
return { exitCode };
|
|
21401
|
+
}
|
|
21402
|
+
if (context.command === "eval") {
|
|
21403
|
+
const exitCode = await this.evalBuiltin(context, state, { ...io, ...context }, special);
|
|
21404
|
+
state.lastArgument = context.args.at(-1) ?? context.command;
|
|
21405
|
+
return { exitCode };
|
|
21406
|
+
}
|
|
21824
21407
|
const builtinWork = this.builtin({ ...context, [declarationArrays]: io[declarationArrays] }, state, assignments, (error, diagnostic) => { builtinFailure = { error, diagnostic }; }, bypassFunctions);
|
|
21825
21408
|
const builtin = await interruptible(builtinWork, this.signal);
|
|
21826
21409
|
if (builtin !== undefined) {
|
|
@@ -27162,6 +26745,10 @@ export class Runtime {
|
|
|
27162
26745
|
expandSyncTrCharArray(spec) {
|
|
27163
26746
|
if (spec.length === 0)
|
|
27164
26747
|
return undefined;
|
|
26748
|
+
// Non-ASCII sets require the byte-oriented tr parser.
|
|
26749
|
+
for (let i = 0; i < spec.length; i++)
|
|
26750
|
+
if (spec.charCodeAt(i) >= 128)
|
|
26751
|
+
return undefined;
|
|
27165
26752
|
// Escaped endpoints need the tr parser before range expansion.
|
|
27166
26753
|
if (spec.includes("\\") && spec.includes("-"))
|
|
27167
26754
|
return undefined;
|
|
@@ -27694,10 +27281,10 @@ export class Runtime {
|
|
|
27694
27281
|
if (!el || typeof el !== "object" || Array.isArray(el))
|
|
27695
27282
|
return undefined;
|
|
27696
27283
|
const rec = el;
|
|
27697
|
-
const k = rec.key
|
|
27284
|
+
const k = [rec.key, rec.Key, rec.name, rec.Name].find(value => value !== undefined && value !== null && value !== false);
|
|
27698
27285
|
if (typeof k !== "string")
|
|
27699
27286
|
return undefined;
|
|
27700
|
-
obj[k] = Object.hasOwn(rec, "value") ? rec.value : (Object.hasOwn(rec, "
|
|
27287
|
+
obj[k] = Object.hasOwn(rec, "value") ? rec.value : (Object.hasOwn(rec, "Value") ? rec.Value : null);
|
|
27701
27288
|
}
|
|
27702
27289
|
return [obj];
|
|
27703
27290
|
}
|
|
@@ -27730,7 +27317,7 @@ export class Runtime {
|
|
|
27730
27317
|
return [item.map(x => x === null ? "" : String(x)).join(arg)];
|
|
27731
27318
|
}
|
|
27732
27319
|
if (fn === "split")
|
|
27733
|
-
return typeof item === "string" ? [item.split(arg)] : undefined;
|
|
27320
|
+
return typeof item === "string" ? [arg === "" ? Array.from(item) : item.split(arg)] : undefined;
|
|
27734
27321
|
if (fn === "ltrimstr")
|
|
27735
27322
|
return typeof item === "string" ? [item.startsWith(arg) ? item.slice(arg.length) : item] : undefined;
|
|
27736
27323
|
if (fn === "rtrimstr")
|
|
@@ -27992,9 +27579,8 @@ export class Runtime {
|
|
|
27992
27579
|
break;
|
|
27993
27580
|
}
|
|
27994
27581
|
if (!a.startsWith("-") || a === "-") {
|
|
27995
|
-
operands.push(
|
|
27996
|
-
|
|
27997
|
-
continue;
|
|
27582
|
+
operands.push(...args.slice(idx));
|
|
27583
|
+
break;
|
|
27998
27584
|
}
|
|
27999
27585
|
if (a === "-a" || a === "--multiple") {
|
|
28000
27586
|
multiple = true;
|
|
@@ -28320,6 +27906,10 @@ export class Runtime {
|
|
|
28320
27906
|
const core = anchorEnd ? core1.slice(0, -1) : core1;
|
|
28321
27907
|
if (core.length === 0 && !anchorStart && !anchorEnd)
|
|
28322
27908
|
return undefined;
|
|
27909
|
+
// POSIX-only brackets and leftmost-longest alternatives belong to sed's matcher.
|
|
27910
|
+
if (core.includes("[:") || core.includes("[.") || core.includes("[=") ||
|
|
27911
|
+
core.includes("[]") || core.includes("[^]") || isExtended && core.includes("|"))
|
|
27912
|
+
return undefined;
|
|
28323
27913
|
let reSrc;
|
|
28324
27914
|
if (/^[a-zA-Z0-9_ :;,=-]*$/.test(core)) {
|
|
28325
27915
|
if (/\\[1-9]/.test(rep))
|
|
@@ -28376,7 +27966,9 @@ export class Runtime {
|
|
|
28376
27966
|
}
|
|
28377
27967
|
if (st.kind === "=") {
|
|
28378
27968
|
out.push(String(idx1));
|
|
28379
|
-
|
|
27969
|
+
// Line numbers are generated records, always terminated, even when
|
|
27970
|
+
// the final input record is partial. Later prints can override this.
|
|
27971
|
+
lastInputIndex = -1;
|
|
28380
27972
|
continue;
|
|
28381
27973
|
}
|
|
28382
27974
|
if (st.kind === "q") {
|
|
@@ -28419,8 +28011,10 @@ export class Runtime {
|
|
|
28419
28011
|
}
|
|
28420
28012
|
return replacement;
|
|
28421
28013
|
});
|
|
28422
|
-
if (matchedOnce && st.printOnMatch)
|
|
28014
|
+
if (matchedOnce && st.printOnMatch) {
|
|
28423
28015
|
out.push(l);
|
|
28016
|
+
lastInputIndex = i;
|
|
28017
|
+
}
|
|
28424
28018
|
}
|
|
28425
28019
|
if (quitNow) {
|
|
28426
28020
|
break;
|
|
@@ -28529,7 +28123,9 @@ export class Runtime {
|
|
|
28529
28123
|
const isOnlyMatching = mode.includes("o");
|
|
28530
28124
|
// POSIX bracket classes, collating symbols and equivalence classes have
|
|
28531
28125
|
// different meanings in JS RegExp. Let the command matcher parse them.
|
|
28532
|
-
if (
|
|
28126
|
+
if (!isFixed && rawPatterns.some(pattern => pattern.includes("[:") || pattern.includes("[.") || pattern.includes("[=") ||
|
|
28127
|
+
pattern.includes("[]") || pattern.includes("[^]") ||
|
|
28128
|
+
isExtended && pattern.includes("[") && pattern.includes("|")))
|
|
28533
28129
|
return undefined;
|
|
28534
28130
|
if (isCount && (isLineNumber || isOnlyMatching || beforeCtx > 0 || afterCtx > 0))
|
|
28535
28131
|
return undefined;
|
|
@@ -28562,15 +28158,19 @@ export class Runtime {
|
|
|
28562
28158
|
if (!re)
|
|
28563
28159
|
return undefined;
|
|
28564
28160
|
const out = [];
|
|
28161
|
+
let matched = false;
|
|
28565
28162
|
for (let li = 0; li < rawLines.length; li++) {
|
|
28566
28163
|
const matches = rawLines[li].match(re);
|
|
28567
28164
|
if (matches) {
|
|
28165
|
+
matched = true;
|
|
28568
28166
|
for (let mi = 0; mi < matches.length; mi++) {
|
|
28167
|
+
if (matches[mi].length === 0)
|
|
28168
|
+
continue;
|
|
28569
28169
|
out.push(isLineNumber ? `${li + 1}:${matches[mi]}` : matches[mi]);
|
|
28570
28170
|
}
|
|
28571
28171
|
}
|
|
28572
28172
|
}
|
|
28573
|
-
return { lines: out, status:
|
|
28173
|
+
return { lines: out, status: matched ? 0 : 1 };
|
|
28574
28174
|
}
|
|
28575
28175
|
if (isExtended && isFixed)
|
|
28576
28176
|
return undefined;
|
|
@@ -29606,22 +29206,16 @@ export class Runtime {
|
|
|
29606
29206
|
if (endField !== undefined && endField < startField)
|
|
29607
29207
|
return undefined;
|
|
29608
29208
|
const kf = km[3] ?? "";
|
|
29609
|
-
if (kf.
|
|
29610
|
-
keyNum =
|
|
29611
|
-
|
|
29612
|
-
|
|
29613
|
-
|
|
29614
|
-
|
|
29615
|
-
|
|
29616
|
-
|
|
29617
|
-
|
|
29618
|
-
|
|
29619
|
-
if (kf.includes("f"))
|
|
29620
|
-
keyFold = true;
|
|
29621
|
-
if (kf.includes("b"))
|
|
29622
|
-
keyBlanks = true;
|
|
29623
|
-
if (kf.includes("V"))
|
|
29624
|
-
keyVer = true;
|
|
29209
|
+
if (kf.length > 0) {
|
|
29210
|
+
keyNum = kf.includes("n");
|
|
29211
|
+
keyHuman = kf.includes("h");
|
|
29212
|
+
keyMonth = kf.includes("M");
|
|
29213
|
+
keyDict = kf.includes("d");
|
|
29214
|
+
keyRev = kf.includes("r");
|
|
29215
|
+
keyFold = kf.includes("f");
|
|
29216
|
+
keyBlanks = kf.includes("b");
|
|
29217
|
+
keyVer = kf.includes("V");
|
|
29218
|
+
}
|
|
29625
29219
|
}
|
|
29626
29220
|
const extractKey = (l) => {
|
|
29627
29221
|
let raw = keySpec === undefined
|
|
@@ -29653,7 +29247,14 @@ export class Runtime {
|
|
|
29653
29247
|
raw = raw.replace(/^[ \t]+/, "");
|
|
29654
29248
|
if (keyDict)
|
|
29655
29249
|
raw = raw.replace(/[^a-zA-Z0-9 \t]+/g, "");
|
|
29656
|
-
|
|
29250
|
+
if (!keyFold)
|
|
29251
|
+
return raw;
|
|
29252
|
+
let folded = "";
|
|
29253
|
+
for (let i = 0; i < raw.length; i++) {
|
|
29254
|
+
const code = raw.charCodeAt(i);
|
|
29255
|
+
folded += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : raw[i];
|
|
29256
|
+
}
|
|
29257
|
+
return folded;
|
|
29657
29258
|
};
|
|
29658
29259
|
const parseNum = (s) => {
|
|
29659
29260
|
const m = /^[ \t]*(-?(?:\d+(?:\.\d*)?|\.\d+))/.exec(s);
|
|
@@ -29686,6 +29287,24 @@ export class Runtime {
|
|
|
29686
29287
|
const idx = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"].indexOf(m[1].toUpperCase());
|
|
29687
29288
|
return idx === -1 ? 0 : idx + 1;
|
|
29688
29289
|
};
|
|
29290
|
+
const encoded = new Map();
|
|
29291
|
+
const bytesOf = (line) => {
|
|
29292
|
+
let bytes = encoded.get(line);
|
|
29293
|
+
if (bytes === undefined) {
|
|
29294
|
+
bytes = fastSharedTextEncoder.encode(line);
|
|
29295
|
+
encoded.set(line, bytes);
|
|
29296
|
+
}
|
|
29297
|
+
return bytes;
|
|
29298
|
+
};
|
|
29299
|
+
const compareBytes = (left, right) => {
|
|
29300
|
+
const a = bytesOf(left);
|
|
29301
|
+
const b = bytesOf(right);
|
|
29302
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
29303
|
+
if (a[i] !== b[i])
|
|
29304
|
+
return a[i] - b[i];
|
|
29305
|
+
}
|
|
29306
|
+
return a.length - b.length;
|
|
29307
|
+
};
|
|
29689
29308
|
const sorted = [...rawLines].sort((a, b) => {
|
|
29690
29309
|
const ka = extractKey(a);
|
|
29691
29310
|
const kb = extractKey(b);
|
|
@@ -29712,15 +29331,13 @@ export class Runtime {
|
|
|
29712
29331
|
return keyRev ? -vc : vc;
|
|
29713
29332
|
}
|
|
29714
29333
|
else if (ka !== kb) {
|
|
29715
|
-
|
|
29334
|
+
const comparison = compareBytes(ka, kb);
|
|
29335
|
+
return keyRev ? -comparison : comparison;
|
|
29716
29336
|
}
|
|
29717
29337
|
if (uniq || stable)
|
|
29718
29338
|
return 0;
|
|
29719
|
-
const
|
|
29720
|
-
|
|
29721
|
-
if (fa !== fb)
|
|
29722
|
-
return fa < fb ? (rev ? 1 : -1) : (rev ? -1 : 1);
|
|
29723
|
-
return a < b ? (rev ? 1 : -1) : a > b ? (rev ? -1 : 1) : 0;
|
|
29339
|
+
const comparison = compareBytes(a, b);
|
|
29340
|
+
return rev ? -comparison : comparison;
|
|
29724
29341
|
});
|
|
29725
29342
|
if (!uniq)
|
|
29726
29343
|
return sorted;
|
|
@@ -29846,6 +29463,11 @@ export class Runtime {
|
|
|
29846
29463
|
return this.evalSyncSort(rawLines, flag !== undefined ? [flag] : [], false) ?? [...rawLines];
|
|
29847
29464
|
}
|
|
29848
29465
|
translateSyncTr(input, set1Spec, set2Spec, truncateSet1 = false) {
|
|
29466
|
+
// Other string2 classes require the tr parser's class and alignment validation.
|
|
29467
|
+
if (set2Spec.includes("[:") &&
|
|
29468
|
+
!(set1Spec === "[:lower:]" && set2Spec === "[:upper:]") &&
|
|
29469
|
+
!(set1Spec === "[:upper:]" && set2Spec === "[:lower:]"))
|
|
29470
|
+
return undefined;
|
|
29849
29471
|
if ((set1Spec === "a-z" || set1Spec === "[:lower:]") && (set2Spec === "A-Z" || set2Spec === "[:upper:]")) {
|
|
29850
29472
|
return input.replace(/[a-z]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 32));
|
|
29851
29473
|
}
|
|
@@ -30221,7 +29843,7 @@ export class Runtime {
|
|
|
30221
29843
|
const firstName = stageNames[sIdx];
|
|
30222
29844
|
const extDef = stageDefs[sIdx];
|
|
30223
29845
|
const stageArgs = stageArgsList[sIdx];
|
|
30224
|
-
|
|
29846
|
+
let nextBuf = (index & 1) === 0 ? sharedSyncPipeBuf0 : sharedSyncPipeBuf1;
|
|
30225
29847
|
const isInlineCutField = firstName === "cut";
|
|
30226
29848
|
const isInlineTr = firstName === "tr";
|
|
30227
29849
|
const isInlineAwk = firstName === "awk";
|
|
@@ -30270,6 +29892,8 @@ export class Runtime {
|
|
|
30270
29892
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && sub.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
30271
29893
|
this.budget.fail("maxOutputBytes");
|
|
30272
29894
|
this.budget.bytes = nextTotalBytes;
|
|
29895
|
+
if (sub.byteLength > nextBuf.byteLength)
|
|
29896
|
+
nextBuf = new Uint8Array(sub.byteLength);
|
|
30273
29897
|
nextBuf.set(sub, 0);
|
|
30274
29898
|
prevBuf = nextBuf;
|
|
30275
29899
|
prevLen = sub.byteLength;
|
|
@@ -30319,12 +29943,12 @@ export class Runtime {
|
|
|
30319
29943
|
if (transformed === undefined)
|
|
30320
29944
|
return undefined;
|
|
30321
29945
|
const outByteLen = shellValueByteLength(transformed);
|
|
30322
|
-
if (outByteLen > nextBuf.byteLength)
|
|
30323
|
-
return undefined;
|
|
30324
29946
|
const nextTotalBytes = this.budget.bytes + outByteLen;
|
|
30325
29947
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && outByteLen > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
30326
29948
|
this.budget.fail("maxOutputBytes");
|
|
30327
29949
|
this.budget.bytes = nextTotalBytes;
|
|
29950
|
+
if (outByteLen > nextBuf.byteLength)
|
|
29951
|
+
nextBuf = new Uint8Array(outByteLen);
|
|
30328
29952
|
const written = transformed.length === 0 ? 0 : fastSharedTextEncoder.encodeInto(transformed, nextBuf).written;
|
|
30329
29953
|
prevBuf = nextBuf;
|
|
30330
29954
|
prevLen = written;
|
|
@@ -30362,9 +29986,10 @@ export class Runtime {
|
|
|
30362
29986
|
outLines = grepRes.lines;
|
|
30363
29987
|
}
|
|
30364
29988
|
else if (isInlineTac) {
|
|
30365
|
-
if (inStr.length > 0 && !inStr.endsWith("\n"))
|
|
30366
|
-
return undefined;
|
|
30367
29989
|
outLines = [...rawLines].reverse();
|
|
29990
|
+
if (!inStr.endsWith("\n") && outLines.length > 1) {
|
|
29991
|
+
outLines.splice(0, 2, outLines[0] + outLines[1]);
|
|
29992
|
+
}
|
|
30368
29993
|
}
|
|
30369
29994
|
else if (isInlineNl) {
|
|
30370
29995
|
const nlRes = this.evalSyncNl(rawLines, stageArgs);
|
|
@@ -30394,12 +30019,14 @@ export class Runtime {
|
|
|
30394
30019
|
if (cleaned.length % 4 !== 0 || (cleaned.length > 0 && !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)))
|
|
30395
30020
|
return undefined;
|
|
30396
30021
|
const decoded = this.syncBase64DecodeBytes(cleaned);
|
|
30397
|
-
if (decoded.some(byte => byte === 0 || byte >= 128)
|
|
30022
|
+
if (decoded.some(byte => byte === 0 || byte >= 128))
|
|
30398
30023
|
return undefined;
|
|
30399
30024
|
const nextTotalBytes = this.budget.bytes + decoded.byteLength;
|
|
30400
30025
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && decoded.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
30401
30026
|
this.budget.fail("maxOutputBytes");
|
|
30402
30027
|
this.budget.bytes = nextTotalBytes;
|
|
30028
|
+
if (decoded.byteLength > nextBuf.byteLength)
|
|
30029
|
+
nextBuf = new Uint8Array(decoded.byteLength);
|
|
30403
30030
|
nextBuf.set(decoded, 0);
|
|
30404
30031
|
prevBuf = nextBuf;
|
|
30405
30032
|
prevLen = decoded.byteLength;
|
|
@@ -30414,15 +30041,15 @@ export class Runtime {
|
|
|
30414
30041
|
}
|
|
30415
30042
|
// These filters preserve the terminator of the final selected input line.
|
|
30416
30043
|
const preservesTerminator = firstName === "head" || firstName === "tail" || firstName === "rev" || firstName === "sed";
|
|
30417
|
-
const terminated = sedTerminated || !preservesTerminator || inStr.endsWith("\n") || firstName === "head" && outLines.length < rawLines.length;
|
|
30044
|
+
const terminated = isInlineTac ? inStr.includes("\n") : sedTerminated || !preservesTerminator || inStr.endsWith("\n") || firstName === "head" && outLines.length < rawLines.length;
|
|
30418
30045
|
const outStr = outLines.length > 0 ? outLines.join("\n") + (terminated ? "\n" : "") : "";
|
|
30419
30046
|
const outByteLen = shellValueByteLength(outStr);
|
|
30420
|
-
if (outByteLen > nextBuf.byteLength)
|
|
30421
|
-
return undefined;
|
|
30422
30047
|
const nextTotalBytes = this.budget.bytes + outByteLen;
|
|
30423
30048
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && outByteLen > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
30424
30049
|
this.budget.fail("maxOutputBytes");
|
|
30425
30050
|
this.budget.bytes = nextTotalBytes;
|
|
30051
|
+
if (outByteLen > nextBuf.byteLength)
|
|
30052
|
+
nextBuf = new Uint8Array(outByteLen);
|
|
30426
30053
|
const written = outStr.length === 0 ? 0 : fastSharedTextEncoder.encodeInto(outStr, nextBuf).written;
|
|
30427
30054
|
prevBuf = nextBuf;
|
|
30428
30055
|
prevLen = written;
|