@poe-platform/safe-bash 0.1.750 → 0.1.752
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-U7DRVFRD.js → chunk-W7QHCGDH.js} +42 -9
- package/dist/safe-bash/chunks/{src-YKSQQDD3.js → src-XCSZZ35R.js} +2 -2
- package/dist/safe-bash/commands/pr/index.browser.js +1 -1
- package/dist/safe-bash/commands/text.js +11 -3
- 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 +190 -171
- package/dist/safe-bash/shell/pattern.js +6 -1
- package/dist/safe-bash/shell/runtime.d.ts +3 -0
- package/dist/safe-bash/shell/runtime.js +246 -207
- package/dist/safe-bash-command-ssconvert/index.js +41 -8
- package/package.json +2 -2
|
@@ -1245,8 +1245,6 @@ class DiscardCommandFailure extends CommandFailure {
|
|
|
1245
1245
|
}
|
|
1246
1246
|
class ParameterExpansionFailure extends ExpansionFailure {
|
|
1247
1247
|
}
|
|
1248
|
-
class SubstringRangeFailure extends ExpansionFailure {
|
|
1249
|
-
}
|
|
1250
1248
|
class NounsetFailure extends ExpansionFailure {
|
|
1251
1249
|
status;
|
|
1252
1250
|
constructor(message, line, status = 1) {
|
|
@@ -9796,10 +9794,19 @@ export class Runtime {
|
|
|
9796
9794
|
// Matching publishes BASH_REMATCH. A sibling may still require fallback,
|
|
9797
9795
|
// so only a standalone match can be attempted without replaying writes.
|
|
9798
9796
|
if (expr.operator === "=~")
|
|
9799
|
-
return depth === 0 && this.extractSimpleErePattern(expr.right, rawState) !== undefined;
|
|
9797
|
+
return !requireTotal && depth === 0 && this.extractSimpleErePattern(expr.right, rawState) !== undefined;
|
|
9800
9798
|
if (expr.operator === "==" || expr.operator === "=" || expr.operator === "!=") {
|
|
9801
9799
|
if (expr.right.parts.length > 0 && expr.right.parts.every(p => p.quoted))
|
|
9802
9800
|
return true;
|
|
9801
|
+
if (requireTotal) {
|
|
9802
|
+
// Variable patterns can change during the loop. Only admit a static
|
|
9803
|
+
// pattern whose synchronous matcher is guaranteed to return a result.
|
|
9804
|
+
if (!expr.right.parts.every((p, idx) => p.kind === "text" && !p.byteValue && (p.quoted || idx > 0 || !p.value.startsWith("~"))))
|
|
9805
|
+
return false;
|
|
9806
|
+
const pattern = expr.right.parts.map(p => p.kind === "text" ? (p.quoted ? p.value.replace(/[\\*?[\]()]/g, "\\$&") : p.value) : "").join("");
|
|
9807
|
+
const work = { remaining: 4096, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
|
|
9808
|
+
return tryMatchesPatternSync(pattern, "", work, false, true) !== undefined;
|
|
9809
|
+
}
|
|
9803
9810
|
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)))
|
|
9804
9811
|
return true;
|
|
9805
9812
|
return false;
|
|
@@ -9988,7 +9995,10 @@ export class Runtime {
|
|
|
9988
9995
|
return undefined;
|
|
9989
9996
|
const syncTouched = this._syncArithRawWriteOnly ? this._syncArithTouched : undefined;
|
|
9990
9997
|
const lTrim = left.trim() || "0";
|
|
9991
|
-
|
|
9998
|
+
// Preserve all signed 64-bit literal digits instead of rounding through Number.
|
|
9999
|
+
let lNum = lTrim.length > 13 && /^-?[0-9]{1,19}$/.test(lTrim)
|
|
10000
|
+
? BigInt.asIntN(64, BigInt(lTrim))
|
|
10001
|
+
: resolveSimpleArithOperand(lTrim, rawState, monitor, store, syncTouched);
|
|
9992
10002
|
if (lNum === undefined) {
|
|
9993
10003
|
const lExpr = tryEvalSimpleExpandedArith(lTrim, rawState, monitor, store, syncTouched);
|
|
9994
10004
|
if (lExpr !== undefined)
|
|
@@ -9997,7 +10007,9 @@ export class Runtime {
|
|
|
9997
10007
|
if (lNum === undefined)
|
|
9998
10008
|
return undefined;
|
|
9999
10009
|
const rTrim = right.trim() || "0";
|
|
10000
|
-
let rNum =
|
|
10010
|
+
let rNum = rTrim.length > 13 && /^-?[0-9]{1,19}$/.test(rTrim)
|
|
10011
|
+
? BigInt.asIntN(64, BigInt(rTrim))
|
|
10012
|
+
: resolveSimpleArithOperand(rTrim, rawState, monitor, store, syncTouched);
|
|
10001
10013
|
if (rNum === undefined) {
|
|
10002
10014
|
const rExpr = tryEvalSimpleExpandedArith(rTrim, rawState, monitor, store, syncTouched);
|
|
10003
10015
|
if (rExpr !== undefined)
|
|
@@ -10005,6 +10017,10 @@ export class Runtime {
|
|
|
10005
10017
|
}
|
|
10006
10018
|
if (rNum === undefined)
|
|
10007
10019
|
return undefined;
|
|
10020
|
+
if (typeof lNum !== typeof rNum) {
|
|
10021
|
+
lNum = BigInt(lNum);
|
|
10022
|
+
rNum = BigInt(rNum);
|
|
10023
|
+
}
|
|
10008
10024
|
let ok;
|
|
10009
10025
|
switch (expr.operator) {
|
|
10010
10026
|
case "-eq":
|
|
@@ -13026,7 +13042,6 @@ export class Runtime {
|
|
|
13026
13042
|
rawState.substitutionStatus = 0;
|
|
13027
13043
|
if (rawState.variables._ !== undefined)
|
|
13028
13044
|
delete rawState.variables._;
|
|
13029
|
-
rawState.lastArgument = fnArgs.length > 0 ? fnArgs[fnArgs.length - 1] : w0Plain;
|
|
13030
13045
|
const positional = rawState.positional;
|
|
13031
13046
|
let fnArgsBytes = 0;
|
|
13032
13047
|
for (let i = 0; i < fnArgs.length; i++)
|
|
@@ -13240,7 +13255,6 @@ export class Runtime {
|
|
|
13240
13255
|
rawState.substitutionStatus = 0;
|
|
13241
13256
|
if (rawState.variables._ !== undefined)
|
|
13242
13257
|
delete rawState.variables._;
|
|
13243
|
-
rawState.lastArgument = lastCmdArg;
|
|
13244
13258
|
rawState.depth++;
|
|
13245
13259
|
let evalStatus;
|
|
13246
13260
|
try {
|
|
@@ -13250,6 +13264,7 @@ export class Runtime {
|
|
|
13250
13264
|
rawState.depth--;
|
|
13251
13265
|
}
|
|
13252
13266
|
if (typeof evalStatus === "number") {
|
|
13267
|
+
rawState.lastArgument = lastCmdArg;
|
|
13253
13268
|
if (cachedUnit.script.lists.length === 0) {
|
|
13254
13269
|
const activeStore = monitor.store;
|
|
13255
13270
|
const activeExisting = activeStore?.get("PIPESTATUS");
|
|
@@ -13589,6 +13604,11 @@ export class Runtime {
|
|
|
13589
13604
|
continue;
|
|
13590
13605
|
}
|
|
13591
13606
|
if (part.kind === "variable") {
|
|
13607
|
+
// Unsubscripted array references expand element zero through the normal
|
|
13608
|
+
// evaluator. fastValueWord cannot guarantee that expansion throughout
|
|
13609
|
+
// a loop, so reject it before executing any loop effects.
|
|
13610
|
+
if (stateMonitor(rawState)?.store?.get(part.name))
|
|
13611
|
+
return false;
|
|
13592
13612
|
// These expansions can require multiple fields or asynchronous quoting
|
|
13593
13613
|
// as loop state changes. Decide before the loop produces any effects.
|
|
13594
13614
|
if (this._syncLoopFnCheckDepth > 0 && (part.substring || getArraySelector(part)?.kind === "element"))
|
|
@@ -13653,21 +13673,17 @@ export class Runtime {
|
|
|
13653
13673
|
// their alternates rather than risking corruption or replay.
|
|
13654
13674
|
return false;
|
|
13655
13675
|
}
|
|
13656
|
-
|
|
13676
|
+
// Substrings and pattern/case operators can decline as loop values
|
|
13677
|
+
// change to Unicode or lengths become invalid. Loop steps cannot resume
|
|
13678
|
+
// after that decline without corrupting assignments or replaying effects.
|
|
13679
|
+
if (part.substring)
|
|
13680
|
+
return false;
|
|
13681
|
+
if (part.length || part.operator !== undefined) {
|
|
13657
13682
|
if (this.budget.limits.maxExpansionBytes !== Infinity || rawState.depth >= 32 || rawState.nounset || rawState.nocasematch || byteLocale(rawState.variables) !== false) {
|
|
13658
13683
|
return false;
|
|
13659
13684
|
}
|
|
13660
13685
|
if (part.length) {
|
|
13661
|
-
if (part.operator !== undefined
|
|
13662
|
-
return false;
|
|
13663
|
-
}
|
|
13664
|
-
else if (part.substring) {
|
|
13665
|
-
if (part.operator !== undefined || !part.substring.offset.parts.every(p => p.kind === "text" || (p.kind === "variable" && !p.operator && !p.substring && !p.length)) || (part.substring.length && !part.substring.length.parts.every(p => p.kind === "text" || (p.kind === "variable" && !p.operator && !p.substring && !p.length)))) {
|
|
13666
|
-
return false;
|
|
13667
|
-
}
|
|
13668
|
-
}
|
|
13669
|
-
else if (part.operator === "^" || part.operator === "^^" || part.operator === "," || part.operator === ",,") {
|
|
13670
|
-
if (part.alternate && part.alternate.parts.length > 0)
|
|
13686
|
+
if (part.operator !== undefined)
|
|
13671
13687
|
return false;
|
|
13672
13688
|
}
|
|
13673
13689
|
else if (part.operator === "-" || part.operator === ":-" || part.operator === "+" || part.operator === ":+") {
|
|
@@ -13676,29 +13692,6 @@ export class Runtime {
|
|
|
13676
13692
|
if (!this.isPureSyncValueWord(part.alternate, rawState, allowAt))
|
|
13677
13693
|
return false;
|
|
13678
13694
|
}
|
|
13679
|
-
else if (part.operator === "#" || part.operator === "##" || part.operator === "%" || part.operator === "%%") {
|
|
13680
|
-
if (!part.alternate || part.alternate.parts.length === 0)
|
|
13681
|
-
return false;
|
|
13682
|
-
const q0 = part.alternate.parts[0].quoted;
|
|
13683
|
-
const trimOp = part.operator;
|
|
13684
|
-
if (!part.alternate.parts.every((p, idx) => p.quoted === q0 && ((p.kind === "text" && !p.byteValue && (p.quoted || !hasGlobOrEscape(p.value, !!rawState.extglob) || (idx === 0 && (trimOp === "#" || trimOp === "##") && p.value === "*") || (idx === part.alternate.parts.length - 1 && (trimOp === "%" || trimOp === "%%") && p.value === "*") || (part.alternate.parts.length === 1 && Boolean(tryCompileTrimGlobToRegex(p.value, trimOp, !!rawState.extglob))))) || (p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && (getArraySelector(p) === undefined || (p.name === "BASH_REMATCH" && getArraySelector(p)?.kind === "element" && /^[0-9]{1,2}$/.test(getArraySelector(p).index.decimal))) && isShellIdentifier(p.name))))) {
|
|
13685
|
-
return false;
|
|
13686
|
-
}
|
|
13687
|
-
}
|
|
13688
|
-
else if (part.operator === "/" || part.operator === "//" || part.operator === "/#" || part.operator === "/%") {
|
|
13689
|
-
if (!part.alternate || part.alternate.parts.length === 0)
|
|
13690
|
-
return false;
|
|
13691
|
-
const q0 = part.alternate.parts[0].quoted;
|
|
13692
|
-
const repOp = part.operator;
|
|
13693
|
-
if (!part.alternate.parts.every(p => p.quoted === q0 && ((p.kind === "text" && !p.byteValue && (p.quoted || !hasGlobOrEscape(p.value, !!rawState.extglob) || (part.alternate.parts.length === 1 && Boolean(tryCompileFixedGlobToRegex(p.value, repOp, !!rawState.extglob))))) || (p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && getArraySelector(p) === undefined && isShellIdentifier(p.name))))) {
|
|
13694
|
-
return false;
|
|
13695
|
-
}
|
|
13696
|
-
if (part.replacement && part.replacement.parts.length > 0) {
|
|
13697
|
-
if (!part.replacement.parts.every(p => ((p.kind === "text" && !p.byteValue && !p.value.includes("&") && !p.value.includes("\\") && (p.quoted || !p.value.startsWith("~"))) || (p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && getArraySelector(p) === undefined && isShellIdentifier(p.name))))) {
|
|
13698
|
-
return false;
|
|
13699
|
-
}
|
|
13700
|
-
}
|
|
13701
|
-
}
|
|
13702
13695
|
else
|
|
13703
13696
|
return false;
|
|
13704
13697
|
}
|
|
@@ -13808,14 +13801,8 @@ export class Runtime {
|
|
|
13808
13801
|
return false;
|
|
13809
13802
|
}
|
|
13810
13803
|
else if (w0Plain === "echo") {
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
const prefix = firstArg.parts.find(part => part.kind !== "text" || part.value.length > 0);
|
|
13814
|
-
// Substitution execution rejects options, including those that appear
|
|
13815
|
-
// only on later iterations. Require a stable non-option prefix.
|
|
13816
|
-
if (prefix?.kind !== "text" || prefix.value.startsWith("-"))
|
|
13817
|
-
return false;
|
|
13818
|
-
}
|
|
13804
|
+
if (!this.isSyncSubEchoCallOk(cmd, rawState))
|
|
13805
|
+
return false;
|
|
13819
13806
|
}
|
|
13820
13807
|
else if (w0Plain === "dirname" || w0Plain === "basename") {
|
|
13821
13808
|
const simArgs = [];
|
|
@@ -13887,11 +13874,11 @@ export class Runtime {
|
|
|
13887
13874
|
if (isNumOp && checkIntVars) {
|
|
13888
13875
|
const isGuaranteedIntWord = (w) => {
|
|
13889
13876
|
if (w.plain !== undefined)
|
|
13890
|
-
return /^-?[0-9]
|
|
13877
|
+
return /^-?[0-9]{1,16}$/.test(w.plain);
|
|
13891
13878
|
if (w.parts.length === 1) {
|
|
13892
13879
|
const p0 = w.parts[0];
|
|
13893
13880
|
if (p0.kind === "arithmetic")
|
|
13894
|
-
return
|
|
13881
|
+
return false;
|
|
13895
13882
|
if (p0.kind === "variable" && !p0.indirect && !p0.prefixNames && !p0.substring && !p0.transform && p0.operator === undefined && getArraySelector(p0) === undefined) {
|
|
13896
13883
|
if (script) {
|
|
13897
13884
|
for (const lst of script.lists) {
|
|
@@ -13899,14 +13886,14 @@ export class Runtime {
|
|
|
13899
13886
|
const c0 = pl.commands[0];
|
|
13900
13887
|
if (c0?.kind === "simple" && c0.words.length === 1) {
|
|
13901
13888
|
const asg = this.assignment(c0.words[0]);
|
|
13902
|
-
if (asg?.name === p0.name && (!asg.value.plain || !/^-?[0-9]
|
|
13889
|
+
if (asg?.name === p0.name && (!asg.value.plain || !/^-?[0-9]{1,16}$/.test(asg.value.plain)))
|
|
13903
13890
|
return false;
|
|
13904
13891
|
}
|
|
13905
13892
|
}
|
|
13906
13893
|
}
|
|
13907
13894
|
}
|
|
13908
13895
|
const cur = rawState.variables[p0.name];
|
|
13909
|
-
return cur === undefined || /^-?[0-9]
|
|
13896
|
+
return cur === undefined || /^-?[0-9]{1,16}$/.test(cur);
|
|
13910
13897
|
}
|
|
13911
13898
|
}
|
|
13912
13899
|
return false;
|
|
@@ -14034,6 +14021,10 @@ export class Runtime {
|
|
|
14034
14021
|
return true;
|
|
14035
14022
|
if (words.length === 0 || words.length > 64)
|
|
14036
14023
|
return false;
|
|
14024
|
+
// Slice expressions and member transformations may stop being synchronous
|
|
14025
|
+
// after body mutations. Preserve member expansion through the normal path.
|
|
14026
|
+
if (words.some(w => w.parts.some(p => p.kind === "variable" && (p.substring || p.operator !== undefined || p.transform))))
|
|
14027
|
+
return false;
|
|
14037
14028
|
if (words.length === 1 && this.isQuotedAtWord(words[0]))
|
|
14038
14029
|
return true;
|
|
14039
14030
|
// Unquoted expansions need splitting and globbing against the state at
|
|
@@ -14075,9 +14066,10 @@ export class Runtime {
|
|
|
14075
14066
|
}
|
|
14076
14067
|
if (words.length === 1) {
|
|
14077
14068
|
const w0 = words[0];
|
|
14078
|
-
|
|
14079
|
-
|
|
14080
|
-
return
|
|
14069
|
+
if (this.canSyncArrayMembersWord(w0, rawState)) {
|
|
14070
|
+
// Member words must never degrade into a single joined scalar word.
|
|
14071
|
+
return this.tryExpandSyncArrayMembersWord(w0, rawState, io, line);
|
|
14072
|
+
}
|
|
14081
14073
|
const p0 = w0.parts.length === 1 ? w0.parts[0] : (w0.parts.length === 2 && w0.parts[0].kind === "text" && w0.parts[0].value === "" ? w0.parts[1] : undefined);
|
|
14082
14074
|
if (p0 && !p0.quoted && (rawState.variables.IFS === undefined || rawState.variables.IFS === " \t\n") && !rawState.noglob) {
|
|
14083
14075
|
if (p0.kind === "variable" && !p0.indirect && !p0.prefixNames && !p0.length && !p0.transform && getArraySelector(p0) === undefined) {
|
|
@@ -14307,6 +14299,45 @@ export class Runtime {
|
|
|
14307
14299
|
}
|
|
14308
14300
|
return { ifsWord, varNames, isBareReply, inputWord: r0.target, ...(delimChar !== undefined ? { delimChar } : {}), ...(maxChars !== undefined ? { maxChars } : {}), ...(exactChars !== undefined ? { exactChars } : {}) };
|
|
14309
14301
|
}
|
|
14302
|
+
canSyncLoopConditionalOperands(expression, rawState, allowRegex = true) {
|
|
14303
|
+
const checkLeaf = (e) => {
|
|
14304
|
+
if (e.kind === "nonempty")
|
|
14305
|
+
return this.isPureSyncValueWord(e.operand, rawState);
|
|
14306
|
+
if (e.kind === "unary")
|
|
14307
|
+
return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v" || (syncConditionalFileUnaryOps.has(e.operator) && this._isMemoryBackingFs && this.canFastMemoryRedirect && this.budget.limits.maxFileSystemOperations >= 1000 && (this._fileWrites === undefined || this._fileWrites.size === 0) && (this._outputFiles === undefined || this._outputFiles.size === 0))) && this.isPureSyncValueWord(e.operand, rawState);
|
|
14308
|
+
if (e.kind === "binary") {
|
|
14309
|
+
if (e.operator === "=~") {
|
|
14310
|
+
if (!allowRegex)
|
|
14311
|
+
return false;
|
|
14312
|
+
const patInfo = this.extractSimpleErePattern(e.right, rawState);
|
|
14313
|
+
// A variable pattern can change through expansions or called functions.
|
|
14314
|
+
if (patInfo?.isVar)
|
|
14315
|
+
return false;
|
|
14316
|
+
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") && !(stateMonitor(rawState)?.store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH");
|
|
14317
|
+
}
|
|
14318
|
+
// A synchronous loop cannot hand off an unsupported pattern mid-iteration.
|
|
14319
|
+
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)));
|
|
14320
|
+
}
|
|
14321
|
+
if (e.kind === "not")
|
|
14322
|
+
return checkLeaf(e.operand);
|
|
14323
|
+
if (e.kind === "and" || e.kind === "or")
|
|
14324
|
+
return checkLeaf(e.left) && checkLeaf(e.right);
|
|
14325
|
+
return false;
|
|
14326
|
+
};
|
|
14327
|
+
return checkLeaf(expression);
|
|
14328
|
+
}
|
|
14329
|
+
isSyncSubEchoCallOk(cmd, rawState) {
|
|
14330
|
+
if (!this.isSyncEchoCallOk(cmd, rawState))
|
|
14331
|
+
return false;
|
|
14332
|
+
if (!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)))))
|
|
14333
|
+
return false;
|
|
14334
|
+
const firstArg = cmd.words[1];
|
|
14335
|
+
if (!firstArg)
|
|
14336
|
+
return true;
|
|
14337
|
+
const prefix = firstArg.parts.find(part => part.kind !== "text" || part.value.length > 0);
|
|
14338
|
+
// Substitution execution cannot resume when an option appears mid-loop.
|
|
14339
|
+
return prefix?.kind === "text" && !prefix.value.startsWith("-");
|
|
14340
|
+
}
|
|
14310
14341
|
isPureSyncSubIfOrCase(cmd, rawState) {
|
|
14311
14342
|
if (cmd.redirects.length !== 0)
|
|
14312
14343
|
return false;
|
|
@@ -14321,7 +14352,7 @@ export class Runtime {
|
|
|
14321
14352
|
return false;
|
|
14322
14353
|
const w0 = sc.words[0]?.plain;
|
|
14323
14354
|
if (w0 === "echo")
|
|
14324
|
-
return this.
|
|
14355
|
+
return this.isSyncSubEchoCallOk(sc, rawState);
|
|
14325
14356
|
if (w0 === "printf" && sc.words[1]?.plain !== "-v")
|
|
14326
14357
|
return this.isSyncPrintfCallOk(sc, 1, rawState);
|
|
14327
14358
|
return false;
|
|
@@ -14341,11 +14372,13 @@ export class Runtime {
|
|
|
14341
14372
|
else if (bc.kind === "conditional") {
|
|
14342
14373
|
if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState, true))
|
|
14343
14374
|
return false;
|
|
14344
|
-
if (
|
|
14375
|
+
if (!this.canSyncLoopConditionalOperands(bc.expression, rawState, false))
|
|
14345
14376
|
return false;
|
|
14346
14377
|
}
|
|
14347
|
-
else
|
|
14348
|
-
|
|
14378
|
+
else {
|
|
14379
|
+
const expression = this.extractPosixBracketCondExpr(bc, rawState, true, br.body);
|
|
14380
|
+
if (!expression || !this.canSyncLoopConditionalOperands(expression, rawState, false))
|
|
14381
|
+
return false;
|
|
14349
14382
|
}
|
|
14350
14383
|
if (!isSimpleEchoOrPrintf(br.body))
|
|
14351
14384
|
return false;
|
|
@@ -14383,29 +14416,7 @@ export class Runtime {
|
|
|
14383
14416
|
const canSyncLoopCondCmd = (c) => {
|
|
14384
14417
|
if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState, true))
|
|
14385
14418
|
return false;
|
|
14386
|
-
|
|
14387
|
-
if (e.kind === "nonempty")
|
|
14388
|
-
return this.isPureSyncValueWord(e.operand, rawState);
|
|
14389
|
-
if (e.kind === "unary")
|
|
14390
|
-
return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v" || (syncConditionalFileUnaryOps.has(e.operator) && this._isMemoryBackingFs && this.canFastMemoryRedirect && this.budget.limits.maxFileSystemOperations >= 1000 && (this._fileWrites === undefined || this._fileWrites.size === 0) && (this._outputFiles === undefined || this._outputFiles.size === 0))) && this.isPureSyncValueWord(e.operand, rawState);
|
|
14391
|
-
if (e.kind === "binary") {
|
|
14392
|
-
if (e.operator === "=~") {
|
|
14393
|
-
const patInfo = this.extractSimpleErePattern(e.right, rawState);
|
|
14394
|
-
// A variable pattern can change through expansions or called functions.
|
|
14395
|
-
if (patInfo?.isVar)
|
|
14396
|
-
return false;
|
|
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");
|
|
14398
|
-
}
|
|
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)));
|
|
14401
|
-
}
|
|
14402
|
-
if (e.kind === "not")
|
|
14403
|
-
return checkLeaf(e.operand);
|
|
14404
|
-
if (e.kind === "and" || e.kind === "or")
|
|
14405
|
-
return checkLeaf(e.left) && checkLeaf(e.right);
|
|
14406
|
-
return false;
|
|
14407
|
-
};
|
|
14408
|
-
return checkLeaf(c.expression);
|
|
14419
|
+
return this.canSyncLoopConditionalOperands(c.expression, rawState);
|
|
14409
14420
|
};
|
|
14410
14421
|
if (cmd.kind === "arithmetic") {
|
|
14411
14422
|
if (cmd.redirects.length === 0) {
|
|
@@ -14577,11 +14588,16 @@ export class Runtime {
|
|
|
14577
14588
|
(w0DevPlain === "pwd" || this.commands.has(w0DevPlain))) {
|
|
14578
14589
|
const devWordOk = (w) => this.isPureSyncValueWord(w, rawState) &&
|
|
14579
14590
|
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)));
|
|
14591
|
+
// Only a nonempty literal prefix proves an expansion cannot become an option.
|
|
14592
|
+
const devOperandOk = (w) => {
|
|
14593
|
+
const first = w.parts[0];
|
|
14594
|
+
return first?.kind === "text" && first.value.length > 0 && !first.value.startsWith("-") && devWordOk(w);
|
|
14595
|
+
};
|
|
14580
14596
|
const devShapeOk = (w0DevPlain === "pwd" && cmd.words.length === 1) ||
|
|
14581
|
-
((w0DevPlain === "dirname" || w0DevPlain === "echo") && cmd.words.length === 2 &&
|
|
14582
|
-
(w0DevPlain === "basename" && ((cmd.words.length === 2 &&
|
|
14583
|
-
(cmd.words.length === 3 &&
|
|
14584
|
-
(cmd.words.length === 4 && cmd.words[1].plain === "-s" && cmd.words[2].plain !== undefined &&
|
|
14597
|
+
((w0DevPlain === "dirname" || w0DevPlain === "echo") && cmd.words.length === 2 && devOperandOk(cmd.words[1])) ||
|
|
14598
|
+
(w0DevPlain === "basename" && ((cmd.words.length === 2 && devOperandOk(cmd.words[1])) ||
|
|
14599
|
+
(cmd.words.length === 3 && devOperandOk(cmd.words[1]) && devWordOk(cmd.words[2])) ||
|
|
14600
|
+
(cmd.words.length === 4 && cmd.words[1].plain === "-s" && cmd.words[2].plain !== undefined && devOperandOk(cmd.words[3]))));
|
|
14585
14601
|
if (devShapeOk)
|
|
14586
14602
|
continue;
|
|
14587
14603
|
}
|
|
@@ -14722,9 +14738,9 @@ export class Runtime {
|
|
|
14722
14738
|
return false;
|
|
14723
14739
|
}
|
|
14724
14740
|
const assignment = this.assignment(w0);
|
|
14725
|
-
// Eligibility
|
|
14726
|
-
//
|
|
14727
|
-
if (assignment && (assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG"))
|
|
14741
|
+
// Eligibility uses the entry locale and IFS. Mutations can require
|
|
14742
|
+
// field splitting or byte-aware expansion during a later iteration.
|
|
14743
|
+
if (assignment && (assignment.name === "IFS" || assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG"))
|
|
14728
14744
|
return false;
|
|
14729
14745
|
if (!assignment || (assignment.append && this.budget.limits.maxExpansionBytes !== Infinity) || assignment.name === "OPTIND" || assignment.name === "PIPESTATUS" || assignment.name.includes("[") || rawState.readonlyVariables?.has(assignment.name) || rawState.variableAttributes?.get(assignment.name) || store?.get(assignment.name) || !this.isPureSyncValueWord(assignment.value, rawState)) {
|
|
14730
14746
|
return false;
|
|
@@ -15705,7 +15721,8 @@ export class Runtime {
|
|
|
15705
15721
|
}
|
|
15706
15722
|
else {
|
|
15707
15723
|
monitor.values.invalidate(varName);
|
|
15708
|
-
|
|
15724
|
+
if (!rawState.exported.has(varName))
|
|
15725
|
+
monitor.proxy.exported.delete(varName);
|
|
15709
15726
|
}
|
|
15710
15727
|
}
|
|
15711
15728
|
}
|
|
@@ -16146,7 +16163,8 @@ export class Runtime {
|
|
|
16146
16163
|
}
|
|
16147
16164
|
else {
|
|
16148
16165
|
monitor.values.invalidate(varName);
|
|
16149
|
-
|
|
16166
|
+
if (!rawState.exported.has(varName))
|
|
16167
|
+
monitor.proxy.exported.delete(varName);
|
|
16150
16168
|
}
|
|
16151
16169
|
}
|
|
16152
16170
|
}
|
|
@@ -16686,7 +16704,24 @@ export class Runtime {
|
|
|
16686
16704
|
}
|
|
16687
16705
|
if (command.kind !== "simple" || hasShellFunction(rawState, command.words[0]?.plain ?? "") || command.words.some(word => getArrayAssignment(word) !== undefined))
|
|
16688
16706
|
return false;
|
|
16689
|
-
|
|
16707
|
+
let commandIndex = 0;
|
|
16708
|
+
while (commandIndex < command.words.length && this.assignment(command.words[commandIndex]))
|
|
16709
|
+
commandIndex++;
|
|
16710
|
+
const commandWord = command.words[commandIndex];
|
|
16711
|
+
if (!commandWord?.parts.every(part => part.kind === "text"))
|
|
16712
|
+
return false;
|
|
16713
|
+
const w0 = commandWord.parts.map(part => part.kind === "text" ? part.value : "").join("");
|
|
16714
|
+
// read targets and printf -v targets may be quoted or expanded. Both can
|
|
16715
|
+
// invalidate the integer assumption used by induction formatting.
|
|
16716
|
+
if (w0 === "read")
|
|
16717
|
+
return false;
|
|
16718
|
+
if (w0 === "printf") {
|
|
16719
|
+
const option = command.words[commandIndex + 1];
|
|
16720
|
+
if (!option?.parts.every(part => part.kind === "text"))
|
|
16721
|
+
return false;
|
|
16722
|
+
if (option.parts.map(part => part.kind === "text" ? part.value : "").join("") === "-v")
|
|
16723
|
+
return false;
|
|
16724
|
+
}
|
|
16690
16725
|
if (w0 === "let" || w0 === "declare" || w0 === "typeset" || w0 === "local" || w0 === "export" || w0 === "unset" || w0 === "mapfile" || w0 === "readarray")
|
|
16691
16726
|
return false;
|
|
16692
16727
|
if (inductionName !== undefined) {
|
|
@@ -16695,10 +16730,6 @@ export class Runtime {
|
|
|
16695
16730
|
return p0?.kind === "text" && !p0.quoted && (p0.value.startsWith(inductionName + "=") || p0.value.startsWith(inductionName + "+="));
|
|
16696
16731
|
}))
|
|
16697
16732
|
return false;
|
|
16698
|
-
if (w0 === "read" && (inductionName === "REPLY" || command.words.slice(1).some(w => w.plain === inductionName || w.plain === "-a" || w.plain === "-ra" || w.plain === "-ar")))
|
|
16699
|
-
return false;
|
|
16700
|
-
if (w0 === "printf" && command.words[1]?.plain === "-v" && command.words[2]?.plain === inductionName)
|
|
16701
|
-
return false;
|
|
16702
16733
|
}
|
|
16703
16734
|
for (const word of [...command.words, ...command.redirects.map(redirect => redirect.target)])
|
|
16704
16735
|
for (const part of word.parts) {
|
|
@@ -16977,6 +17008,7 @@ export class Runtime {
|
|
|
16977
17008
|
execSyncLocalDeclStep(decl, rawState, io, monitor, touched, line) {
|
|
16978
17009
|
const locals = (decl.kind === "local" || decl.kind === "declare") && rawState.locals.length > 0 ? rawState.locals[rawState.locals.length - 1] : undefined;
|
|
16979
17010
|
let lastArg = decl.kind;
|
|
17011
|
+
const values = decl.items.map(item => item.value === undefined ? undefined : this.fastValueWord(item.value, rawState, io, false, false, false, rawState.braceexpand !== false, 0, line));
|
|
16980
17012
|
for (let i = 0; i < decl.items.length; i++) {
|
|
16981
17013
|
const item = decl.items[i];
|
|
16982
17014
|
const isNewLocal = locals ? !locals.has(item.name) : false;
|
|
@@ -16994,7 +17026,7 @@ export class Runtime {
|
|
|
16994
17026
|
touched.add(item.name);
|
|
16995
17027
|
}
|
|
16996
17028
|
if (item.value !== undefined) {
|
|
16997
|
-
const val =
|
|
17029
|
+
const val = values[i];
|
|
16998
17030
|
if (item.append) {
|
|
16999
17031
|
const curRaw = rawState.variables[item.name];
|
|
17000
17032
|
const curStr = curRaw === undefined ? "" : (touched.has(item.name) ? curRaw : (monitor.values.get(item.name, curRaw) ?? curRaw));
|
|
@@ -17004,7 +17036,7 @@ export class Runtime {
|
|
|
17004
17036
|
rawState.variables[item.name] = val;
|
|
17005
17037
|
}
|
|
17006
17038
|
touched.add(item.name);
|
|
17007
|
-
lastArg = `${item.name}
|
|
17039
|
+
lastArg = `${item.name}${item.append ? "+=" : "="}${val}`;
|
|
17008
17040
|
}
|
|
17009
17041
|
else {
|
|
17010
17042
|
lastArg = item.name;
|
|
@@ -17103,7 +17135,28 @@ export class Runtime {
|
|
|
17103
17135
|
if (this._syncStdoutBatch.length >= 8192)
|
|
17104
17136
|
this.flushSyncStdoutBatch(io);
|
|
17105
17137
|
}
|
|
17138
|
+
runSyncDiscardStep(step, rawState, io) {
|
|
17139
|
+
this.budget.fileSystemOperation();
|
|
17140
|
+
const args = step.discardWords.map(word => this.fastValueWord(word, rawState, io, false, false, false, false, 0, step.line));
|
|
17141
|
+
const cmdName = step.cmd.words[0].plain;
|
|
17142
|
+
let output;
|
|
17143
|
+
if (cmdName === "pwd")
|
|
17144
|
+
output = rawState.variables.PWD ?? rawState.cwd;
|
|
17145
|
+
else if (cmdName === "echo")
|
|
17146
|
+
output = args[0];
|
|
17147
|
+
else if (cmdName === "dirname")
|
|
17148
|
+
output = this.evalSyncDirname(args);
|
|
17149
|
+
else
|
|
17150
|
+
output = this.evalSyncBasename(step.discardSuffix === undefined ? args : ["-s", step.discardSuffix, ...args]);
|
|
17151
|
+
const bytes = shellValueByteLength(output) + 1;
|
|
17152
|
+
if (this.budget.bytes + bytes > this.budget.maxOutputBytesSmi && bytes > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
17153
|
+
this.budget.fail("maxOutputBytes");
|
|
17154
|
+
this.budget.bytes += bytes;
|
|
17155
|
+
return args[args.length - 1] ?? cmdName;
|
|
17156
|
+
}
|
|
17106
17157
|
writeSyncRedirectStep(step, encoded, append, mode, rawState, io) {
|
|
17158
|
+
if (this.budget.bytes + encoded.byteLength > this.budget.maxOutputBytesSmi && encoded.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
17159
|
+
this.budget.fail("maxOutputBytes");
|
|
17107
17160
|
// The memory fast writer may retain its input; the encoder scratch is reused.
|
|
17108
17161
|
encoded = encoded.slice();
|
|
17109
17162
|
if (step.targetDirPrefix !== undefined && step.targetNamePrefix !== undefined) {
|
|
@@ -17267,31 +17320,7 @@ export class Runtime {
|
|
|
17267
17320
|
lastArg = pLastArg;
|
|
17268
17321
|
}
|
|
17269
17322
|
else if (step.isDiscardDevNull && step.discardWords !== undefined) {
|
|
17270
|
-
this.
|
|
17271
|
-
const dw = step.discardWords;
|
|
17272
|
-
const cmdName = step.cmd.words[0].plain;
|
|
17273
|
-
let evalFirst = "";
|
|
17274
|
-
let evalLast = cmdName;
|
|
17275
|
-
for (let wi = 0; wi < dw.length; wi++) {
|
|
17276
|
-
const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
|
|
17277
|
-
if (wi === 0)
|
|
17278
|
-
evalFirst = v;
|
|
17279
|
-
evalLast = v;
|
|
17280
|
-
}
|
|
17281
|
-
let outChars = 0;
|
|
17282
|
-
if (cmdName === "pwd")
|
|
17283
|
-
outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
|
|
17284
|
-
else if (cmdName === "echo")
|
|
17285
|
-
outChars = evalFirst.length + 1;
|
|
17286
|
-
else if (cmdName === "dirname")
|
|
17287
|
-
outChars = dirname(evalFirst).length + 1;
|
|
17288
|
-
else if (cmdName === "basename") {
|
|
17289
|
-
const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
|
|
17290
|
-
const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
|
|
17291
|
-
outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
|
|
17292
|
-
}
|
|
17293
|
-
this.budget.bytes += outChars;
|
|
17294
|
-
lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
|
|
17323
|
+
lastArg = this.runSyncDiscardStep(step, rawState, io);
|
|
17295
17324
|
}
|
|
17296
17325
|
else if (step.name !== undefined && step.value !== undefined) {
|
|
17297
17326
|
rawState.substitutionStatus = 0;
|
|
@@ -17851,31 +17880,7 @@ export class Runtime {
|
|
|
17851
17880
|
lastArg = pLastArg;
|
|
17852
17881
|
}
|
|
17853
17882
|
else if (step.isDiscardDevNull && step.discardWords !== undefined) {
|
|
17854
|
-
this.
|
|
17855
|
-
const dw = step.discardWords;
|
|
17856
|
-
const cmdName = step.cmd.words[0].plain;
|
|
17857
|
-
let evalFirst = "";
|
|
17858
|
-
let evalLast = cmdName;
|
|
17859
|
-
for (let wi = 0; wi < dw.length; wi++) {
|
|
17860
|
-
const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
|
|
17861
|
-
if (wi === 0)
|
|
17862
|
-
evalFirst = v;
|
|
17863
|
-
evalLast = v;
|
|
17864
|
-
}
|
|
17865
|
-
let outChars = 0;
|
|
17866
|
-
if (cmdName === "pwd")
|
|
17867
|
-
outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
|
|
17868
|
-
else if (cmdName === "echo")
|
|
17869
|
-
outChars = evalFirst.length + 1;
|
|
17870
|
-
else if (cmdName === "dirname")
|
|
17871
|
-
outChars = dirname(evalFirst).length + 1;
|
|
17872
|
-
else if (cmdName === "basename") {
|
|
17873
|
-
const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
|
|
17874
|
-
const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
|
|
17875
|
-
outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
|
|
17876
|
-
}
|
|
17877
|
-
this.budget.bytes += outChars;
|
|
17878
|
-
lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
|
|
17883
|
+
lastArg = this.runSyncDiscardStep(step, rawState, io);
|
|
17879
17884
|
}
|
|
17880
17885
|
else if (step.name !== undefined && step.value !== undefined) {
|
|
17881
17886
|
rawState.substitutionStatus = 0;
|
|
@@ -18031,31 +18036,7 @@ export class Runtime {
|
|
|
18031
18036
|
lastArg = pLastArg;
|
|
18032
18037
|
}
|
|
18033
18038
|
else if (step.isDiscardDevNull && step.discardWords !== undefined) {
|
|
18034
|
-
this.
|
|
18035
|
-
const dw = step.discardWords;
|
|
18036
|
-
const cmdName = step.cmd.words[0].plain;
|
|
18037
|
-
let evalFirst = "";
|
|
18038
|
-
let evalLast = cmdName;
|
|
18039
|
-
for (let wi = 0; wi < dw.length; wi++) {
|
|
18040
|
-
const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
|
|
18041
|
-
if (wi === 0)
|
|
18042
|
-
evalFirst = v;
|
|
18043
|
-
evalLast = v;
|
|
18044
|
-
}
|
|
18045
|
-
let outChars = 0;
|
|
18046
|
-
if (cmdName === "pwd")
|
|
18047
|
-
outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
|
|
18048
|
-
else if (cmdName === "echo")
|
|
18049
|
-
outChars = evalFirst.length + 1;
|
|
18050
|
-
else if (cmdName === "dirname")
|
|
18051
|
-
outChars = dirname(evalFirst).length + 1;
|
|
18052
|
-
else if (cmdName === "basename") {
|
|
18053
|
-
const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
|
|
18054
|
-
const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
|
|
18055
|
-
outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
|
|
18056
|
-
}
|
|
18057
|
-
this.budget.bytes += outChars;
|
|
18058
|
-
lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
|
|
18039
|
+
lastArg = this.runSyncDiscardStep(step, rawState, io);
|
|
18059
18040
|
}
|
|
18060
18041
|
else if (step.name !== undefined && step.value !== undefined) {
|
|
18061
18042
|
rawState.substitutionStatus = 0;
|
|
@@ -18990,8 +18971,6 @@ export class Runtime {
|
|
|
18990
18971
|
const rawState = stateMonitor(state)?.raw ?? state;
|
|
18991
18972
|
if (rawState.variables._ !== undefined)
|
|
18992
18973
|
delete rawState.variables._;
|
|
18993
|
-
rawState.lastArgument = wordValues[wordValues.length - 1] ?? w0Plain;
|
|
18994
|
-
state.lastArgument = rawState.lastArgument;
|
|
18995
18974
|
return await this.dispatchFastFunction(w0Plain, state.functions.get(w0Plain), wordValues.slice(1), state, originalIO);
|
|
18996
18975
|
}
|
|
18997
18976
|
}
|
|
@@ -19796,7 +19775,7 @@ export class Runtime {
|
|
|
19796
19775
|
publicDiagnosticMessage(failure, this.budget.onInternalError);
|
|
19797
19776
|
}
|
|
19798
19777
|
}
|
|
19799
|
-
if (
|
|
19778
|
+
if (error instanceof ExpansionFailure || error instanceof BraceExpansionFailure)
|
|
19800
19779
|
throw completedExit(error instanceof ParameterExpansionFailure && !state.isolated ? 127 : 1);
|
|
19801
19780
|
if (error instanceof FatalCommandFailure)
|
|
19802
19781
|
throw completedExit(error.status);
|
|
@@ -20265,8 +20244,10 @@ export class Runtime {
|
|
|
20265
20244
|
const rawState = stateMonitor(state)?.raw ?? state;
|
|
20266
20245
|
if (!state._readOnlyStage && rawState.variables._ !== undefined)
|
|
20267
20246
|
delete rawState.variables._;
|
|
20268
|
-
|
|
20269
|
-
|
|
20247
|
+
if (!state.functions.has(words[0] ?? "") && !["eval", ".", "source"].includes(words[declarationIndex] ?? "")) {
|
|
20248
|
+
rawState.lastArgument = words.at(-1) ?? "";
|
|
20249
|
+
state.lastArgument = rawState.lastArgument;
|
|
20250
|
+
}
|
|
20270
20251
|
if (!words.length)
|
|
20271
20252
|
return state.substitutionStatus;
|
|
20272
20253
|
const args = words.slice(1);
|
|
@@ -20530,8 +20511,10 @@ export class Runtime {
|
|
|
20530
20511
|
const rawState = stateMonitor(state)?.raw ?? state;
|
|
20531
20512
|
if (rawState.variables._ !== undefined && !assignments.some(assignment => assignment.name === "_"))
|
|
20532
20513
|
delete rawState.variables._;
|
|
20533
|
-
|
|
20534
|
-
|
|
20514
|
+
if (!functionCommand && !["eval", ".", "source"].includes(words[declarationIndex] ?? "")) {
|
|
20515
|
+
rawState.lastArgument = words.at(-1) ?? "";
|
|
20516
|
+
state.lastArgument = rawState.lastArgument;
|
|
20517
|
+
}
|
|
20535
20518
|
}
|
|
20536
20519
|
if (!words.length)
|
|
20537
20520
|
return state.substitutionStatus;
|
|
@@ -21402,10 +21385,16 @@ export class Runtime {
|
|
|
21402
21385
|
}
|
|
21403
21386
|
if (context.command === "command" || context.command === "builtin" || context.command === "type")
|
|
21404
21387
|
return { exitCode: await this.discoveryBuiltin(context, state, io, assignments, defaultPath) };
|
|
21405
|
-
if (context.command === "." || context.command === "source")
|
|
21406
|
-
|
|
21407
|
-
|
|
21408
|
-
return { exitCode
|
|
21388
|
+
if (context.command === "." || context.command === "source") {
|
|
21389
|
+
const exitCode = await this.sourceBuiltin(context, state, { ...io, ...context }, special);
|
|
21390
|
+
state.lastArgument = context.args.at(-1) ?? context.command;
|
|
21391
|
+
return { exitCode };
|
|
21392
|
+
}
|
|
21393
|
+
if (context.command === "eval") {
|
|
21394
|
+
const exitCode = await this.evalBuiltin(context, state, { ...io, ...context }, special);
|
|
21395
|
+
state.lastArgument = context.args.at(-1) ?? context.command;
|
|
21396
|
+
return { exitCode };
|
|
21397
|
+
}
|
|
21409
21398
|
const builtinWork = this.builtin({ ...context, [declarationArrays]: io[declarationArrays] }, state, assignments, (error, diagnostic) => { builtinFailure = { error, diagnostic }; }, bypassFunctions);
|
|
21410
21399
|
const builtin = await interruptible(builtinWork, this.signal);
|
|
21411
21400
|
if (builtin !== undefined) {
|
|
@@ -25517,7 +25506,7 @@ export class Runtime {
|
|
|
25517
25506
|
const length = await arithmetic(expression.length);
|
|
25518
25507
|
end = length.value < 0n ? size + length.value : offset + length.value;
|
|
25519
25508
|
if (end < offset)
|
|
25520
|
-
throw new
|
|
25509
|
+
throw new ExpansionFailure(`${length.source}: substring expression < 0`, line);
|
|
25521
25510
|
if (end > size)
|
|
25522
25511
|
end = size;
|
|
25523
25512
|
}
|
|
@@ -26824,6 +26813,13 @@ export class Runtime {
|
|
|
26824
26813
|
return out;
|
|
26825
26814
|
}
|
|
26826
26815
|
evalSyncTr(input, opArgs) {
|
|
26816
|
+
// Non-ASCII sets require the byte parser; reject them before running producers.
|
|
26817
|
+
for (const value of opArgs) {
|
|
26818
|
+
for (let i = 0; i < value.length; i++) {
|
|
26819
|
+
if (value.charCodeAt(i) >= 128)
|
|
26820
|
+
return undefined;
|
|
26821
|
+
}
|
|
26822
|
+
}
|
|
26827
26823
|
let idx = 0;
|
|
26828
26824
|
let flagC = false;
|
|
26829
26825
|
let flagD = false;
|
|
@@ -27908,6 +27904,10 @@ export class Runtime {
|
|
|
27908
27904
|
const core = anchorEnd ? core1.slice(0, -1) : core1;
|
|
27909
27905
|
if (core.length === 0 && !anchorStart && !anchorEnd)
|
|
27910
27906
|
return undefined;
|
|
27907
|
+
// POSIX-only brackets and leftmost-longest alternatives belong to sed's matcher.
|
|
27908
|
+
if (core.includes("[:") || core.includes("[.") || core.includes("[=") ||
|
|
27909
|
+
core.includes("[]") || core.includes("[^]") || isExtended && core.includes("|"))
|
|
27910
|
+
return undefined;
|
|
27911
27911
|
let reSrc;
|
|
27912
27912
|
if (/^[a-zA-Z0-9_ :;,=-]*$/.test(core)) {
|
|
27913
27913
|
if (/\\[1-9]/.test(rep))
|
|
@@ -28121,7 +28121,9 @@ export class Runtime {
|
|
|
28121
28121
|
const isOnlyMatching = mode.includes("o");
|
|
28122
28122
|
// POSIX bracket classes, collating symbols and equivalence classes have
|
|
28123
28123
|
// different meanings in JS RegExp. Let the command matcher parse them.
|
|
28124
|
-
if (
|
|
28124
|
+
if (!isFixed && rawPatterns.some(pattern => pattern.includes("[:") || pattern.includes("[.") || pattern.includes("[=") ||
|
|
28125
|
+
pattern.includes("[]") || pattern.includes("[^]") ||
|
|
28126
|
+
isExtended && pattern.includes("[") && pattern.includes("|")))
|
|
28125
28127
|
return undefined;
|
|
28126
28128
|
if (isCount && (isLineNumber || isOnlyMatching || beforeCtx > 0 || afterCtx > 0))
|
|
28127
28129
|
return undefined;
|
|
@@ -28154,15 +28156,19 @@ export class Runtime {
|
|
|
28154
28156
|
if (!re)
|
|
28155
28157
|
return undefined;
|
|
28156
28158
|
const out = [];
|
|
28159
|
+
let matched = false;
|
|
28157
28160
|
for (let li = 0; li < rawLines.length; li++) {
|
|
28158
28161
|
const matches = rawLines[li].match(re);
|
|
28159
28162
|
if (matches) {
|
|
28163
|
+
matched = true;
|
|
28160
28164
|
for (let mi = 0; mi < matches.length; mi++) {
|
|
28165
|
+
if (matches[mi].length === 0)
|
|
28166
|
+
continue;
|
|
28161
28167
|
out.push(isLineNumber ? `${li + 1}:${matches[mi]}` : matches[mi]);
|
|
28162
28168
|
}
|
|
28163
28169
|
}
|
|
28164
28170
|
}
|
|
28165
|
-
return { lines: out, status:
|
|
28171
|
+
return { lines: out, status: matched ? 0 : 1 };
|
|
28166
28172
|
}
|
|
28167
28173
|
if (isExtended && isFixed)
|
|
28168
28174
|
return undefined;
|
|
@@ -28854,7 +28860,7 @@ export class Runtime {
|
|
|
28854
28860
|
const out = [];
|
|
28855
28861
|
let curStyle = bodyStyle;
|
|
28856
28862
|
let num = start;
|
|
28857
|
-
const unnumberedPrefix = " ".repeat(width + sep
|
|
28863
|
+
const unnumberedPrefix = " ".repeat(width + shellValueByteLength(sep));
|
|
28858
28864
|
for (let i = 0; i < rawLines.length; i++) {
|
|
28859
28865
|
const l = rawLines[i];
|
|
28860
28866
|
if (l === "\\:" || l === "\\:\\:" || l === "\\:\\:\\:") {
|
|
@@ -29019,7 +29025,11 @@ export class Runtime {
|
|
|
29019
29025
|
const units = "KMGTPEZY";
|
|
29020
29026
|
const out = [];
|
|
29021
29027
|
for (let i = 0; i < rawLines.length; i++) {
|
|
29022
|
-
const
|
|
29028
|
+
const line = rawLines[i];
|
|
29029
|
+
const raw = line.trim();
|
|
29030
|
+
// Preserve field whitespace and padding through the command implementation.
|
|
29031
|
+
if (line !== raw)
|
|
29032
|
+
return undefined;
|
|
29023
29033
|
if (raw.length === 0) {
|
|
29024
29034
|
out.push("");
|
|
29025
29035
|
continue;
|
|
@@ -29361,7 +29371,7 @@ export class Runtime {
|
|
|
29361
29371
|
return "-L";
|
|
29362
29372
|
return undefined;
|
|
29363
29373
|
}
|
|
29364
|
-
evalSyncUniq(rawLines, opArgs) {
|
|
29374
|
+
evalSyncUniq(rawLines, opArgs, isByteLocale = false) {
|
|
29365
29375
|
let ignoreCase = false;
|
|
29366
29376
|
let hasCount = false;
|
|
29367
29377
|
let onlyRepeated = false;
|
|
@@ -29420,6 +29430,21 @@ export class Runtime {
|
|
|
29420
29430
|
return undefined;
|
|
29421
29431
|
}
|
|
29422
29432
|
}
|
|
29433
|
+
if (isByteLocale && (skipChars !== 0 || checkChars !== Infinity) && rawLines.some(line => {
|
|
29434
|
+
for (let i = 0; i < line.length; i++)
|
|
29435
|
+
if (line.charCodeAt(i) > 127)
|
|
29436
|
+
return true;
|
|
29437
|
+
return false;
|
|
29438
|
+
}))
|
|
29439
|
+
return undefined;
|
|
29440
|
+
const foldAscii = (value) => {
|
|
29441
|
+
let folded = "";
|
|
29442
|
+
for (let i = 0; i < value.length; i++) {
|
|
29443
|
+
const code = value.charCodeAt(i);
|
|
29444
|
+
folded += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : value[i];
|
|
29445
|
+
}
|
|
29446
|
+
return folded;
|
|
29447
|
+
};
|
|
29423
29448
|
const keyOf = (l) => {
|
|
29424
29449
|
let offset = 0;
|
|
29425
29450
|
for (let f = 0; f < skipFields; f++) {
|
|
@@ -29430,11 +29455,11 @@ export class Runtime {
|
|
|
29430
29455
|
}
|
|
29431
29456
|
if (skipChars === 0 && checkChars === Infinity) {
|
|
29432
29457
|
const sub = l.slice(offset);
|
|
29433
|
-
return ignoreCase ? sub
|
|
29458
|
+
return ignoreCase ? foldAscii(sub) : sub;
|
|
29434
29459
|
}
|
|
29435
29460
|
const chars = Array.from(l.slice(offset));
|
|
29436
29461
|
const sub = (checkChars === Infinity ? chars.slice(skipChars) : chars.slice(skipChars, skipChars + checkChars)).join("");
|
|
29437
|
-
return ignoreCase ? sub
|
|
29462
|
+
return ignoreCase ? foldAscii(sub) : sub;
|
|
29438
29463
|
};
|
|
29439
29464
|
const outLines = [];
|
|
29440
29465
|
let uIdx = 0;
|
|
@@ -29455,6 +29480,11 @@ export class Runtime {
|
|
|
29455
29480
|
return this.evalSyncSort(rawLines, flag !== undefined ? [flag] : [], false) ?? [...rawLines];
|
|
29456
29481
|
}
|
|
29457
29482
|
translateSyncTr(input, set1Spec, set2Spec, truncateSet1 = false) {
|
|
29483
|
+
// Other string2 classes require the tr parser's class and alignment validation.
|
|
29484
|
+
if (set2Spec.includes("[:") &&
|
|
29485
|
+
!(set1Spec === "[:lower:]" && set2Spec === "[:upper:]") &&
|
|
29486
|
+
!(set1Spec === "[:upper:]" && set2Spec === "[:lower:]"))
|
|
29487
|
+
return undefined;
|
|
29458
29488
|
if ((set1Spec === "a-z" || set1Spec === "[:lower:]") && (set2Spec === "A-Z" || set2Spec === "[:upper:]")) {
|
|
29459
29489
|
return input.replace(/[a-z]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 32));
|
|
29460
29490
|
}
|
|
@@ -29830,7 +29860,7 @@ export class Runtime {
|
|
|
29830
29860
|
const firstName = stageNames[sIdx];
|
|
29831
29861
|
const extDef = stageDefs[sIdx];
|
|
29832
29862
|
const stageArgs = stageArgsList[sIdx];
|
|
29833
|
-
|
|
29863
|
+
let nextBuf = (index & 1) === 0 ? sharedSyncPipeBuf0 : sharedSyncPipeBuf1;
|
|
29834
29864
|
const isInlineCutField = firstName === "cut";
|
|
29835
29865
|
const isInlineTr = firstName === "tr";
|
|
29836
29866
|
const isInlineAwk = firstName === "awk";
|
|
@@ -29879,6 +29909,8 @@ export class Runtime {
|
|
|
29879
29909
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && sub.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
29880
29910
|
this.budget.fail("maxOutputBytes");
|
|
29881
29911
|
this.budget.bytes = nextTotalBytes;
|
|
29912
|
+
if (sub.byteLength > nextBuf.byteLength)
|
|
29913
|
+
nextBuf = new Uint8Array(sub.byteLength);
|
|
29882
29914
|
nextBuf.set(sub, 0);
|
|
29883
29915
|
prevBuf = nextBuf;
|
|
29884
29916
|
prevLen = sub.byteLength;
|
|
@@ -29904,8 +29936,9 @@ export class Runtime {
|
|
|
29904
29936
|
outLines = [String(nl)];
|
|
29905
29937
|
}
|
|
29906
29938
|
else if (stageArgs[0] === "-w") {
|
|
29907
|
-
|
|
29908
|
-
|
|
29939
|
+
if (!byteLocale(rawState.variables) && prevBuf.subarray(0, prevLen).some(byte => byte > 127))
|
|
29940
|
+
return undefined;
|
|
29941
|
+
outLines = [String(inStr.split(/[ \t\n\r\f\v]+/).filter(Boolean).length)];
|
|
29909
29942
|
}
|
|
29910
29943
|
else if (stageArgs[0] === "-m") {
|
|
29911
29944
|
outLines = [String(byteLocale(rawState.variables) ? prevLen : Array.from(inStr).length)];
|
|
@@ -29928,12 +29961,12 @@ export class Runtime {
|
|
|
29928
29961
|
if (transformed === undefined)
|
|
29929
29962
|
return undefined;
|
|
29930
29963
|
const outByteLen = shellValueByteLength(transformed);
|
|
29931
|
-
if (outByteLen > nextBuf.byteLength)
|
|
29932
|
-
return undefined;
|
|
29933
29964
|
const nextTotalBytes = this.budget.bytes + outByteLen;
|
|
29934
29965
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && outByteLen > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
29935
29966
|
this.budget.fail("maxOutputBytes");
|
|
29936
29967
|
this.budget.bytes = nextTotalBytes;
|
|
29968
|
+
if (outByteLen > nextBuf.byteLength)
|
|
29969
|
+
nextBuf = new Uint8Array(outByteLen);
|
|
29937
29970
|
const written = transformed.length === 0 ? 0 : fastSharedTextEncoder.encodeInto(transformed, nextBuf).written;
|
|
29938
29971
|
prevBuf = nextBuf;
|
|
29939
29972
|
prevLen = written;
|
|
@@ -29943,7 +29976,7 @@ export class Runtime {
|
|
|
29943
29976
|
outLines = this.evalSyncSort(rawLines, stageArgs, false) ?? [];
|
|
29944
29977
|
}
|
|
29945
29978
|
else if (isInlineUniq) {
|
|
29946
|
-
const uniqRes = this.evalSyncUniq(rawLines, stageArgs);
|
|
29979
|
+
const uniqRes = this.evalSyncUniq(rawLines, stageArgs, byteLocale(rawState.variables));
|
|
29947
29980
|
if (uniqRes === undefined)
|
|
29948
29981
|
return undefined;
|
|
29949
29982
|
outLines = uniqRes;
|
|
@@ -29971,9 +30004,10 @@ export class Runtime {
|
|
|
29971
30004
|
outLines = grepRes.lines;
|
|
29972
30005
|
}
|
|
29973
30006
|
else if (isInlineTac) {
|
|
29974
|
-
if (inStr.length > 0 && !inStr.endsWith("\n"))
|
|
29975
|
-
return undefined;
|
|
29976
30007
|
outLines = [...rawLines].reverse();
|
|
30008
|
+
if (!inStr.endsWith("\n") && outLines.length > 1) {
|
|
30009
|
+
outLines.splice(0, 2, outLines[0] + outLines[1]);
|
|
30010
|
+
}
|
|
29977
30011
|
}
|
|
29978
30012
|
else if (isInlineNl) {
|
|
29979
30013
|
const nlRes = this.evalSyncNl(rawLines, stageArgs);
|
|
@@ -30003,12 +30037,14 @@ export class Runtime {
|
|
|
30003
30037
|
if (cleaned.length % 4 !== 0 || (cleaned.length > 0 && !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)))
|
|
30004
30038
|
return undefined;
|
|
30005
30039
|
const decoded = this.syncBase64DecodeBytes(cleaned);
|
|
30006
|
-
if (decoded.some(byte => byte === 0 || byte >= 128)
|
|
30040
|
+
if (decoded.some(byte => byte === 0 || byte >= 128))
|
|
30007
30041
|
return undefined;
|
|
30008
30042
|
const nextTotalBytes = this.budget.bytes + decoded.byteLength;
|
|
30009
30043
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && decoded.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
30010
30044
|
this.budget.fail("maxOutputBytes");
|
|
30011
30045
|
this.budget.bytes = nextTotalBytes;
|
|
30046
|
+
if (decoded.byteLength > nextBuf.byteLength)
|
|
30047
|
+
nextBuf = new Uint8Array(decoded.byteLength);
|
|
30012
30048
|
nextBuf.set(decoded, 0);
|
|
30013
30049
|
prevBuf = nextBuf;
|
|
30014
30050
|
prevLen = decoded.byteLength;
|
|
@@ -30023,15 +30059,15 @@ export class Runtime {
|
|
|
30023
30059
|
}
|
|
30024
30060
|
// These filters preserve the terminator of the final selected input line.
|
|
30025
30061
|
const preservesTerminator = firstName === "head" || firstName === "tail" || firstName === "rev" || firstName === "sed";
|
|
30026
|
-
const terminated = sedTerminated || !preservesTerminator || inStr.endsWith("\n") || firstName === "head" && outLines.length < rawLines.length;
|
|
30062
|
+
const terminated = isInlineTac ? inStr.includes("\n") : sedTerminated || !preservesTerminator || inStr.endsWith("\n") || firstName === "head" && outLines.length < rawLines.length;
|
|
30027
30063
|
const outStr = outLines.length > 0 ? outLines.join("\n") + (terminated ? "\n" : "") : "";
|
|
30028
30064
|
const outByteLen = shellValueByteLength(outStr);
|
|
30029
|
-
if (outByteLen > nextBuf.byteLength)
|
|
30030
|
-
return undefined;
|
|
30031
30065
|
const nextTotalBytes = this.budget.bytes + outByteLen;
|
|
30032
30066
|
if (nextTotalBytes > this.budget.maxOutputBytesSmi && outByteLen > this.budget.limits.maxOutputBytes - this.budget.bytes)
|
|
30033
30067
|
this.budget.fail("maxOutputBytes");
|
|
30034
30068
|
this.budget.bytes = nextTotalBytes;
|
|
30069
|
+
if (outByteLen > nextBuf.byteLength)
|
|
30070
|
+
nextBuf = new Uint8Array(outByteLen);
|
|
30035
30071
|
const written = outStr.length === 0 ? 0 : fastSharedTextEncoder.encodeInto(outStr, nextBuf).written;
|
|
30036
30072
|
prevBuf = nextBuf;
|
|
30037
30073
|
prevLen = written;
|
|
@@ -30341,7 +30377,7 @@ export class Runtime {
|
|
|
30341
30377
|
fileRes = renderLines(sortRes);
|
|
30342
30378
|
}
|
|
30343
30379
|
else if (w0Plain === "uniq") {
|
|
30344
|
-
const uniqRes = this.evalSyncUniq(rawLines, opArgs);
|
|
30380
|
+
const uniqRes = this.evalSyncUniq(rawLines, opArgs, byteLocale(rawState.variables));
|
|
30345
30381
|
if (uniqRes !== undefined)
|
|
30346
30382
|
fileRes = renderLines(uniqRes);
|
|
30347
30383
|
}
|
|
@@ -30352,7 +30388,10 @@ export class Runtime {
|
|
|
30352
30388
|
if (opArgs.length === 0)
|
|
30353
30389
|
fileRes = this.syncBase64Encode(view) + "\n";
|
|
30354
30390
|
else {
|
|
30355
|
-
const
|
|
30391
|
+
const cleaned = fileStr.replace(/[ \t\r\n]+/g, "");
|
|
30392
|
+
if (cleaned.length % 4 !== 0 || (cleaned.length > 0 && !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)))
|
|
30393
|
+
return undefined;
|
|
30394
|
+
const decoded = this.syncBase64DecodeBytes(cleaned);
|
|
30356
30395
|
// Text-only substitutions cannot retain binary byte provenance.
|
|
30357
30396
|
if (decoded.some(byte => byte === 0 || byte >= 128))
|
|
30358
30397
|
return undefined;
|