@poe-platform/safe-bash 0.1.78 → 0.1.80
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/browser.js +287 -167
- package/dist/safe-bash/browser.js.map +4 -4
- package/dist/safe-bash/commands/text-programs/awk-runtime.js +5 -5
- package/dist/safe-bash/commands/text-programs/awk-runtime.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/awk-values.d.ts +4 -3
- package/dist/safe-bash/commands/text-programs/awk-values.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/awk-values.js +71 -26
- package/dist/safe-bash/commands/text-programs/awk-values.js.map +1 -1
- package/dist/safe-bash/shell/parser.d.ts.map +1 -1
- package/dist/safe-bash/shell/parser.js +33 -22
- package/dist/safe-bash/shell/parser.js.map +1 -1
- package/dist/safe-bash/shell/pattern.d.ts +3 -8
- package/dist/safe-bash/shell/pattern.d.ts.map +1 -1
- package/dist/safe-bash/shell/pattern.js +20 -16
- package/dist/safe-bash/shell/pattern.js.map +1 -1
- package/dist/safe-bash/shell/runtime.d.ts +3 -0
- package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
- package/dist/safe-bash/shell/runtime.js +229 -151
- package/dist/safe-bash/shell/runtime.js.map +1 -1
- package/dist/safe-bash/shell/string-operations.d.ts +17 -0
- package/dist/safe-bash/shell/string-operations.d.ts.map +1 -0
- package/dist/safe-bash/shell/string-operations.js +42 -0
- package/dist/safe-bash/shell/string-operations.js.map +1 -0
- package/package.json +2 -2
|
@@ -10,6 +10,7 @@ import { evaluateArithmetic, prepareArithmetic } from "./arithmetic.js";
|
|
|
10
10
|
import { defaultMaxParseUnits, ParseBudget } from "./parse-budget.js";
|
|
11
11
|
import { evaluatePositionalArithmetic } from "./arithmetic-parameters.js";
|
|
12
12
|
import { compilePattern, matchesPattern } from "./pattern.js";
|
|
13
|
+
import { nextCodePointOffset, previousCodePointOffset, scanString, stringCheckpoint } from "./string-operations.js";
|
|
13
14
|
import { byteLocale } from "./locale.js";
|
|
14
15
|
import { functionDisplay } from "./display.js";
|
|
15
16
|
import { ConditionalUnsupported, evaluateConditional } from "./conditional.js";
|
|
@@ -4939,7 +4940,15 @@ export class Runtime {
|
|
|
4939
4940
|
holding?.release();
|
|
4940
4941
|
}
|
|
4941
4942
|
}
|
|
4943
|
+
parameterOperandIO(word, state, io) {
|
|
4944
|
+
this.signal.throwIfAborted();
|
|
4945
|
+
const parameterDepth = (io.parameterDepth ?? 0) + 1;
|
|
4946
|
+
if (state.depth + parameterDepth > 64)
|
|
4947
|
+
throw new ShellSyntaxError("Syntax nesting exceeds 64", word.offset);
|
|
4948
|
+
return { ...io, parameterDepth };
|
|
4949
|
+
}
|
|
4942
4950
|
async partValue(part, state, io, hereString) {
|
|
4951
|
+
this.signal.throwIfAborted();
|
|
4943
4952
|
if (part.kind === "failed-substitution") {
|
|
4944
4953
|
if (state.depth >= this.budget.limits.maxSubstitutionDepth)
|
|
4945
4954
|
this.budget.fail("maxSubstitutionDepth");
|
|
@@ -4966,6 +4975,9 @@ export class Runtime {
|
|
|
4966
4975
|
if (part.kind === "substitution") {
|
|
4967
4976
|
if (state.depth >= this.budget.limits.maxSubstitutionDepth)
|
|
4968
4977
|
this.budget.fail("maxSubstitutionDepth");
|
|
4978
|
+
const parameterDepth = io.parameterDepth ?? 0;
|
|
4979
|
+
if (parameterDepth > 0 && state.depth + parameterDepth + 1 > 64)
|
|
4980
|
+
throw new ShellSyntaxError("Syntax nesting exceeds 64", 0);
|
|
4969
4981
|
const capture = new Capture();
|
|
4970
4982
|
const child = await cloneState(state, this.signal);
|
|
4971
4983
|
child.isolated = true;
|
|
@@ -5022,7 +5034,7 @@ export class Runtime {
|
|
|
5022
5034
|
throw new ArrayFailure("index outside 0..2147483647");
|
|
5023
5035
|
const value = binding ? binding.get(index) : index === 0 ? state.variables[part.name] : undefined;
|
|
5024
5036
|
this.requireParameter(value, `${part.name}[${selector.index}]`, state, io, part.line);
|
|
5025
|
-
return part.length ?
|
|
5037
|
+
return part.length ? this.parameterLength(value ?? "") : value ?? "";
|
|
5026
5038
|
}
|
|
5027
5039
|
if (part.length)
|
|
5028
5040
|
return String(binding?.values.size ?? (state.variables[part.name] === undefined ? 0 : 1));
|
|
@@ -5060,17 +5072,18 @@ export class Runtime {
|
|
|
5060
5072
|
const missing = value === undefined || (part.operator.startsWith(":") && value === "");
|
|
5061
5073
|
const operator = part.operator.at(-1);
|
|
5062
5074
|
if ((operator === "+" && !missing) || (operator !== "+" && missing)) {
|
|
5075
|
+
const operandIO = this.parameterOperandIO(part.alternate, state, io);
|
|
5063
5076
|
let alternate;
|
|
5064
5077
|
if (operator === "=" && arrayStore(state)?.get(part.name)) {
|
|
5065
5078
|
alternate = "";
|
|
5066
5079
|
await this.arrayZero(state, part.name, async () => {
|
|
5067
|
-
alternate = await this.arrayJoin(requireArrays(state).owner, await this.word(part.alternate, state,
|
|
5080
|
+
alternate = await this.arrayJoin(requireArrays(state).owner, await this.word(part.alternate, state, operandIO, false, false, hereString), "");
|
|
5068
5081
|
return alternate;
|
|
5069
5082
|
});
|
|
5070
5083
|
value = alternate;
|
|
5071
|
-
return part.length ?
|
|
5084
|
+
return part.length ? this.parameterLength(value) : value;
|
|
5072
5085
|
}
|
|
5073
|
-
retained = concatShellValues(await this.valueWord(part.alternate, state,
|
|
5086
|
+
retained = concatShellValues(await this.valueWord(part.alternate, state, operandIO, false, false, hereString), io[valueScope]);
|
|
5074
5087
|
alternate = shellValueText(retained);
|
|
5075
5088
|
if (operator === "?")
|
|
5076
5089
|
throw new ParameterExpansionFailure(`${part.name}: ${alternate || (part.operator.startsWith(":") ? "parameter null or not set" : "parameter not set")}`, io.diagnosticLine ?? part.line);
|
|
@@ -5088,7 +5101,15 @@ export class Runtime {
|
|
|
5088
5101
|
}
|
|
5089
5102
|
else
|
|
5090
5103
|
this.requireParameter(value, part.name, state, io, part.line);
|
|
5091
|
-
return part.length ?
|
|
5104
|
+
return part.length ? this.parameterLength(value ?? "") : retained ?? "";
|
|
5105
|
+
}
|
|
5106
|
+
async parameterLength(value) {
|
|
5107
|
+
const limit = this.budget.limits.maxExpansionBytes;
|
|
5108
|
+
const work = { remaining: Math.min(Number.MAX_SAFE_INTEGER, limit * 4 + 1024), signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
|
|
5109
|
+
const scanned = await scanString(value, work);
|
|
5110
|
+
if (scanned.bytes > limit)
|
|
5111
|
+
this.budget.fail("maxExpansionBytes");
|
|
5112
|
+
return String(scanned.count);
|
|
5092
5113
|
}
|
|
5093
5114
|
async substring(part, value, state, io) {
|
|
5094
5115
|
const owner = arrayStore(state)?.get(part.name) ? requireArrays(state).owner : undefined;
|
|
@@ -5101,170 +5122,226 @@ export class Runtime {
|
|
|
5101
5122
|
const limit = this.budget.limits.maxExpansionBytes;
|
|
5102
5123
|
if (Buffer.byteLength(value) > limit)
|
|
5103
5124
|
this.budget.fail("maxExpansionBytes");
|
|
5104
|
-
const
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
this.
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5125
|
+
const scratch = this.budget.values.scope();
|
|
5126
|
+
const work = { remaining: Math.min(Number.MAX_SAFE_INTEGER, limit * 4 + 1024), signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
|
|
5127
|
+
try {
|
|
5128
|
+
const variables = new Proxy(this.arithmeticVariables(state, line), { get: (target, key) => {
|
|
5129
|
+
this.signal.throwIfAborted();
|
|
5130
|
+
const value = Reflect.get(target, key);
|
|
5131
|
+
if (typeof value === "string" && Buffer.byteLength(value) > limit)
|
|
5132
|
+
this.budget.fail("maxExpansionBytes");
|
|
5133
|
+
return value;
|
|
5134
|
+
} });
|
|
5135
|
+
const arithmetic = async (word) => {
|
|
5136
|
+
const operandIO = this.parameterOperandIO(word, state, io);
|
|
5137
|
+
let source = "";
|
|
5138
|
+
let bytes = 0;
|
|
5139
|
+
let retained;
|
|
5140
|
+
for (const entry of word.parts) {
|
|
5141
|
+
this.signal.throwIfAborted();
|
|
5142
|
+
const text = entry.kind === "text" ? entry.value : await this.part(entry, state, operandIO);
|
|
5143
|
+
bytes += Buffer.byteLength(text);
|
|
5144
|
+
if (bytes > limit)
|
|
5145
|
+
this.budget.fail("maxExpansionBytes");
|
|
5146
|
+
owner?.reserve({ metadata: 32, payload: bytes, work: text.length + 4 });
|
|
5147
|
+
const pending = stringCheckpoint(work, text.length + 1);
|
|
5148
|
+
if (pending)
|
|
5149
|
+
await pending;
|
|
5150
|
+
const next = scratch.reserve((source.length + text.length) * 2, 0);
|
|
5151
|
+
source += text;
|
|
5152
|
+
retained?.release();
|
|
5153
|
+
retained = next;
|
|
5154
|
+
}
|
|
5115
5155
|
this.signal.throwIfAborted();
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5156
|
+
try {
|
|
5157
|
+
return { value: evaluateArithmetic(prepareArithmetic(source, this.budget.parsing), variables, this.budget.parsing), source };
|
|
5158
|
+
}
|
|
5159
|
+
catch (error) {
|
|
5160
|
+
this.rethrowArithmeticControl(error);
|
|
5161
|
+
throw new ExpansionFailure(`${part.name}: ${message(error)}`, line);
|
|
5162
|
+
}
|
|
5163
|
+
finally {
|
|
5164
|
+
retained?.release();
|
|
5165
|
+
}
|
|
5166
|
+
};
|
|
5167
|
+
const offsetExpression = await arithmetic(expression.offset);
|
|
5168
|
+
let bytes;
|
|
5169
|
+
if (byteLocale(state.variables)) {
|
|
5170
|
+
scratch.reserve(Buffer.byteLength(value), 0);
|
|
5171
|
+
bytes = Buffer.from(value);
|
|
5172
|
+
}
|
|
5173
|
+
const size = BigInt(bytes?.byteLength ?? (await scanString(value, work)).count);
|
|
5174
|
+
const offset = offsetExpression.value < 0n ? size + offsetExpression.value : offsetExpression.value;
|
|
5175
|
+
if (offset < 0n || offset > size)
|
|
5176
|
+
return "";
|
|
5177
|
+
let end = size;
|
|
5178
|
+
if (expression.length) {
|
|
5179
|
+
const length = await arithmetic(expression.length);
|
|
5180
|
+
end = length.value < 0n ? size + length.value : offset + length.value;
|
|
5181
|
+
if (end < offset)
|
|
5182
|
+
throw new ExpansionFailure(`${length.source}: substring expression < 0`, line);
|
|
5183
|
+
if (end > size)
|
|
5184
|
+
end = size;
|
|
5122
5185
|
}
|
|
5123
5186
|
this.signal.throwIfAborted();
|
|
5187
|
+
if (!bytes) {
|
|
5188
|
+
const start = (await scanString(value, work, 0, value.length, Number(offset))).end;
|
|
5189
|
+
const finish = (await scanString(value, work, start, value.length, Number(end - offset))).end;
|
|
5190
|
+
if (finish > start)
|
|
5191
|
+
scratch.reserve((finish - start) * 2, 0);
|
|
5192
|
+
return value.slice(start, finish);
|
|
5193
|
+
}
|
|
5194
|
+
scratch.reserve(Number(end - offset) * 2, 0);
|
|
5124
5195
|
try {
|
|
5125
|
-
return {
|
|
5196
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(Number(offset), Number(end)));
|
|
5126
5197
|
}
|
|
5127
|
-
catch
|
|
5128
|
-
|
|
5129
|
-
throw new ExpansionFailure(`${part.name}: ${message(error)}`, line);
|
|
5198
|
+
catch {
|
|
5199
|
+
throw new ExpansionFailure("substring expansion splits a UTF-8 character in a byte locale", line);
|
|
5130
5200
|
}
|
|
5131
|
-
};
|
|
5132
|
-
const offsetExpression = await arithmetic(expression.offset);
|
|
5133
|
-
owner?.reserve({ metadata: 128 + value.length * 64, payload: Buffer.byteLength(value), allocatedSlots: value.length, work: value.length + 8 });
|
|
5134
|
-
const characters = byteLocale(state.variables) ? undefined : Array.from(value);
|
|
5135
|
-
const bytes = characters ? undefined : Buffer.from(value);
|
|
5136
|
-
const size = BigInt(characters?.length ?? bytes.byteLength);
|
|
5137
|
-
const offset = offsetExpression.value < 0n ? size + offsetExpression.value : offsetExpression.value;
|
|
5138
|
-
if (offset < 0n || offset > size)
|
|
5139
|
-
return "";
|
|
5140
|
-
let end = size;
|
|
5141
|
-
if (expression.length) {
|
|
5142
|
-
const length = await arithmetic(expression.length);
|
|
5143
|
-
end = length.value < 0n ? size + length.value : offset + length.value;
|
|
5144
|
-
if (end < offset)
|
|
5145
|
-
throw new ExpansionFailure(`${length.source}: substring expression < 0`, line);
|
|
5146
|
-
if (end > size)
|
|
5147
|
-
end = size;
|
|
5148
5201
|
}
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
if (characters)
|
|
5152
|
-
return characters.slice(Number(offset), Number(end)).join("");
|
|
5153
|
-
try {
|
|
5154
|
-
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(Number(offset), Number(end)));
|
|
5155
|
-
}
|
|
5156
|
-
catch {
|
|
5157
|
-
throw new ExpansionFailure("substring expansion splits a UTF-8 character in a byte locale", line);
|
|
5202
|
+
finally {
|
|
5203
|
+
scratch.close();
|
|
5158
5204
|
}
|
|
5159
5205
|
}
|
|
5160
5206
|
async parameterPattern(part, text, state, io, hereString) {
|
|
5161
|
-
const owner = arrayStore(state)?.get(part.name) ? requireArrays(state).owner : undefined;
|
|
5162
5207
|
const limit = this.budget.limits.maxExpansionBytes;
|
|
5163
5208
|
if (Buffer.byteLength(text) > limit)
|
|
5164
5209
|
this.budget.fail("maxExpansionBytes");
|
|
5165
|
-
const
|
|
5166
|
-
const
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
const longest = operator.length === 2;
|
|
5190
|
-
for (let length = longest ? characters.length : 0; longest ? length >= 0 : length <= characters.length; length += longest ? -1 : 1) {
|
|
5210
|
+
const scratch = this.budget.values.scope();
|
|
5211
|
+
const work = { remaining: Math.min(Number.MAX_SAFE_INTEGER, limit * 4 + 1024), signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes"), allocation: scratch };
|
|
5212
|
+
try {
|
|
5213
|
+
const patternFields = await this.word(part.alternate, state, this.parameterOperandIO(part.alternate, state, io), false, true, hereString);
|
|
5214
|
+
let patternUnits = 0;
|
|
5215
|
+
for (const field of patternFields) {
|
|
5216
|
+
const pending = stringCheckpoint(work, field.length + 1);
|
|
5217
|
+
if (pending)
|
|
5218
|
+
await pending;
|
|
5219
|
+
patternUnits += field.length;
|
|
5220
|
+
}
|
|
5221
|
+
scratch.reserve(patternUnits * 2, 0);
|
|
5222
|
+
const pattern = patternFields.join("");
|
|
5223
|
+
const size = (await scanString(text, work)).count;
|
|
5224
|
+
const matches = await compilePattern(pattern, work);
|
|
5225
|
+
const match = async (start, end, length) => {
|
|
5226
|
+
const pending = stringCheckpoint(work, length + 1);
|
|
5227
|
+
if (pending)
|
|
5228
|
+
await pending;
|
|
5229
|
+
return matches(text, start, end);
|
|
5230
|
+
};
|
|
5231
|
+
const operator = part.operator;
|
|
5232
|
+
if (!operator.startsWith("/")) {
|
|
5233
|
+
const longest = operator.length === 2;
|
|
5191
5234
|
const prefix = operator.startsWith("#");
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5235
|
+
let boundary = longest === prefix ? text.length : 0;
|
|
5236
|
+
for (let length = longest ? size : 0; longest ? length >= 0 : length <= size; length += longest ? -1 : 1) {
|
|
5237
|
+
if (await match(prefix ? 0 : boundary, prefix ? boundary : text.length, length)) {
|
|
5238
|
+
const start = prefix ? boundary : 0;
|
|
5239
|
+
const end = prefix ? text.length : boundary;
|
|
5240
|
+
scratch.reserve((end - start) * 2, 0);
|
|
5241
|
+
return text.slice(start, end);
|
|
5242
|
+
}
|
|
5243
|
+
boundary = longest === prefix ? previousCodePointOffset(text, boundary) : nextCodePointOffset(text, boundary);
|
|
5244
|
+
}
|
|
5245
|
+
return text;
|
|
5246
|
+
}
|
|
5247
|
+
scratch.reserve(64, 0);
|
|
5248
|
+
const replacements = [];
|
|
5249
|
+
let replacementBytes = 0;
|
|
5250
|
+
const replacementIO = part.replacement ? this.parameterOperandIO(part.replacement, state, io) : io;
|
|
5251
|
+
for (const [index, entry] of (part.replacement?.parts ?? []).entries()) {
|
|
5252
|
+
let value = entry.kind === "text" ? entry.value : await this.part(entry, state, replacementIO, hereString);
|
|
5253
|
+
if (index === 0 && !entry.quoted && /^~(?:\/|$)/u.test(value)) {
|
|
5254
|
+
const home = state.variables.HOME ?? "~";
|
|
5255
|
+
scratch.reserve((home.length + value.length - 1) * 2, 0);
|
|
5256
|
+
value = home + value.slice(1);
|
|
5257
|
+
}
|
|
5258
|
+
replacementBytes += Buffer.byteLength(value);
|
|
5259
|
+
if (replacementBytes > limit)
|
|
5260
|
+
this.budget.fail("maxExpansionBytes");
|
|
5261
|
+
const pending = stringCheckpoint(work, value.length + 1);
|
|
5262
|
+
if (pending)
|
|
5263
|
+
await pending;
|
|
5264
|
+
scratch.reserve(64 + value.length * 2, 0);
|
|
5265
|
+
replacements.push({ value, quoted: entry.quoted });
|
|
5266
|
+
}
|
|
5267
|
+
if (!pattern && operator !== "/#" && operator !== "/%")
|
|
5268
|
+
return text;
|
|
5269
|
+
let result = "";
|
|
5270
|
+
let resultBytes = 0;
|
|
5271
|
+
let retained;
|
|
5272
|
+
const append = async (value, start = 0, end = value.length) => {
|
|
5273
|
+
resultBytes += (await scanString(value, work, start, end)).bytes;
|
|
5274
|
+
if (resultBytes > limit)
|
|
5275
|
+
this.budget.fail("maxExpansionBytes");
|
|
5276
|
+
if (start === end)
|
|
5277
|
+
return;
|
|
5278
|
+
const fragment = scratch.reserve((end - start) * 2, 0);
|
|
5279
|
+
const next = scratch.reserve((result.length + end - start) * 2, 0);
|
|
5280
|
+
result += value.slice(start, end);
|
|
5281
|
+
fragment.release();
|
|
5282
|
+
retained?.release();
|
|
5283
|
+
retained = next;
|
|
5284
|
+
};
|
|
5285
|
+
let position = 0;
|
|
5286
|
+
let positionIndex = 0;
|
|
5287
|
+
while (positionIndex <= size) {
|
|
5288
|
+
let found = false;
|
|
5289
|
+
for (let start = position, startIndex = positionIndex; startIndex <= size; startIndex++, start = nextCodePointOffset(text, start)) {
|
|
5290
|
+
if (operator === "/#" && start !== 0)
|
|
5229
5291
|
break;
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5292
|
+
for (let end = text.length, endIndex = size; endIndex >= startIndex; endIndex--, end = previousCodePointOffset(text, end)) {
|
|
5293
|
+
if (operator === "/%" && end !== text.length)
|
|
5294
|
+
break;
|
|
5295
|
+
if (!await match(start, end, endIndex - startIndex))
|
|
5296
|
+
continue;
|
|
5297
|
+
await append(text, position, start);
|
|
5298
|
+
for (const replacement of replacements) {
|
|
5299
|
+
if (replacement.quoted)
|
|
5300
|
+
await append(replacement.value);
|
|
5301
|
+
else {
|
|
5302
|
+
let fragment = 0;
|
|
5303
|
+
for (let cursor = 0; cursor < replacement.value.length; cursor++) {
|
|
5304
|
+
const pending = stringCheckpoint(work);
|
|
5305
|
+
if (pending)
|
|
5306
|
+
await pending;
|
|
5307
|
+
if (replacement.value[cursor] !== "&")
|
|
5308
|
+
continue;
|
|
5309
|
+
await append(replacement.value, fragment, cursor);
|
|
5310
|
+
await append(text, start, end);
|
|
5311
|
+
fragment = cursor + 1;
|
|
5312
|
+
}
|
|
5313
|
+
await append(replacement.value, fragment);
|
|
5244
5314
|
}
|
|
5245
5315
|
}
|
|
5316
|
+
position = end;
|
|
5317
|
+
positionIndex = endIndex;
|
|
5318
|
+
found = true;
|
|
5319
|
+
if (operator !== "//" || end === text.length) {
|
|
5320
|
+
await append(text, end);
|
|
5321
|
+
return result;
|
|
5322
|
+
}
|
|
5323
|
+
if (end === start) {
|
|
5324
|
+
position = nextCodePointOffset(text, end);
|
|
5325
|
+
positionIndex++;
|
|
5326
|
+
await append(text, end, position);
|
|
5327
|
+
}
|
|
5328
|
+
break;
|
|
5246
5329
|
}
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
if (operator !== "//" || end === characters.length) {
|
|
5250
|
-
append(slice(end));
|
|
5251
|
-
return result;
|
|
5252
|
-
}
|
|
5253
|
-
if (end === start) {
|
|
5254
|
-
append(characters[position]);
|
|
5255
|
-
position++;
|
|
5256
|
-
}
|
|
5257
|
-
break;
|
|
5330
|
+
if (found)
|
|
5331
|
+
break;
|
|
5258
5332
|
}
|
|
5259
|
-
if (found)
|
|
5333
|
+
if (!found) {
|
|
5334
|
+
if (positionIndex === 0)
|
|
5335
|
+
return text;
|
|
5336
|
+
await append(text, position);
|
|
5260
5337
|
break;
|
|
5338
|
+
}
|
|
5261
5339
|
}
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5340
|
+
return result;
|
|
5341
|
+
}
|
|
5342
|
+
finally {
|
|
5343
|
+
scratch.close();
|
|
5266
5344
|
}
|
|
5267
|
-
return result;
|
|
5268
5345
|
}
|
|
5269
5346
|
async word(word, state, io, split = true, pattern = false, hereString = false, conditionalPattern = false, regexAppend) {
|
|
5270
5347
|
return (await this.valueWord(word, state, io, split, pattern, hereString, conditionalPattern, regexAppend)).map(shellValueText);
|
|
@@ -5349,16 +5426,17 @@ export class Runtime {
|
|
|
5349
5426
|
if (boundary)
|
|
5350
5427
|
addField();
|
|
5351
5428
|
};
|
|
5352
|
-
const parts = word.parts.map((part) => ({ part, splitText: false }));
|
|
5429
|
+
const parts = word.parts.map((part) => ({ part, splitText: false, io }));
|
|
5353
5430
|
for (let index = 0; index < parts.length; index++) {
|
|
5354
|
-
const { part, splitText } = parts[index];
|
|
5431
|
+
const { part, splitText, io: partIO } = parts[index];
|
|
5355
5432
|
const quotedPresence = part.quoted && !(arrayOwned && isQuoteMarker(part));
|
|
5356
5433
|
if (part.kind === "variable" && ["-", "+", ":-", ":+"].includes(part.operator ?? "") && /^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(part.name)) {
|
|
5357
5434
|
const value = this.variable(state, part.name);
|
|
5358
5435
|
const missing = value === undefined || (part.operator.startsWith(":") && value === "");
|
|
5359
5436
|
if (part.operator.endsWith("+") ? !missing : missing) {
|
|
5437
|
+
const operandIO = this.parameterOperandIO(part.alternate, state, partIO);
|
|
5360
5438
|
scratch?.reserve(part.alternate.parts.length * 32, 0);
|
|
5361
|
-
const alternate = part.alternate.parts.map((entry) => ({ part: copyArraySelector(entry, { ...entry, quoted: entry.quoted || part.quoted }), splitText: true }));
|
|
5439
|
+
const alternate = part.alternate.parts.map((entry) => ({ part: copyArraySelector(entry, { ...entry, quoted: entry.quoted || part.quoted }), splitText: true, io: operandIO }));
|
|
5362
5440
|
if (!alternate.length && part.quoted)
|
|
5363
5441
|
append("", false, true);
|
|
5364
5442
|
parts.splice(index + 1, 0, ...alternate);
|
|
@@ -5394,7 +5472,7 @@ export class Runtime {
|
|
|
5394
5472
|
fields[0].present = false;
|
|
5395
5473
|
}
|
|
5396
5474
|
else {
|
|
5397
|
-
const value = part.kind === "text" ? part.byteValue ?? part.value : await this.valuePart(part, state,
|
|
5475
|
+
const value = part.kind === "text" ? part.byteValue ?? part.value : await this.valuePart(part, state, partIO, hereString);
|
|
5398
5476
|
if (part.quoted || !split || state.variables.IFS === "")
|
|
5399
5477
|
append(value, !part.quoted, quotedPresence || !split || shellValueByteLength(value) > 0);
|
|
5400
5478
|
else
|