@poe-platform/safe-bash 0.1.746 → 0.1.748

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.
@@ -1,6 +1,6 @@
1
1
  import { splitSyncJqExpression } from "./sync-jq-expression.js";
2
2
  import { wcDisplayWidth } from "../commands/wc-width.js";
3
- import { text as awkValueText, compare as awkCompare, inputValue as awkInputValue, numeric as awkNumeric, string as awkString } from "../commands/text-programs/awk-values.js";
3
+ import { text as awkValueText, compare as awkCompare, inputValue as awkInputValue, numeric as awkNumeric, number as awkNumber, string as awkString } from "../commands/text-programs/awk-values.js";
4
4
  import { bytesToHex, latin1Text } from "../byte-encoding.js";
5
5
  const sharedCaptureDecoder = new TextDecoder();
6
6
  const cachedCaptureAsciiBytes = new Uint8Array(4096);
@@ -13637,60 +13637,11 @@ export class Runtime {
13637
13637
  // command. A late fallback would replay effects already performed.
13638
13638
  return false;
13639
13639
  }
13640
- const sel = getArraySelector(part);
13641
- if (sel !== undefined) {
13642
- if (rawState.nounset)
13643
- return false;
13644
- if (sel.kind === "members" || sel.kind === "keys") {
13645
- if (part.operator !== undefined) {
13646
- if (sel.kind === "members" &&
13647
- !part.keys &&
13648
- !part.length &&
13649
- !part.substring &&
13650
- part.quoted &&
13651
- (sel.separator === "*" || (allowAt && sel.separator === "@")) &&
13652
- this.budget.limits.maxExpansionBytes === Infinity &&
13653
- !rawState.nocasematch) {
13654
- if ((part.operator === "#" || part.operator === "##" || part.operator === "%" || part.operator === "%%") && part.alternate && part.alternate.parts.length === 1 && part.alternate.parts[0].kind === "text" && (!hasGlobOrEscape(part.alternate.parts[0].value, true) || (!part.alternate.parts[0].quoted && Boolean(tryCompileTrimGlobToRegex(part.alternate.parts[0].value, part.operator, !!rawState.extglob)))))
13655
- continue;
13656
- if ((part.operator === "/" || part.operator === "//" || part.operator === "/#" || part.operator === "/%") && part.alternate && part.alternate.parts.length === 1 && part.alternate.parts[0].kind === "text" && part.alternate.parts[0].value.length > 0 && (!hasGlobOrEscape(part.alternate.parts[0].value, true) || (!part.alternate.parts[0].quoted && Boolean(tryCompileFixedGlobToRegex(part.alternate.parts[0].value, part.operator, !!rawState.extglob)))) && (!part.replacement || part.replacement.parts.length === 0 || (part.replacement.parts.length === 1 && part.replacement.parts[0].kind === "text" && !part.replacement.parts[0].value.includes("&") && !part.replacement.parts[0].value.includes("\\") && !part.replacement.parts[0].value.startsWith("~"))))
13657
- continue;
13658
- if ((part.operator === "^" || part.operator === "^^" || part.operator === "," || part.operator === ",,") && (!part.alternate || part.alternate.parts.length === 0))
13659
- continue;
13660
- }
13661
- return false;
13662
- }
13663
- if (part.length) {
13664
- if (part.substring)
13665
- return false;
13666
- continue;
13667
- }
13668
- if (part.quoted && (sel.separator === "*" || (allowAt && sel.separator === "@")) && this.budget.limits.maxExpansionBytes === Infinity && (!part.substring || (sel.kind === "members" && 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)))))) {
13669
- continue;
13670
- }
13671
- return false;
13672
- }
13673
- if (sel.kind === "element") {
13674
- if (part.substring) {
13675
- // Bindings, indices and character widths can change each
13676
- // iteration. The limited element slicer may then need fallback;
13677
- // do not speculate after earlier compound effects have run.
13678
- return false;
13679
- }
13680
- if (part.operator === undefined)
13681
- continue;
13682
- if ((part.operator === "-" || part.operator === ":-" || part.operator === "+" || part.operator === ":+") && part.alternate && !part.length)
13683
- continue;
13684
- if ((part.operator === "^" || part.operator === "^^" || part.operator === "," || part.operator === ",,") && (!part.alternate || part.alternate.parts.length === 0) && !part.length)
13685
- continue;
13686
- if (!part.length && !rawState.nocasematch && this.budget.limits.maxExpansionBytes === Infinity) {
13687
- if ((part.operator === "#" || part.operator === "##" || part.operator === "%" || part.operator === "%%") && part.alternate && part.alternate.parts.length === 1 && part.alternate.parts[0].kind === "text" && !hasGlobOrEscape(part.alternate.parts[0].value, true))
13688
- continue;
13689
- if ((part.operator === "/" || part.operator === "//" || part.operator === "/#" || part.operator === "/%") && part.alternate && part.alternate.parts.length === 1 && part.alternate.parts[0].kind === "text" && part.alternate.parts[0].value.length > 0 && !hasGlobOrEscape(part.alternate.parts[0].value, true) && (!part.replacement || part.replacement.parts.length === 0 || (part.replacement.parts.length === 1 && part.replacement.parts[0].kind === "text" && !part.replacement.parts[0].value.includes("&") && !part.replacement.parts[0].value.includes("\\") && !part.replacement.parts[0].value.startsWith("~"))))
13690
- continue;
13691
- }
13692
- return false;
13693
- }
13640
+ if (getArraySelector(part) !== undefined) {
13641
+ // A loop can change the binding or subscript after preflight. The
13642
+ // limited fast evaluator may then return undefined, after earlier
13643
+ // effects have already run. Use normal expansion for selectors and
13644
+ // their alternates rather than risking corruption or replay.
13694
13645
  return false;
13695
13646
  }
13696
13647
  if (part.length || part.substring || part.operator !== undefined) {
@@ -13925,13 +13876,19 @@ export class Runtime {
13925
13876
  return false;
13926
13877
  }
13927
13878
  else if (w0Plain === "awk") {
13928
- if (plainOps.length !== opCount || this.evalSyncAwk([], plainOps) === undefined)
13879
+ const input = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
13880
+ if (plainOps.length !== opCount || input === undefined)
13881
+ return false;
13882
+ const lines = input.length === 0 ? [] : input.split("\n");
13883
+ if (input.endsWith("\n"))
13884
+ lines.pop();
13885
+ if (this.evalSyncAwk(lines, plainOps) === undefined)
13929
13886
  return false;
13930
13887
  }
13931
13888
  else if (w0Plain === "jq") {
13932
13889
  if (plainOps.length !== opCount)
13933
13890
  return false;
13934
- const jqIn = this.extractLoopInvariantJqInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
13891
+ const jqIn = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
13935
13892
  if (jqIn === undefined || this.evalSyncJq(jqIn, plainOps) === undefined)
13936
13893
  return false;
13937
13894
  }
@@ -13964,7 +13921,7 @@ export class Runtime {
13964
13921
  return false;
13965
13922
  }
13966
13923
  else if (w0Plain === "numfmt") {
13967
- const nmCheck = this.extractLoopInvariantJqInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
13924
+ const nmCheck = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
13968
13925
  if (nmCheck === undefined || plainOps.length !== opCount || this.evalSyncNumfmt(nmCheck.split("\n"), plainOps) === undefined)
13969
13926
  return false;
13970
13927
  }
@@ -14087,7 +14044,15 @@ export class Runtime {
14087
14044
  return false;
14088
14045
  }
14089
14046
  else if (sName === "awk") {
14090
- if (this.evalSyncAwk([], sPlainArgs) === undefined)
14047
+ if (sIdx !== 1)
14048
+ return false;
14049
+ const input = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
14050
+ if (input === undefined)
14051
+ return false;
14052
+ const lines = input.length === 0 ? [] : input.split("\n");
14053
+ if (input.endsWith("\n"))
14054
+ lines.pop();
14055
+ if (this.evalSyncAwk(lines, sPlainArgs) === undefined)
14091
14056
  return false;
14092
14057
  }
14093
14058
  else if (sName === "grep") {
@@ -14101,7 +14066,7 @@ export class Runtime {
14101
14066
  else if (sName === "jq") {
14102
14067
  if (sIdx !== 1)
14103
14068
  return false;
14104
- const jqIn = this.extractLoopInvariantJqInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
14069
+ const jqIn = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
14105
14070
  if (jqIn === undefined || this.evalSyncJq(jqIn, sPlainArgs) === undefined)
14106
14071
  return false;
14107
14072
  }
@@ -14124,7 +14089,7 @@ export class Runtime {
14124
14089
  else if (sName === "numfmt") {
14125
14090
  if (sIdx !== 1)
14126
14091
  return false;
14127
- const nmCheck = this.extractLoopInvariantJqInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState);
14092
+ const nmCheck = this.extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState)?.trim();
14128
14093
  if (nmCheck === undefined || this.evalSyncNumfmt(nmCheck.split("\n"), sPlainArgs) === undefined)
14129
14094
  return false;
14130
14095
  }
@@ -14201,50 +14166,15 @@ export class Runtime {
14201
14166
  }
14202
14167
  return undefined;
14203
14168
  }
14204
- execSyncUnsetStep(step, rawState, io, monitor, touched) {
14169
+ execSyncUnsetStep(step, rawState, monitor, touched) {
14205
14170
  const uVars = step.unsetVars;
14206
- const store = monitor.store;
14207
14171
  let lastExpanded = "";
14208
- for (let ui = 0; ui < uVars.length; ui++) {
14209
- const uvn = uVars[ui];
14210
- if (uvn.charCodeAt(0) === 64) {
14211
- const wIdx = Number(uvn.slice(1));
14212
- const wu = step.cmd.words[wIdx];
14213
- const tExpanded = this.fastValueWord(wu, rawState, io, false, false, false, false, 0, step.line);
14214
- lastExpanded = tExpanded;
14215
- const bracket = tExpanded.indexOf("[");
14216
- if (bracket > 0 && tExpanded.endsWith("]") && store) {
14217
- const arrName = tExpanded.slice(0, bracket);
14218
- const subStr = tExpanded.slice(bracket + 1, -1);
14219
- const binding = store.get(arrName);
14220
- if (binding) {
14221
- if (binding.associative) {
14222
- const id = fastStringHexIdentity(subStr);
14223
- const existingKey = binding.keys.get(id);
14224
- if (existingKey !== undefined) {
14225
- binding.remove(existingKey.index);
14226
- store.changed(monitor.chargeInternal(syncRestorationCharge, syncRestorationTickets), arrName);
14227
- }
14228
- }
14229
- else {
14230
- const idxNum = /^(?:0|[1-9][0-9]{0,8})$/.test(subStr)
14231
- ? Number(subStr)
14232
- : resolveSimpleArithOperand(subStr, rawState, monitor, store, this._syncArithRawWriteOnly ? this._syncArithTouched : undefined);
14233
- const resolvedIdx = idxNum !== undefined ? (idxNum >= 0 ? idxNum : binding.maximum + 1 + idxNum) : undefined;
14234
- if (resolvedIdx !== undefined && resolvedIdx >= 0 && binding.values.has(resolvedIdx)) {
14235
- binding.remove(resolvedIdx);
14236
- store.changed(monitor.chargeInternal(syncRestorationCharge, syncRestorationTickets), arrName);
14237
- }
14238
- }
14239
- }
14240
- }
14241
- }
14242
- else {
14243
- delete rawState.variables[uvn];
14244
- monitor.proxy.exported.delete(uvn);
14245
- touched.add(uvn);
14246
- lastExpanded = uvn;
14247
- }
14172
+ for (const uvn of uVars) {
14173
+ delete rawState.variables[uvn];
14174
+ rawState.exported.delete(uvn);
14175
+ monitor.proxy.exported.delete(uvn);
14176
+ touched.add(uvn);
14177
+ lastExpanded = uvn;
14248
14178
  }
14249
14179
  return lastExpanded;
14250
14180
  }
@@ -14268,26 +14198,7 @@ export class Runtime {
14268
14198
  if (vn && startIdx === 1 && hasShellFunction(rawState, vn))
14269
14199
  return undefined;
14270
14200
  if (!vn || vn.startsWith("-") || !isShellIdentifier(vn) || controlNames.has(vn) || vn === "OPTIND" || rawState.readonlyVariables?.has(vn) || rawState.variableAttributes?.get(vn) || store?.get(vn) || stateMonitor(rawState)?.hasOverlay(vn) || rawState.locals.some(f => f.has(vn))) {
14271
- const wu = cmd.words[i];
14272
- const p0 = wu.parts[0];
14273
- const pLast = wu.parts[wu.parts.length - 1];
14274
- const m = p0?.kind === "text" ? /^([a-zA-Z_][a-zA-Z0-9_]*)\[/.exec(p0.value) : undefined;
14275
- const arrName = m?.[1];
14276
- const b = arrName ? store?.get(arrName) : undefined;
14277
- if (arrName &&
14278
- b &&
14279
- b.references === 1 &&
14280
- !store.watches.has(arrName) &&
14281
- !rawState.readonlyVariables?.has(arrName) &&
14282
- !hasUnpreparedLocal(rawState, arrName) &&
14283
- !stateMonitor(rawState)?.hasOverlay(arrName) &&
14284
- pLast?.kind === "text" &&
14285
- pLast.value.endsWith("]") &&
14286
- this.isPureSyncValueWord(wu, rawState) &&
14287
- (p0.quoted || rawState.noglob || (!rawState.nullglob && this._isMemoryBackingFs && tryGetMemoryDirectoryEntryNamesSync(this.backingFs, rawState.cwd)?.size === 0))) {
14288
- vars.push(`@${i}`);
14289
- continue;
14290
- }
14201
+ // Array unsets need the full builtin: arithmetic, @/* and maximum-index updates.
14291
14202
  return undefined;
14292
14203
  }
14293
14204
  vars.push(vn);
@@ -15052,6 +14963,10 @@ export class Runtime {
15052
14963
  return false;
15053
14964
  }
15054
14965
  const assignment = this.assignment(w0);
14966
+ // Eligibility is checked against the entry locale. Changing it can make
14967
+ // otherwise pure expansions require the asynchronous byte-aware path.
14968
+ if (assignment && (assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG"))
14969
+ return false;
15055
14970
  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)) {
15056
14971
  return false;
15057
14972
  }
@@ -16855,7 +16770,8 @@ export class Runtime {
16855
16770
  }
16856
16771
  else {
16857
16772
  monitor.values.invalidate(varName);
16858
- monitor.proxy.exported.delete(varName);
16773
+ if (!rawState.exported.has(varName))
16774
+ monitor.proxy.exported.delete(varName);
16859
16775
  }
16860
16776
  }
16861
16777
  }
@@ -17120,7 +17036,7 @@ export class Runtime {
17120
17036
  };
17121
17037
  return visit(script);
17122
17038
  }
17123
- extractLoopInvariantJqInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState) {
17039
+ extractLoopInvariantInput(cmd, hasSingleStdinRedir, hasSingleHereStringRedir, rawState) {
17124
17040
  const w0 = cmd.words[0]?.plain;
17125
17041
  const resolveInvariantWord = (w) => {
17126
17042
  if (w.plain !== undefined)
@@ -17137,10 +17053,10 @@ export class Runtime {
17137
17053
  }
17138
17054
  return undefined;
17139
17055
  };
17140
- if (w0 === "jq" || w0 === "numfmt") {
17056
+ if (w0 === "jq" || w0 === "numfmt" || w0 === "awk") {
17141
17057
  if (hasSingleHereStringRedir) {
17142
17058
  const resolved = resolveInvariantWord(cmd.redirects[0].target);
17143
- return resolved !== undefined ? resolved.trim() : undefined;
17059
+ return resolved !== undefined ? resolved + "\n" : undefined;
17144
17060
  }
17145
17061
  if (this._fileWrites?.size || this._outputFiles?.size)
17146
17062
  return undefined;
@@ -17152,12 +17068,12 @@ export class Runtime {
17152
17068
  const vCheck = this.tryReadMemoryFileViewSync(resolvePath(rawState.cwd, fPlain), false);
17153
17069
  if (!vCheck || vCheck.byteLength > 16384 || vCheck.includes(0))
17154
17070
  return undefined;
17155
- return sharedSyncPipeDecoder.decode(vCheck).trim();
17071
+ return sharedSyncPipeDecoder.decode(vCheck);
17156
17072
  }
17157
17073
  if (w0 === "cat") {
17158
17074
  if (hasSingleHereStringRedir) {
17159
17075
  const resolved = resolveInvariantWord(cmd.redirects[0].target);
17160
- return resolved !== undefined ? resolved.trim() : undefined;
17076
+ return resolved !== undefined ? resolved + "\n" : undefined;
17161
17077
  }
17162
17078
  if (this._fileWrites?.size || this._outputFiles?.size)
17163
17079
  return undefined;
@@ -17169,12 +17085,12 @@ export class Runtime {
17169
17085
  const vCheck = this.tryReadMemoryFileViewSync(resolvePath(rawState.cwd, fPlain), false);
17170
17086
  if (!vCheck || vCheck.byteLength > 16384 || vCheck.includes(0))
17171
17087
  return undefined;
17172
- return sharedSyncPipeDecoder.decode(vCheck).trim();
17088
+ return sharedSyncPipeDecoder.decode(vCheck);
17173
17089
  }
17174
17090
  if (w0 === "echo" && cmd.words.length === 2) {
17175
17091
  const resolved = resolveInvariantWord(cmd.words[1]);
17176
17092
  if (resolved !== undefined && !resolved.startsWith("-"))
17177
- return resolved.trim();
17093
+ return resolved + "\n";
17178
17094
  return undefined;
17179
17095
  }
17180
17096
  if (w0 === "printf" && cmd.words.length >= 2) {
@@ -17186,7 +17102,7 @@ export class Runtime {
17186
17102
  args.push(r);
17187
17103
  }
17188
17104
  const formatted = tryFastPrintf(args);
17189
- return formatted !== undefined ? formatted.trim() : undefined;
17105
+ return formatted;
17190
17106
  }
17191
17107
  return undefined;
17192
17108
  }
@@ -17662,7 +17578,7 @@ export class Runtime {
17662
17578
  break;
17663
17579
  }
17664
17580
  if (step.unsetVars !== undefined) {
17665
- lastArg = this.execSyncUnsetStep(step, rawState, io, monitor, touched);
17581
+ lastArg = this.execSyncUnsetStep(step, rawState, monitor, touched);
17666
17582
  rawState.status = 0;
17667
17583
  continue;
17668
17584
  }
@@ -18240,7 +18156,7 @@ export class Runtime {
18240
18156
  break;
18241
18157
  }
18242
18158
  if (step.unsetVars !== undefined) {
18243
- lastArg = this.execSyncUnsetStep(step, rawState, io, monitor, touched);
18159
+ lastArg = this.execSyncUnsetStep(step, rawState, monitor, touched);
18244
18160
  rawState.status = 0;
18245
18161
  onUpdate({ lastCmd, lastArg });
18246
18162
  continue;
@@ -18425,7 +18341,7 @@ export class Runtime {
18425
18341
  break;
18426
18342
  }
18427
18343
  if (step.unsetVars !== undefined) {
18428
- lastArg = this.execSyncUnsetStep(step, rawState, io, monitor, touched);
18344
+ lastArg = this.execSyncUnsetStep(step, rawState, monitor, touched);
18429
18345
  rawState.status = 0;
18430
18346
  continue;
18431
18347
  }
@@ -27530,17 +27446,17 @@ export class Runtime {
27530
27446
  if (st === "ceil")
27531
27447
  return typeof item === "number" ? [Math.ceil(item)] : undefined;
27532
27448
  if (st === "round")
27533
- return typeof item === "number" ? [Math.round(item)] : undefined;
27449
+ return typeof item === "number" ? [Math.sign(item) * Math.round(Math.abs(item))] : undefined;
27534
27450
  if (st === "abs")
27535
27451
  return typeof item === "number" ? [Math.abs(item)] : undefined;
27536
27452
  if (st === "utf8bytelength")
27537
- return typeof item === "string" ? [Buffer.byteLength(item, "utf8")] : undefined;
27453
+ return typeof item === "string" ? [fastSharedTextEncoder.encode(item).byteLength] : undefined;
27538
27454
  if (st === "explode")
27539
27455
  return typeof item === "string" ? [Array.from(item, c => c.codePointAt(0))] : undefined;
27540
27456
  if (st === "implode") {
27541
27457
  if (!Array.isArray(item) || !item.every(x => typeof x === "number" && Number.isInteger(x) && x >= 0 && x <= 0x10ffff))
27542
27458
  return undefined;
27543
- return [String.fromCodePoint(...item)];
27459
+ return [item.map(cp => String.fromCodePoint(cp >= 0xd800 && cp <= 0xdfff ? 0xfffd : cp)).join("")];
27544
27460
  }
27545
27461
  if (st === "transpose") {
27546
27462
  if (!Array.isArray(item) || !item.every(row => Array.isArray(row)))
@@ -27554,14 +27470,20 @@ export class Runtime {
27554
27470
  return [trans];
27555
27471
  }
27556
27472
  const idxFnM = /^(index|rindex|indices)\(\s*"([^"\\]*)"\s*\)$/.exec(st);
27557
- if (idxFnM && typeof item === "string") {
27473
+ if (idxFnM && (typeof item === "string" || Array.isArray(item))) {
27558
27474
  const fn = idxFnM[1];
27559
27475
  const sub = idxFnM[2];
27560
27476
  const found = [];
27561
- if (sub.length > 0) {
27477
+ if (Array.isArray(item)) {
27478
+ for (let i = 0; i < item.length; i++) {
27479
+ if (item[i] === sub)
27480
+ found.push(i);
27481
+ }
27482
+ }
27483
+ else if (sub.length > 0) {
27562
27484
  let pos = 0;
27563
27485
  while ((pos = item.indexOf(sub, pos)) !== -1) {
27564
- found.push(Array.from(item.slice(0, pos)).length);
27486
+ found.push(fastSharedTextEncoder.encode(item.slice(0, pos)).byteLength);
27565
27487
  pos += 1;
27566
27488
  }
27567
27489
  }
@@ -28599,6 +28521,10 @@ export class Runtime {
28599
28521
  const isExtended = mode.includes("E");
28600
28522
  const isFixed = mode.includes("F");
28601
28523
  const isOnlyMatching = mode.includes("o");
28524
+ // POSIX bracket classes, collating symbols and equivalence classes have
28525
+ // different meanings in JS RegExp. Let the command matcher parse them.
28526
+ if (isExtended && !isFixed && rawPatterns.some(pattern => pattern.includes("[:") || pattern.includes("[.") || pattern.includes("[=")))
28527
+ return undefined;
28602
28528
  if (isCount && (isLineNumber || isOnlyMatching || beforeCtx > 0 || afterCtx > 0))
28603
28529
  return undefined;
28604
28530
  if (isOnlyMatching && (beforeCtx > 0 || afterCtx > 0))
@@ -28609,7 +28535,10 @@ export class Runtime {
28609
28535
  const pat = rawPatterns[0];
28610
28536
  const reFlags = isCaseInsensitive ? "gi" : "g";
28611
28537
  let re;
28612
- if (isExtended && /^[a-zA-Z0-9_ :;,=.+*?|()^\-[\]]+$/.test(pat) && !/\([^)]*[+*][^)]*\)[+*?]/.test(pat)) {
28538
+ // JS alternatives are ordered; only literal alternatives below can be
28539
+ // sorted to implement grep's leftmost-longest choice. Other alternatives
28540
+ // use the command's POSIX matcher through the normal fallback.
28541
+ if (isExtended && !pat.includes("|") && /^[a-zA-Z0-9_ :;,=.+*?()^\-[\]]+$/.test(pat) && !/\([^)]*[+*][^)]*\)[+*?]/.test(pat)) {
28613
28542
  try {
28614
28543
  re = new RegExp(pat, reFlags);
28615
28544
  }
@@ -28960,6 +28889,8 @@ export class Runtime {
28960
28889
  const tm = /^(\$(?:[0-9]+|NF)|NR|NF)\s*(==|!=|>=|<=|>|<)\s*(-?[0-9]+(?:\.[0-9]+)?|"[^"$\\]*")\s*\?\s*(\$(?:[0-9]+|NF)|"[^"$\\]*"|-?[0-9]+(?:\.[0-9]+)?)\s*:\s*(\$(?:[0-9]+|NF)|"[^"$\\]*"|-?[0-9]+(?:\.[0-9]+)?)$/.exec(m[1].trim());
28961
28890
  if (!tm)
28962
28891
  return undefined;
28892
+ if (tm[2] === ">")
28893
+ return undefined; // Unparenthesized print > is output redirection.
28963
28894
  parts.push({ kind: "ternary", lhs: tm[1], op: tm[2], rhs: tm[3], yes: tm[4], no: tm[5] });
28964
28895
  }
28965
28896
  else if (m[2] !== undefined)
@@ -29073,8 +29004,10 @@ export class Runtime {
29073
29004
  else if (p.kind === "int") {
29074
29005
  const idx = p.token === "NF" ? fields.length : Number(p.token);
29075
29006
  const s = idx === 0 ? l : (idx >= 1 && idx <= fields.length ? fields[idx - 1] : "");
29076
- const n = Number(/^[ \t]*([+-]?(?:\d+(?:\.\d*)?|\.\d+))/.exec(s)?.[1] ?? 0);
29077
- out += String(Math.trunc(n));
29007
+ const n = awkNumber(awkInputValue(s));
29008
+ if (!Number.isFinite(n))
29009
+ return undefined;
29010
+ out += awkValueText(awkNumeric(Math.trunc(n)));
29078
29011
  }
29079
29012
  else if (p.kind === "ternary") {
29080
29013
  const resolveAtom = (tok) => {
@@ -29092,16 +29025,10 @@ export class Runtime {
29092
29025
  };
29093
29026
  const lv = resolveAtom(p.lhs);
29094
29027
  const rv = resolveAtom(p.rhs);
29095
- const isStrCmp = p.rhs.startsWith("\"");
29096
- let cond = false;
29097
- if (isStrCmp) {
29098
- cond = p.op === "==" ? lv === rv : p.op === "!=" ? lv !== rv : p.op === ">" ? lv > rv : p.op === "<" ? lv < rv : p.op === ">=" ? lv >= rv : lv <= rv;
29099
- }
29100
- else {
29101
- const ln = Number(lv) || 0;
29102
- const rn = Number(rv) || 0;
29103
- cond = p.op === "==" ? ln === rn : p.op === "!=" ? ln !== rn : p.op === ">" ? ln > rn : p.op === "<" ? ln < rn : p.op === ">=" ? ln >= rn : ln <= rn;
29104
- }
29028
+ const left = p.lhs === "NR" || p.lhs === "NF" ? awkNumeric(Number(lv)) : awkInputValue(lv);
29029
+ const right = p.rhs.startsWith('"') ? awkString(rv) : awkNumeric(Number(rv));
29030
+ const comparison = awkCompare(left, right, "%.6g");
29031
+ const cond = p.op === "==" ? comparison === 0 : p.op === "!=" ? comparison !== 0 : p.op === ">" ? comparison > 0 : p.op === "<" ? comparison < 0 : p.op === ">=" ? comparison >= 0 : comparison <= 0;
29105
29032
  out += resolveAtom(cond ? p.yes : p.no);
29106
29033
  }
29107
29034
  else if (p.kind === "arith") {
@@ -29366,13 +29293,14 @@ export class Runtime {
29366
29293
  if (s.length === 0)
29367
29294
  return [""];
29368
29295
  const res = [];
29296
+ const chars = Array.from(s);
29369
29297
  const escapes = { b: "\b", f: "\f", n: "\n", r: "\r", t: "\t", v: "\v", "0": "" };
29370
- for (let i = 0; i < s.length; i++) {
29371
- const ch = s[i];
29298
+ for (let i = 0; i < chars.length; i++) {
29299
+ const ch = chars[i];
29372
29300
  if (ch === "\\") {
29373
- if (i + 1 >= s.length)
29301
+ if (i + 1 >= chars.length)
29374
29302
  return undefined;
29375
- const esc = s[++i];
29303
+ const esc = chars[++i];
29376
29304
  res.push(escapes[esc] ?? esc);
29377
29305
  }
29378
29306
  else {