@poe-platform/safe-bash 0.1.749 → 0.1.750
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-BQEH4DKJ.js → chunk-U7DRVFRD.js} +171 -41
- package/dist/safe-bash/chunks/{src-YBSQFQZO.js → src-YKSQQDD3.js} +2 -2
- package/dist/safe-bash/commands/basic.js +2 -2
- package/dist/safe-bash/core.browser.js +105 -355
- package/dist/safe-bash/shell/conditional.js +1 -2
- package/dist/safe-bash/shell/runtime.d.ts +0 -7
- package/dist/safe-bash/shell/runtime.js +128 -519
- package/dist/safe-bash-command-ssconvert/index.js +170 -40
- 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);
|
|
@@ -13075,6 +13076,7 @@ export class Runtime {
|
|
|
13075
13076
|
positionalSetVersion,
|
|
13076
13077
|
savedPositionals: resumeSavedPos,
|
|
13077
13078
|
callerLoopDepth,
|
|
13079
|
+
lastArgument: fnArgs.at(-1) ?? w0Plain,
|
|
13078
13080
|
listIndex: bodyRes.listIndex,
|
|
13079
13081
|
pipelineIndex: bodyRes.pipelineIndex,
|
|
13080
13082
|
});
|
|
@@ -13139,6 +13141,7 @@ export class Runtime {
|
|
|
13139
13141
|
this.setSyncPipeStatusCell(activeExisting, statusStr);
|
|
13140
13142
|
activeStore.changed(monitor.chargeInternal(syncPipeStatusCharge, syncPipeStatusTickets), "PIPESTATUS");
|
|
13141
13143
|
}
|
|
13144
|
+
rawState.lastArgument = fnArgs.at(-1) ?? w0Plain;
|
|
13142
13145
|
rawState.status = exitStatus;
|
|
13143
13146
|
if (exitStatus !== 0 && !ignored && rawState.errexit)
|
|
13144
13147
|
throw new Flow("exit", exitStatus);
|
|
@@ -13736,25 +13739,12 @@ export class Runtime {
|
|
|
13736
13739
|
if (list.terminator || list.pipelines.length !== 1)
|
|
13737
13740
|
return false;
|
|
13738
13741
|
const p = list.pipelines[0];
|
|
13739
|
-
|
|
13742
|
+
// Pipeline buffers can overflow after variable or stage expansion.
|
|
13743
|
+
// A synchronous loop cannot resume after earlier body effects, so keep
|
|
13744
|
+
// bounded pipelines on the normal substitution execution path.
|
|
13745
|
+
if (p.negate || p.commands.length !== 1)
|
|
13740
13746
|
return false;
|
|
13741
13747
|
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
13748
|
// Loop substitutions require a real subshell: header admission can
|
|
13759
13749
|
// decline at runtime, and induction writes also change export attributes.
|
|
13760
13750
|
if (cmd.kind === "for" || cmd.kind === "arithmetic-for" || cmd.kind === "while")
|
|
@@ -13762,7 +13752,7 @@ export class Runtime {
|
|
|
13762
13752
|
if (p.commands.length === 1 && (cmd.kind === "if" || cmd.kind === "case") && this.isPureSyncSubIfOrCase(cmd, rawState)) {
|
|
13763
13753
|
continue;
|
|
13764
13754
|
}
|
|
13765
|
-
if (cmd.kind !== "simple" ||
|
|
13755
|
+
if (cmd.kind !== "simple" || cmd.redirects.length > 0 || cmd.words.length === 0)
|
|
13766
13756
|
return false;
|
|
13767
13757
|
let w0Plain = cmd.words[0].plain;
|
|
13768
13758
|
// Range-aware tr and Buffer-free 76-col base64 are handled below.
|
|
@@ -13777,10 +13767,10 @@ export class Runtime {
|
|
|
13777
13767
|
fnBody.body.lists[0].pipelines[0].commands.length === 1 &&
|
|
13778
13768
|
fnBody.body.lists[0].pipelines[0].commands[0].kind === "simple" &&
|
|
13779
13769
|
fnBody.body.lists[0].pipelines[0].commands[0].redirects.length === 0 &&
|
|
13780
|
-
cmd.words.slice(1).every(w => this.isPureSyncValueWord(w, rawState))) {
|
|
13770
|
+
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
13771
|
const innerCmd = fnBody.body.lists[0].pipelines[0].commands[0];
|
|
13782
13772
|
const innerW0 = innerCmd.words[0]?.plain;
|
|
13783
|
-
if (innerW0 && !rawState.functions.has(innerW0) && !rawState.extensions?.builtins.has(innerW0)) {
|
|
13773
|
+
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
13774
|
cmd = innerCmd;
|
|
13785
13775
|
w0Plain = innerW0;
|
|
13786
13776
|
}
|
|
@@ -13796,143 +13786,23 @@ export class Runtime {
|
|
|
13796
13786
|
return false;
|
|
13797
13787
|
}
|
|
13798
13788
|
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
|
-
}
|
|
13789
|
+
// PATH entries can change after admission, making discovery decline.
|
|
13808
13790
|
return false;
|
|
13809
13791
|
}
|
|
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))
|
|
13792
|
+
// File and here-string evaluators can decline on mutable input. Keep
|
|
13793
|
+
// these on the normal path, which can handle arbitrary input sizes.
|
|
13794
|
+
const def = w0Plain ? this.commands.get(w0Plain) : undefined;
|
|
13795
|
+
if (!w0Plain || (w0Plain !== "printf" && w0Plain !== "echo" && w0Plain !== "dirname" && w0Plain !== "basename" && w0Plain !== "seq") || !def || rawState.functions.has(w0Plain) || rawState.extensions?.builtins.has(w0Plain))
|
|
13825
13796
|
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))
|
|
13797
|
+
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
13798
|
return false;
|
|
13830
13799
|
if (!cmd.words.slice(1).every(w => this.isPureSyncValueWord(w, rawState)))
|
|
13831
13800
|
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
|
-
}
|
|
13801
|
+
// These substitutions consume one value per word. Unquoted expansions
|
|
13802
|
+
// can split, glob, or disappear, including after loop admission.
|
|
13803
|
+
if ((w0Plain === "echo" || w0Plain === "basename" || w0Plain === "dirname") &&
|
|
13804
|
+
!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)))))
|
|
13805
|
+
return false;
|
|
13936
13806
|
if (w0Plain === "printf") {
|
|
13937
13807
|
if (!this.isSyncPrintfCallOk(cmd, 1, rawState, this._activePrintfInductionName))
|
|
13938
13808
|
return false;
|
|
@@ -13972,138 +13842,18 @@ export class Runtime {
|
|
|
13972
13842
|
return false;
|
|
13973
13843
|
}
|
|
13974
13844
|
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
|
-
}
|
|
13845
|
+
// Literal operands cannot change after admission. Arithmetic and
|
|
13846
|
+
// variables use the normal path, where seq can report invalid input.
|
|
13847
|
+
if (cmd.words.length < 2 || cmd.words.length > 4)
|
|
13987
13848
|
return false;
|
|
13988
|
-
|
|
13989
|
-
if (
|
|
13849
|
+
const operands = cmd.words.slice(1).map(w => w.plain);
|
|
13850
|
+
if (!operands.every(value => value !== undefined && Number.isInteger(Number(value)) && String(Number(value)) === value && Math.abs(Number(value)) <= 9999999))
|
|
13990
13851
|
return false;
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13852
|
+
const first = operands.length === 1 ? 1 : Number(operands[0]);
|
|
13853
|
+
const increment = operands.length === 3 ? Number(operands[1]) : 1;
|
|
13854
|
+
const last = Number(operands[operands.length - 1]);
|
|
13855
|
+
if (increment === 0 || Math.abs((last - first) / increment) > 1024)
|
|
13994
13856
|
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
13857
|
}
|
|
14108
13858
|
continue;
|
|
14109
13859
|
}
|
|
@@ -14646,7 +14396,8 @@ export class Runtime {
|
|
|
14646
14396
|
return false;
|
|
14647
14397
|
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
14398
|
}
|
|
14649
|
-
|
|
14399
|
+
// A synchronous loop cannot hand off an unsupported pattern mid-iteration.
|
|
14400
|
+
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
14401
|
}
|
|
14651
14402
|
if (e.kind === "not")
|
|
14652
14403
|
return checkLeaf(e.operand);
|
|
@@ -14807,6 +14558,8 @@ export class Runtime {
|
|
|
14807
14558
|
// Nested step execution does not implement redirects; use normal execution.
|
|
14808
14559
|
if (nested && cmd.redirects.length !== 0)
|
|
14809
14560
|
return false;
|
|
14561
|
+
if (cmd.words.length === 0)
|
|
14562
|
+
return false;
|
|
14810
14563
|
if (cmd.redirects.length === 1) {
|
|
14811
14564
|
const r0Check = cmd.redirects[0];
|
|
14812
14565
|
const w0DevPlain = cmd.words[0].plain;
|
|
@@ -15480,22 +15233,13 @@ export class Runtime {
|
|
|
15480
15233
|
? whilePrintfInd
|
|
15481
15234
|
: (forPrintfInd && this.canSyncPrintfInductionBody(command.body, rawState, forPrintfInd) ? forPrintfInd : undefined));
|
|
15482
15235
|
const prevActivePrintfInd = this._activePrintfInductionName;
|
|
15483
|
-
const prevActiveLoopScript = this._activeLoopBodyScript;
|
|
15484
|
-
const prevActiveLoopCond = this._activeLoopCondScript;
|
|
15485
|
-
const prevActiveLoopVar = this._activeLoopVarName;
|
|
15486
15236
|
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
15237
|
let bodySyncOk = false;
|
|
15491
15238
|
try {
|
|
15492
15239
|
bodySyncOk = !(guestArrays(rawState) && (guestArrays(rawState).watches.size > 0 || this.budget.limits.maxExpansionBytes < 65536)) && this.canSyncLoopBody(command.body, rawState, io, false, printfInductionName);
|
|
15493
15240
|
}
|
|
15494
15241
|
finally {
|
|
15495
15242
|
this._activePrintfInductionName = prevActivePrintfInd;
|
|
15496
|
-
this._activeLoopBodyScript = prevActiveLoopScript;
|
|
15497
|
-
this._activeLoopCondScript = prevActiveLoopCond;
|
|
15498
|
-
this._activeLoopVarName = prevActiveLoopVar;
|
|
15499
15243
|
}
|
|
15500
15244
|
if (!bodySyncOk) {
|
|
15501
15245
|
command._skipTrySyncLoop = true;
|
|
@@ -16101,7 +15845,7 @@ export class Runtime {
|
|
|
16101
15845
|
!monitor.hasOverlay("BASH_REMATCH") &&
|
|
16102
15846
|
!(store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH"));
|
|
16103
15847
|
}
|
|
16104
|
-
return this.isPureSyncValueWord(e.left, rawState) && (e.operator === "==" || e.operator === "=" || e.operator === "!="
|
|
15848
|
+
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
15849
|
}
|
|
16106
15850
|
if (e.kind === "not")
|
|
16107
15851
|
return checkWhileCond(e.operand, depth + 1);
|
|
@@ -16432,7 +16176,7 @@ export class Runtime {
|
|
|
16432
16176
|
store.epoch = restEpoch;
|
|
16433
16177
|
return loopStatus;
|
|
16434
16178
|
}
|
|
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) {
|
|
16179
|
+
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
16180
|
return undefined;
|
|
16437
16181
|
}
|
|
16438
16182
|
const activeValScope = io[valueScope];
|
|
@@ -16931,187 +16675,6 @@ export class Runtime {
|
|
|
16931
16675
|
return pos <= 128 ? fastRedirectScratchViews[pos] : fastRedirectScratchBytes.subarray(0, pos);
|
|
16932
16676
|
}
|
|
16933
16677
|
_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
16678
|
canSyncPrintfInductionBody(script, rawState, inductionName) {
|
|
17116
16679
|
for (const list of script.lists)
|
|
17117
16680
|
for (const pipeline of list.pipelines)
|
|
@@ -17806,6 +17369,10 @@ export class Runtime {
|
|
|
17806
17369
|
return check(tree);
|
|
17807
17370
|
}
|
|
17808
17371
|
canSyncErrorArithmeticStmt(expr, rawState) {
|
|
17372
|
+
// Expansion can introduce array, attributed, or control-variable references.
|
|
17373
|
+
// Their reads and writes require the full arithmetic evaluator.
|
|
17374
|
+
if (expr.source.includes("$"))
|
|
17375
|
+
return false;
|
|
17809
17376
|
if (expr.source.includes("/") || expr.source.includes("%") || expr.source.includes("<<") || expr.source.includes(">>") || expr.source.includes("**") || expr.source.includes("["))
|
|
17810
17377
|
return false;
|
|
17811
17378
|
try {
|
|
@@ -17968,19 +17535,34 @@ export class Runtime {
|
|
|
17968
17535
|
const prevFastSubPos = this._fastSubPositional;
|
|
17969
17536
|
rawState.positional = fnArgs;
|
|
17970
17537
|
this._fastSubPositional = fnArgs;
|
|
17971
|
-
const savedLocals =
|
|
17538
|
+
const savedLocals = [];
|
|
17539
|
+
const seenLocals = new Set();
|
|
17972
17540
|
try {
|
|
17973
|
-
for (let i = 0; i < fnCall.locals.length;
|
|
17974
|
-
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
17982
|
-
|
|
17983
|
-
|
|
17541
|
+
for (let i = 0; i < fnCall.locals.length;) {
|
|
17542
|
+
this.budget.tick();
|
|
17543
|
+
let end = i + 1;
|
|
17544
|
+
while (end < fnCall.locals.length && !fnCall.locals[end].isFirstInCmd)
|
|
17545
|
+
end++;
|
|
17546
|
+
// Expand the entire simple command before any of its bindings change.
|
|
17547
|
+
const values = fnCall.locals.slice(i, end).map(loc => loc.valueWord === undefined
|
|
17548
|
+
? undefined
|
|
17549
|
+
: this.fastValueWord(loc.valueWord, rawState, io, false, false, false, false, 0, step.line));
|
|
17550
|
+
const start = i;
|
|
17551
|
+
for (; i < end; i++) {
|
|
17552
|
+
const loc = fnCall.locals[i];
|
|
17553
|
+
const alreadyLocal = seenLocals.has(loc.name);
|
|
17554
|
+
if (!alreadyLocal) {
|
|
17555
|
+
savedLocals.push({ name: loc.name, val: rawState.variables[loc.name], wasTouched: touched.has(loc.name) });
|
|
17556
|
+
seenLocals.add(loc.name);
|
|
17557
|
+
}
|
|
17558
|
+
const value = values[i - start];
|
|
17559
|
+
if (value !== undefined) {
|
|
17560
|
+
rawState.variables[loc.name] = value;
|
|
17561
|
+
touched.add(loc.name);
|
|
17562
|
+
}
|
|
17563
|
+
else if (!alreadyLocal) {
|
|
17564
|
+
delete rawState.variables[loc.name];
|
|
17565
|
+
}
|
|
17984
17566
|
}
|
|
17985
17567
|
}
|
|
17986
17568
|
if (fnCall.steps.length > 0) {
|
|
@@ -17988,11 +17570,12 @@ export class Runtime {
|
|
|
17988
17570
|
}
|
|
17989
17571
|
else {
|
|
17990
17572
|
rawState.status = 0;
|
|
17991
|
-
onUpdate({ lastCmd: step.cmd, lastArg: fnArgs.length > 0 ? fnArgs[fnArgs.length - 1] : fnCall.name });
|
|
17992
17573
|
}
|
|
17574
|
+
rawState.lastArgument = fnArgs.at(-1) ?? fnCall.name;
|
|
17575
|
+
onUpdate({ lastCmd: step.cmd, lastArg: rawState.lastArgument });
|
|
17993
17576
|
}
|
|
17994
17577
|
finally {
|
|
17995
|
-
for (let i =
|
|
17578
|
+
for (let i = savedLocals.length - 1; i >= 0; i--) {
|
|
17996
17579
|
const saved = savedLocals[i];
|
|
17997
17580
|
if (saved.val !== undefined) {
|
|
17998
17581
|
rawState.variables[saved.name] = saved.val;
|
|
@@ -21282,6 +20865,7 @@ export class Runtime {
|
|
|
21282
20865
|
}
|
|
21283
20866
|
if (hasThrown)
|
|
21284
20867
|
throw thrownError;
|
|
20868
|
+
state.lastArgument = pendingResume?.lastArgument ?? args.at(-1) ?? name;
|
|
21285
20869
|
return exitStatus;
|
|
21286
20870
|
}
|
|
21287
20871
|
finally {
|
|
@@ -21801,6 +21385,7 @@ export class Runtime {
|
|
|
21801
21385
|
}
|
|
21802
21386
|
if (outcome.kind === "throw")
|
|
21803
21387
|
throw outcome.reason;
|
|
21388
|
+
state.lastArgument = context.args.at(-1) ?? context.command;
|
|
21804
21389
|
return outcome.value;
|
|
21805
21390
|
}
|
|
21806
21391
|
if (selectedKind === "builtin" && !(state.externalInvocation && this.commands.has(context.command))) {
|
|
@@ -27162,6 +26747,10 @@ export class Runtime {
|
|
|
27162
26747
|
expandSyncTrCharArray(spec) {
|
|
27163
26748
|
if (spec.length === 0)
|
|
27164
26749
|
return undefined;
|
|
26750
|
+
// Non-ASCII sets require the byte-oriented tr parser.
|
|
26751
|
+
for (let i = 0; i < spec.length; i++)
|
|
26752
|
+
if (spec.charCodeAt(i) >= 128)
|
|
26753
|
+
return undefined;
|
|
27165
26754
|
// Escaped endpoints need the tr parser before range expansion.
|
|
27166
26755
|
if (spec.includes("\\") && spec.includes("-"))
|
|
27167
26756
|
return undefined;
|
|
@@ -27694,10 +27283,10 @@ export class Runtime {
|
|
|
27694
27283
|
if (!el || typeof el !== "object" || Array.isArray(el))
|
|
27695
27284
|
return undefined;
|
|
27696
27285
|
const rec = el;
|
|
27697
|
-
const k = rec.key
|
|
27286
|
+
const k = [rec.key, rec.Key, rec.name, rec.Name].find(value => value !== undefined && value !== null && value !== false);
|
|
27698
27287
|
if (typeof k !== "string")
|
|
27699
27288
|
return undefined;
|
|
27700
|
-
obj[k] = Object.hasOwn(rec, "value") ? rec.value : (Object.hasOwn(rec, "
|
|
27289
|
+
obj[k] = Object.hasOwn(rec, "value") ? rec.value : (Object.hasOwn(rec, "Value") ? rec.Value : null);
|
|
27701
27290
|
}
|
|
27702
27291
|
return [obj];
|
|
27703
27292
|
}
|
|
@@ -27730,7 +27319,7 @@ export class Runtime {
|
|
|
27730
27319
|
return [item.map(x => x === null ? "" : String(x)).join(arg)];
|
|
27731
27320
|
}
|
|
27732
27321
|
if (fn === "split")
|
|
27733
|
-
return typeof item === "string" ? [item.split(arg)] : undefined;
|
|
27322
|
+
return typeof item === "string" ? [arg === "" ? Array.from(item) : item.split(arg)] : undefined;
|
|
27734
27323
|
if (fn === "ltrimstr")
|
|
27735
27324
|
return typeof item === "string" ? [item.startsWith(arg) ? item.slice(arg.length) : item] : undefined;
|
|
27736
27325
|
if (fn === "rtrimstr")
|
|
@@ -27992,9 +27581,8 @@ export class Runtime {
|
|
|
27992
27581
|
break;
|
|
27993
27582
|
}
|
|
27994
27583
|
if (!a.startsWith("-") || a === "-") {
|
|
27995
|
-
operands.push(
|
|
27996
|
-
|
|
27997
|
-
continue;
|
|
27584
|
+
operands.push(...args.slice(idx));
|
|
27585
|
+
break;
|
|
27998
27586
|
}
|
|
27999
27587
|
if (a === "-a" || a === "--multiple") {
|
|
28000
27588
|
multiple = true;
|
|
@@ -28376,7 +27964,9 @@ export class Runtime {
|
|
|
28376
27964
|
}
|
|
28377
27965
|
if (st.kind === "=") {
|
|
28378
27966
|
out.push(String(idx1));
|
|
28379
|
-
|
|
27967
|
+
// Line numbers are generated records, always terminated, even when
|
|
27968
|
+
// the final input record is partial. Later prints can override this.
|
|
27969
|
+
lastInputIndex = -1;
|
|
28380
27970
|
continue;
|
|
28381
27971
|
}
|
|
28382
27972
|
if (st.kind === "q") {
|
|
@@ -28419,8 +28009,10 @@ export class Runtime {
|
|
|
28419
28009
|
}
|
|
28420
28010
|
return replacement;
|
|
28421
28011
|
});
|
|
28422
|
-
if (matchedOnce && st.printOnMatch)
|
|
28012
|
+
if (matchedOnce && st.printOnMatch) {
|
|
28423
28013
|
out.push(l);
|
|
28014
|
+
lastInputIndex = i;
|
|
28015
|
+
}
|
|
28424
28016
|
}
|
|
28425
28017
|
if (quitNow) {
|
|
28426
28018
|
break;
|
|
@@ -29606,22 +29198,16 @@ export class Runtime {
|
|
|
29606
29198
|
if (endField !== undefined && endField < startField)
|
|
29607
29199
|
return undefined;
|
|
29608
29200
|
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;
|
|
29201
|
+
if (kf.length > 0) {
|
|
29202
|
+
keyNum = kf.includes("n");
|
|
29203
|
+
keyHuman = kf.includes("h");
|
|
29204
|
+
keyMonth = kf.includes("M");
|
|
29205
|
+
keyDict = kf.includes("d");
|
|
29206
|
+
keyRev = kf.includes("r");
|
|
29207
|
+
keyFold = kf.includes("f");
|
|
29208
|
+
keyBlanks = kf.includes("b");
|
|
29209
|
+
keyVer = kf.includes("V");
|
|
29210
|
+
}
|
|
29625
29211
|
}
|
|
29626
29212
|
const extractKey = (l) => {
|
|
29627
29213
|
let raw = keySpec === undefined
|
|
@@ -29653,7 +29239,14 @@ export class Runtime {
|
|
|
29653
29239
|
raw = raw.replace(/^[ \t]+/, "");
|
|
29654
29240
|
if (keyDict)
|
|
29655
29241
|
raw = raw.replace(/[^a-zA-Z0-9 \t]+/g, "");
|
|
29656
|
-
|
|
29242
|
+
if (!keyFold)
|
|
29243
|
+
return raw;
|
|
29244
|
+
let folded = "";
|
|
29245
|
+
for (let i = 0; i < raw.length; i++) {
|
|
29246
|
+
const code = raw.charCodeAt(i);
|
|
29247
|
+
folded += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : raw[i];
|
|
29248
|
+
}
|
|
29249
|
+
return folded;
|
|
29657
29250
|
};
|
|
29658
29251
|
const parseNum = (s) => {
|
|
29659
29252
|
const m = /^[ \t]*(-?(?:\d+(?:\.\d*)?|\.\d+))/.exec(s);
|
|
@@ -29686,6 +29279,24 @@ export class Runtime {
|
|
|
29686
29279
|
const idx = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"].indexOf(m[1].toUpperCase());
|
|
29687
29280
|
return idx === -1 ? 0 : idx + 1;
|
|
29688
29281
|
};
|
|
29282
|
+
const encoded = new Map();
|
|
29283
|
+
const bytesOf = (line) => {
|
|
29284
|
+
let bytes = encoded.get(line);
|
|
29285
|
+
if (bytes === undefined) {
|
|
29286
|
+
bytes = fastSharedTextEncoder.encode(line);
|
|
29287
|
+
encoded.set(line, bytes);
|
|
29288
|
+
}
|
|
29289
|
+
return bytes;
|
|
29290
|
+
};
|
|
29291
|
+
const compareBytes = (left, right) => {
|
|
29292
|
+
const a = bytesOf(left);
|
|
29293
|
+
const b = bytesOf(right);
|
|
29294
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
29295
|
+
if (a[i] !== b[i])
|
|
29296
|
+
return a[i] - b[i];
|
|
29297
|
+
}
|
|
29298
|
+
return a.length - b.length;
|
|
29299
|
+
};
|
|
29689
29300
|
const sorted = [...rawLines].sort((a, b) => {
|
|
29690
29301
|
const ka = extractKey(a);
|
|
29691
29302
|
const kb = extractKey(b);
|
|
@@ -29712,15 +29323,13 @@ export class Runtime {
|
|
|
29712
29323
|
return keyRev ? -vc : vc;
|
|
29713
29324
|
}
|
|
29714
29325
|
else if (ka !== kb) {
|
|
29715
|
-
|
|
29326
|
+
const comparison = compareBytes(ka, kb);
|
|
29327
|
+
return keyRev ? -comparison : comparison;
|
|
29716
29328
|
}
|
|
29717
29329
|
if (uniq || stable)
|
|
29718
29330
|
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;
|
|
29331
|
+
const comparison = compareBytes(a, b);
|
|
29332
|
+
return rev ? -comparison : comparison;
|
|
29724
29333
|
});
|
|
29725
29334
|
if (!uniq)
|
|
29726
29335
|
return sorted;
|