@sema-agent/core 7.46.0 → 7.47.0
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/CHANGELOG.md +52 -0
- package/dist/core/allow-entry.d.ts +1 -1
- package/dist/core/allow-entry.js +3 -4
- package/dist/core/ask-origin.d.ts +35 -12
- package/dist/core/ask-origin.js +23 -6
- package/dist/core/engine-notice.d.ts +7 -4
- package/dist/core/gate-fold.js +2 -2
- package/dist/core/gate-lanes.js +2 -1
- package/dist/core/gate-outcome.d.ts +4 -2
- package/dist/core/gate-outcome.js +2 -2
- package/dist/core/permission-rule-model.d.ts +12 -6
- package/dist/core/permission-rule-model.js +10 -2
- package/dist/core/permission-rules.d.ts +4 -5
- package/dist/core/permission-rules.js +28 -15
- package/dist/core/runner/gate-exit.d.ts +4 -1
- package/dist/core/runner/gate-exit.js +5 -1
- package/dist/core/runner/prepare-hands-readface.js +5 -5
- package/dist/core/runner/prepare-policy-chain.js +2 -1
- package/dist/core/shell-wrapper-projection.js +1 -1
- package/dist/core/shell-wrapper-table.d.ts +6 -6
- package/dist/core/shell-wrapper-table.js +2 -2
- package/dist/core/tool-catalog-entries.js +3 -2
- package/dist/core/tool-face.d.ts +10 -1
- package/dist/core/tool-name-readings.d.ts +8 -0
- package/dist/core/tool-name-readings.js +2 -0
- package/dist/core/tool-policy.d.ts +10 -9
- package/dist/core/tool-policy.js +2 -2
- package/dist/core/tool-registry.d.ts +9 -0
- package/dist/core/tool-registry.js +10 -0
- package/dist/core/write-protect.d.ts +10 -3
- package/dist/core/write-protect.js +11 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shell/lex.d.ts +15 -0
- package/dist/shell/lex.js +58 -13
- package/dist/tools/fs/bash-program-position.d.ts +9 -6
- package/dist/tools/fs/bash-program-position.js +13 -10
- package/dist/tools/fs/program-read-model.d.ts +24 -7
- package/dist/tools/fs/program-read-model.js +7 -0
- package/dist/tools/fs/program-read-model.rows.js +18 -7
- package/dist/tools/fs/removal-targets.js +14 -4
- package/dist/tools/fs/shell-read-argv.d.ts +5 -4
- package/dist/tools/fs/shell-read-argv.js +5 -3
- package/dist/tools/fs/shell-read-bindings.d.ts +46 -19
- package/dist/tools/fs/shell-read-bindings.js +38 -29
- package/dist/tools/fs/shell-read-establish.js +3 -3
- package/dist/tools/fs/shell-read-plan.d.ts +2 -1
- package/dist/tools/fs/shell-read-plan.js +32 -36
- package/dist/tools/fs/shell-read-word.d.ts +38 -7
- package/dist/tools/fs/shell-read-word.js +26 -8
- package/dist/tools/fs/shell-read-write-targets.js +4 -3
- package/dist/tools/fs/shell-reentry-text.d.ts +98 -0
- package/dist/tools/fs/shell-reentry-text.js +115 -2
- package/dist/tools/monitor.d.ts +3 -3
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -3
- package/wire-changes/core@7.47.0.json +412 -0
package/dist/shell/lex.js
CHANGED
|
@@ -357,6 +357,7 @@ class WordBuilder {
|
|
|
357
357
|
marks = [];
|
|
358
358
|
subscript;
|
|
359
359
|
compound = false;
|
|
360
|
+
widenedClasses = [];
|
|
360
361
|
begin(src) { if (!this.started)
|
|
361
362
|
this.at = src; }
|
|
362
363
|
push(w, f, by, src) {
|
|
@@ -402,6 +403,17 @@ class WordBuilder {
|
|
|
402
403
|
mark(reason, rawAt) { this.marks.push({ at: rawAt, reason }); }
|
|
403
404
|
inSubscript() { return this.subscript !== undefined && this.subscript.rawEnd === undefined; }
|
|
404
405
|
foldedOf(r) { return r >= this.foldedAt.length ? this.folded.length : this.foldedAt[r]; }
|
|
406
|
+
classSpelled(from, to) {
|
|
407
|
+
let out = "";
|
|
408
|
+
let at = from;
|
|
409
|
+
for (const [a, b] of this.widenedClasses) {
|
|
410
|
+
if (a < at || b > to)
|
|
411
|
+
continue;
|
|
412
|
+
out += `${this.folded.slice(at, a)}?`;
|
|
413
|
+
at = b;
|
|
414
|
+
}
|
|
415
|
+
return out + this.folded.slice(at, to);
|
|
416
|
+
}
|
|
405
417
|
slice(from, to) {
|
|
406
418
|
const out = [];
|
|
407
419
|
for (const p of this.pieces) {
|
|
@@ -519,9 +531,23 @@ class WordBuilder {
|
|
|
519
531
|
else if (stack.length > 0 && (d === "," || (d === "." && this.raw[j + 1] === ".")))
|
|
520
532
|
stack[stack.length - 1].listed = true;
|
|
521
533
|
}
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
534
|
+
const text = this.folded;
|
|
535
|
+
const nf = text.length;
|
|
536
|
+
const readableAt = (f) => f < nf && readable(this.rawOfFolded[f]);
|
|
537
|
+
const unitClose = (d) => {
|
|
538
|
+
const at = new Array(nf + 2).fill(-1);
|
|
539
|
+
for (let f = nf - 2; f >= 0; f--)
|
|
540
|
+
at[f] = readableAt(f) && readableAt(f + 1) && text[f] === d && text[f + 1] === "]" ? f : at[f + 1];
|
|
541
|
+
return at;
|
|
542
|
+
};
|
|
543
|
+
const unitCloses = new Map([":", "=", "."].map((d) => [d, unitClose(d)]));
|
|
544
|
+
const nextBracketClose = new Array(nf + 2).fill(-1);
|
|
545
|
+
const nextSlash = new Array(nf + 1).fill(nf);
|
|
546
|
+
for (let f = nf - 1; f >= 0; f--) {
|
|
547
|
+
const unitEnd = readableAt(f) && text[f] === "[" && readableAt(f + 1) ? unitCloses.get(text[f + 1] ?? "")?.[f + 2] ?? -1 : -1;
|
|
548
|
+
nextBracketClose[f] = readableAt(f) && text[f] === "]" ? f : unitEnd >= 0 ? nextBracketClose[unitEnd + 2] : nextBracketClose[f + 1];
|
|
549
|
+
nextSlash[f] = text[f] === "/" ? f : nextSlash[f + 1];
|
|
550
|
+
}
|
|
525
551
|
const ranges = [];
|
|
526
552
|
for (let k = 0; k < n; k++) {
|
|
527
553
|
if (!readable(k))
|
|
@@ -536,19 +562,35 @@ class WordBuilder {
|
|
|
536
562
|
continue;
|
|
537
563
|
}
|
|
538
564
|
if (c === "*" || c === "?") {
|
|
539
|
-
ranges.push({ from: k, to: k + 1, exp: { kind: "glob", yields: "many" } });
|
|
565
|
+
ranges.push({ from: k, to: k + 1, exp: { kind: "glob", yields: "many", pattern: { folded: c, written: c, judged: c } } });
|
|
540
566
|
continue;
|
|
541
567
|
}
|
|
542
568
|
if (c === "[") {
|
|
543
|
-
|
|
544
|
-
|
|
569
|
+
if (this.rawOfFolded[this.foldedOf(k)] !== k)
|
|
570
|
+
continue;
|
|
571
|
+
let j = this.foldedOf(k) + 1;
|
|
572
|
+
if ((text[j] === "!" || text[j] === "^") && readableAt(j))
|
|
545
573
|
j++;
|
|
546
|
-
if (
|
|
574
|
+
if (text[j] === "]" && readableAt(j))
|
|
547
575
|
j++;
|
|
548
|
-
const
|
|
549
|
-
if (
|
|
576
|
+
const closeAt = j < nf ? nextBracketClose[j] : -1;
|
|
577
|
+
if (closeAt < 0 || nextSlash[j] < closeAt)
|
|
550
578
|
continue;
|
|
551
|
-
|
|
579
|
+
const close = this.rawOfFolded[closeAt];
|
|
580
|
+
let expands = false;
|
|
581
|
+
for (let m = k + 1; m < close && !expands; m++)
|
|
582
|
+
expands = inExp[m] || braceClose.get(m)?.listed === true;
|
|
583
|
+
let widened = false;
|
|
584
|
+
for (let m = k + 1; m < close && !widened; m++)
|
|
585
|
+
widened = this.rawQuoted[m] !== undefined;
|
|
586
|
+
for (let f = j; f < closeAt && !widened; f++)
|
|
587
|
+
widened = readableAt(f) && text[f] === "[" && readableAt(f + 1) && (unitCloses.get(text[f + 1] ?? "")?.[f + 2] ?? -1) >= 0;
|
|
588
|
+
const from = this.foldedOf(k);
|
|
589
|
+
const to = this.foldedOf(close + 1);
|
|
590
|
+
if (!expands && widened)
|
|
591
|
+
this.widenedClasses.push([from, to]);
|
|
592
|
+
const pattern = expands ? {} : { pattern: { folded: this.folded.slice(from, to), written: this.raw.slice(k, close + 1), judged: widened ? "?" : this.folded.slice(from, to) } };
|
|
593
|
+
ranges.push({ from: k, to: close + 1, exp: { kind: "glob", yields: "many", body: this.raw.slice(k + 1, close), ...pattern } });
|
|
552
594
|
k = close;
|
|
553
595
|
}
|
|
554
596
|
}
|
|
@@ -647,18 +689,20 @@ class WordBuilder {
|
|
|
647
689
|
}
|
|
648
690
|
finish(atCommandPosition, tildeSpelling = true) {
|
|
649
691
|
const rawText = this.raw;
|
|
650
|
-
let
|
|
692
|
+
let literalTilde = false;
|
|
651
693
|
let assignment;
|
|
652
694
|
const tildeEnd = this.tildePrefixEnd(0, false);
|
|
653
695
|
if (tildeEnd !== undefined)
|
|
654
696
|
this.carveTilde(0, tildeEnd);
|
|
655
697
|
else {
|
|
656
|
-
|
|
657
|
-
foldedText = literalTildeSpelling(foldedText);
|
|
698
|
+
literalTilde = tildeSpelling;
|
|
658
699
|
assignment = this.readAssignment();
|
|
659
700
|
}
|
|
660
701
|
if (!(assignment !== undefined && assignment.bare && atCommandPosition))
|
|
661
702
|
this.carveBraceAndGlob();
|
|
703
|
+
const spelled = (text) => (literalTilde ? literalTildeSpelling(text) : text);
|
|
704
|
+
const foldedText = spelled(this.folded);
|
|
705
|
+
const judged = this.widenedClasses.length > 0 ? spelled(this.classSpelled(0, this.folded.length)) : undefined;
|
|
662
706
|
if (assignment !== undefined && this.subscript?.text)
|
|
663
707
|
this.mark("subscript_text", this.subscript.rawStart);
|
|
664
708
|
const marks = this.marks.filter((m) => m.reason !== "subscript_text" || assignment?.subscript !== undefined).sort((a, b) => a.at - b.at);
|
|
@@ -679,6 +723,7 @@ class WordBuilder {
|
|
|
679
723
|
kind: "word",
|
|
680
724
|
raw: rawText,
|
|
681
725
|
folded: foldedText,
|
|
726
|
+
...(judged !== undefined ? { judged } : {}),
|
|
682
727
|
pieces,
|
|
683
728
|
quotedSpans: spans,
|
|
684
729
|
expansions: pieces.filter((p) => p.expansion !== undefined),
|
|
@@ -53,12 +53,15 @@ export type ProgramPositionRefusal = {
|
|
|
53
53
|
readonly kind: "control_syntax_at_program_position";
|
|
54
54
|
readonly owner: string;
|
|
55
55
|
readonly word: string;
|
|
56
|
-
/** Not a DECLINE ({@link programPositionRunsShell}): the row WAS read and a SHELL
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*
|
|
61
|
-
readonly
|
|
56
|
+
/** Not a DECLINE ({@link programPositionRunsShell}): the row WAS read and a SHELL — or a program the launcher
|
|
57
|
+
* names by an option of its own (`su -s PROGRAM`) — stands at the program position, run by the launcher at argv
|
|
58
|
+
* index `launcher` and handed the words from index `from` on (none where `from` is the argv's end: `sudo -s`,
|
|
59
|
+
* `chroot DIR`), as that launcher's row composes them (`launchedCommands`, shell-reentry-text.ts). Absent ⇒
|
|
60
|
+
* genuine control syntax, which IS one. */
|
|
61
|
+
readonly runsShell?: {
|
|
62
|
+
readonly launcher: number;
|
|
63
|
+
readonly from: number;
|
|
64
|
+
};
|
|
62
65
|
};
|
|
63
66
|
export interface ProgramPosition {
|
|
64
67
|
/** Indices of the bare control words stripped in front (`!`, `if`, `time`, …), in order; their declared options
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SHELL_CONTROL_PREFIXES } from "../../shell/lex.js";
|
|
2
|
-
import { BLOCK_WORD_NAMES, LAUNCHER_NAMES, feedsArgvFromStdin, launcherOptionStep, programRow, selectedOptions, streamDefaultProgram } from "./program-read-model.js";
|
|
2
|
+
import { BLOCK_WORD_NAMES, LAUNCHER_NAMES, feedsArgvFromStdin, launcherOptionStep, launcherRunsShell, programRow, selectedOptions, streamDefaultProgram } from "./program-read-model.js";
|
|
3
3
|
export const NOT_AUTO_ALLOWED = "— not auto-allowed";
|
|
4
4
|
export function programBasename(word) {
|
|
5
5
|
return word.includes("/") ? (word.split("/").pop() ?? word) : word;
|
|
@@ -79,12 +79,12 @@ export function readProgramPosition(words) {
|
|
|
79
79
|
}
|
|
80
80
|
const launchers = [{ index: i, name: basename }];
|
|
81
81
|
const owner = head;
|
|
82
|
-
const shellRefusal = (
|
|
82
|
+
const shellRefusal = (launcher, from) => ({ kind: "control_syntax_at_program_position", owner, word: "sh", runsShell: { launcher, from } });
|
|
83
83
|
let lastRow = launcherRowOf(basename)?.launcher;
|
|
84
84
|
let owedOperands = lastRow?.requiredOperands ?? 0;
|
|
85
85
|
const chain0 = (program, refusal) => ({ controlWords, written, assignments, launchers, argvFromStdin: false, runsDefaultProgram: false, program, refusal });
|
|
86
|
-
if (
|
|
87
|
-
return chain0(undefined, shellRefusal(
|
|
86
|
+
if (launcherRunsShell(launcherRowOf(basename), folded.slice(i + 1)))
|
|
87
|
+
return chain0(undefined, shellRefusal(i, i + 1));
|
|
88
88
|
let argvFromStdin = feedsArgvFromStdin(basename);
|
|
89
89
|
let lastReentry = argvFromStdin ? i : -1;
|
|
90
90
|
let reentryDefault = argvFromStdin ? streamDefaultProgram(basename) : undefined;
|
|
@@ -92,6 +92,7 @@ export function readProgramPosition(words) {
|
|
|
92
92
|
const runsDefaultProgram = lastReentry === folded.length - 1;
|
|
93
93
|
return { controlWords, written, assignments, launchers, argvFromStdin, runsDefaultProgram, ...(runsDefaultProgram && reentryDefault !== undefined ? { defaultProgram: reentryDefault } : {}), program, refusal };
|
|
94
94
|
};
|
|
95
|
+
const selectsShell = (l, end) => selectedOptions(launcherRowOf(l.name), folded.slice(l.index + 1, end)).some((o) => o.mode === "interactive");
|
|
95
96
|
for (let j = pastLauncherOptions(basename, folded, i + 1); j < folded.length; j++) {
|
|
96
97
|
const t = folded[j];
|
|
97
98
|
if (owedOperands > 0 && !t.startsWith("-")) {
|
|
@@ -100,6 +101,8 @@ export function readProgramPosition(words) {
|
|
|
100
101
|
}
|
|
101
102
|
if (lastRow?.assignmentOperands === true && !t.startsWith("-") && t.includes("="))
|
|
102
103
|
continue;
|
|
104
|
+
if (lastRow?.shellCommand !== undefined && !t.startsWith("-") && selectsShell(launchers[launchers.length - 1], j))
|
|
105
|
+
return chain(undefined, shellRefusal(launchers[launchers.length - 1].index, j));
|
|
103
106
|
const wordBasename = programBasename(t);
|
|
104
107
|
if (feedsArgvFromStdin(wordBasename)) {
|
|
105
108
|
argvFromStdin = true;
|
|
@@ -112,8 +115,8 @@ export function readProgramPosition(words) {
|
|
|
112
115
|
if (LAUNCHER_NAMES.has(wordBasename)) {
|
|
113
116
|
launchers.push({ index: j, name: wordBasename });
|
|
114
117
|
lastRow = launcherRowOf(wordBasename)?.launcher;
|
|
115
|
-
if (
|
|
116
|
-
return chain(undefined, shellRefusal(
|
|
118
|
+
if (launcherRunsShell(launcherRowOf(wordBasename), folded.slice(j + 1)))
|
|
119
|
+
return chain(undefined, shellRefusal(j, j + 1));
|
|
117
120
|
owedOperands = lastRow?.requiredOperands ?? 0;
|
|
118
121
|
j = pastLauncherOptions(wordBasename, folded, j + 1) - 1;
|
|
119
122
|
continue;
|
|
@@ -126,8 +129,8 @@ export function readProgramPosition(words) {
|
|
|
126
129
|
return chain({ index: j, name: t, basename: wordBasename });
|
|
127
130
|
}
|
|
128
131
|
const last = launchers[launchers.length - 1];
|
|
129
|
-
if (lastRow?.whenNoProgram === "shell" ||
|
|
130
|
-
return chain(undefined, shellRefusal());
|
|
132
|
+
if (lastRow?.whenNoProgram === "shell" || selectsShell(last, folded.length))
|
|
133
|
+
return chain(undefined, shellRefusal(last.index, folded.length));
|
|
131
134
|
return chain(undefined);
|
|
132
135
|
}
|
|
133
136
|
export function argvReentryUnjudged(pos, acceptsTarget) {
|
|
@@ -139,12 +142,12 @@ export function argvReentryUnjudged(pos, acceptsTarget) {
|
|
|
139
142
|
export const SHELL_SCAN_DECLINES = Object.freeze(["unreadable_program_position", "stream_fed_operands"]);
|
|
140
143
|
export function programPositionRunsShell(pos, runsShell) {
|
|
141
144
|
if (pos.refusal !== undefined)
|
|
142
|
-
return pos.refusal.kind === "control_syntax_at_program_position" && pos.refusal.runsShell
|
|
145
|
+
return pos.refusal.kind === "control_syntax_at_program_position" && pos.refusal.runsShell !== undefined;
|
|
143
146
|
return pos.program !== undefined && runsShell(pos.program.basename);
|
|
144
147
|
}
|
|
145
148
|
export function shellScanDeclineOf(pos, acceptsStreamTarget) {
|
|
146
149
|
if (pos.refusal !== undefined) {
|
|
147
|
-
return pos.refusal.kind === "control_syntax_at_program_position" && pos.refusal.runsShell
|
|
150
|
+
return pos.refusal.kind === "control_syntax_at_program_position" && pos.refusal.runsShell !== undefined ? undefined : "unreadable_program_position";
|
|
148
151
|
}
|
|
149
152
|
return argvReentryUnjudged(pos, acceptsStreamTarget) ? "stream_fed_operands" : undefined;
|
|
150
153
|
}
|
|
@@ -75,7 +75,8 @@ export type ArgRole =
|
|
|
75
75
|
| {
|
|
76
76
|
readonly kind: "shell-text";
|
|
77
77
|
}
|
|
78
|
-
/** The program
|
|
78
|
+
/** The program a launcher or a builtin runs by name: a function (`compgen -F`), the program a launcher runs in place of
|
|
79
|
+
* a shell (`su -s PROGRAM`). */
|
|
79
80
|
| {
|
|
80
81
|
readonly kind: "command";
|
|
81
82
|
}
|
|
@@ -163,10 +164,10 @@ export interface OptionSpec {
|
|
|
163
164
|
readonly endsOptions?: true;
|
|
164
165
|
/** `banner`: prints and exits; `interactive`: reads commands from its standard input (after its program, `python -i`;
|
|
165
166
|
* instead of a script file, `sh -s`; a launcher's shell, run where no program follows, `sudo -s`); `names-program`: selects
|
|
166
|
-
* the program to run without a value (`node --test
|
|
167
|
-
* (`tail -f`); `repeats`: repeats on an interval until killed (`free -s`);
|
|
168
|
-
* text tells it to (`sed -n`); `forced`: removes without asking, and passes
|
|
169
|
-
* (`rm -f`). */
|
|
167
|
+
* the program to run without a value (`node --test`; a launcher's, from the caller's environment: `su -m`); `follows`:
|
|
168
|
+
* follows its files as they grow, until killed (`tail -f`); `repeats`: repeats on an interval until killed (`free -s`);
|
|
169
|
+
* `quiet`: prints only what the program's own text tells it to (`sed -n`); `forced`: removes without asking, and passes
|
|
170
|
+
* over an operand that is not there (`rm -f`). */
|
|
170
171
|
readonly mode?: OptionMode;
|
|
171
172
|
/** This switch turns the program's own value glob off (`curl -g`): every {@link ArgRole} `programGlob` value of the
|
|
172
173
|
* invocation is read literally. */
|
|
@@ -250,10 +251,22 @@ export interface LauncherSpec {
|
|
|
250
251
|
readonly assignmentOperands?: true;
|
|
251
252
|
/** A bare negative integer in its option section is its own argument (`nice -5 <cmd>`). */
|
|
252
253
|
readonly negativeIntegerIsOwnArgument?: true;
|
|
253
|
-
/** It
|
|
254
|
-
|
|
254
|
+
/** It runs a SHELL in place of a program word, and how the words after it become that shell's argv: `joined` — its
|
|
255
|
+
* non-option words, joined by a space, are the shell's `-c` text (`watch "cat /x"`); `user` — its first operand (past
|
|
256
|
+
* a lone `-`) names the user it runs as, and the words after the user are the shell's own argv, behind a `-c TEXT` its
|
|
257
|
+
* own shell-text option gives (`su [-] [USER [ARG…]]`: an implementation that reads its options anywhere takes them
|
|
258
|
+
* past the user too, one that stops at the user hands them on); the program is its option of role `command` where
|
|
259
|
+
* one is given (`su -s PROGRAM`), or a `names-program` switch's (`su -m`: the caller's `$SHELL`). */
|
|
260
|
+
readonly shellText?: "joined" | "user";
|
|
261
|
+
/** Option spellings that make a {@link shellText} launcher run the program its operands name instead
|
|
262
|
+
* (`runuser -u USER CMD…`). */
|
|
263
|
+
readonly programWith?: readonly string[];
|
|
255
264
|
/** With no program word it runs a shell. */
|
|
256
265
|
readonly whenNoProgram?: "shell";
|
|
266
|
+
/** Where a switch selecting its shell (mode `interactive`: `sudo -s`) has words after it, they are that shell's `-c`
|
|
267
|
+
* text: `escaped` — joined by a space, every character of each but a letter, a digit, `_`, `-` and `$` escaped with a
|
|
268
|
+
* backslash. Absent, the launcher takes no such words (`doas -s CMD` is refused) and they are read as the program. */
|
|
269
|
+
readonly shellCommand?: "escaped";
|
|
257
270
|
/** The spelling a required operand must have (a word spelled otherwise is not one the launcher accepts). Absent: any
|
|
258
271
|
* word that is not option-shaped. */
|
|
259
272
|
readonly requiredOperandSpelling?: RegExp;
|
|
@@ -652,6 +665,10 @@ export declare function launcherOptionPathRole(row: ProgramRow, tok: string): Ex
|
|
|
652
665
|
* separated (2), a value fused to its long name or short letter (1); a switch, an attached-only option, or a cluster of
|
|
653
666
|
* switches (1); a lone `-` where the launcher reads it as its own switch (1). */
|
|
654
667
|
export declare function launcherOptionStep(row: ProgramRow, tok: string): 1 | 2 | undefined;
|
|
668
|
+
/** Does a launcher that runs a shell ({@link LauncherSpec.shellText}) run one over these words — the words after it —
|
|
669
|
+
* rather than the program they name? Not where an implementation selects one of its {@link LauncherSpec.programWith}
|
|
670
|
+
* options (as that implementation reads them: past `--` a word is an operand). */
|
|
671
|
+
export declare function launcherRunsShell(row: ProgramRow, args: readonly string[]): boolean;
|
|
655
672
|
/** The spellings of a launcher's command-line options (`env -S` / `--split-string`): the value is a command line the
|
|
656
673
|
* launcher splits and runs. */
|
|
657
674
|
export declare function commandLineOptionSpellings(row: ProgramRow | undefined): readonly string[];
|
|
@@ -1055,6 +1055,13 @@ export function launcherOptionStep(row, tok) {
|
|
|
1055
1055
|
return 1;
|
|
1056
1056
|
return undefined;
|
|
1057
1057
|
}
|
|
1058
|
+
export function launcherRunsShell(row, args) {
|
|
1059
|
+
const l = row.launcher;
|
|
1060
|
+
if (l?.shellText === undefined)
|
|
1061
|
+
return false;
|
|
1062
|
+
const specs = new Set((l.programWith ?? []).map((s) => (s.startsWith("--") ? mergedTable(row, "long").get(s.slice(2)) : mergedTable(row, "short").get(s.slice(1)))?.spec));
|
|
1063
|
+
return !implementationsOf(row).some((view) => selectedOptions(view, args, "one-implementation").some((o) => specs.has(o)));
|
|
1064
|
+
}
|
|
1058
1065
|
export function commandLineOptionSpellings(row) {
|
|
1059
1066
|
if (row === undefined)
|
|
1060
1067
|
return [];
|
|
@@ -360,8 +360,19 @@ const launcherGrammar = (g, extra = {}) => {
|
|
|
360
360
|
put(spelling, val(SHELL_TEXT));
|
|
361
361
|
for (const spelling of g.shells ?? [])
|
|
362
362
|
put(spelling, { arity: 0, mode: "interactive" });
|
|
363
|
+
for (const spelling of g.programs ?? [])
|
|
364
|
+
put(spelling, { arity: 0, mode: "names-program" });
|
|
363
365
|
return { dialect: "any", short, long, afterFirstOperand: "either", ...extra };
|
|
364
366
|
};
|
|
367
|
+
const SU_OPTIONS = {
|
|
368
|
+
switches: ["-f", "-l", "-P", "-T", "-h", "-V", "--fast", "--login", "--pty", "--no-pty", "--help", "--version"],
|
|
369
|
+
banners: ["-h", "-V"],
|
|
370
|
+
values: { "-g": LABEL, "-G": LABEL, "-w": LABEL, "-s": COMMAND, "--group": LABEL, "--supp-group": LABEL, "--whitelist-environment": LABEL, "--shell": COMMAND },
|
|
371
|
+
shellText: ["-c", "--command", "--session-command"],
|
|
372
|
+
programs: ["-m", "-p", "--preserve-environment"],
|
|
373
|
+
};
|
|
374
|
+
const SU = launcherGrammar(SU_OPTIONS, { stepsDashDash: true });
|
|
375
|
+
const SU_LINUX_ONLY = [...SU_OPTIONS.switches, ...Object.keys(SU_OPTIONS.values), ...SU_OPTIONS.shellText, ...SU_OPTIONS.programs].filter((s) => !["-f", "-l", "-m"].includes(s));
|
|
365
376
|
const launchers = [
|
|
366
377
|
{ key: "env", grammars: implementations(launcherGrammar({
|
|
367
378
|
switches: ["-i", "-v", "--ignore-environment", "--debug", "--list-signal-handling", "--help", "--version"],
|
|
@@ -380,18 +391,18 @@ const launchers = [
|
|
|
380
391
|
optional: { "-i": LABEL, "-l": COUNT, "--replace": LABEL, "--eof": LABEL, "--max-lines": COUNT },
|
|
381
392
|
}, {}), { reads: { "-a": val(READ, { delivers: "operands" }), "--arg-file": val(READ, { delivers: "operands" }) } }, { lacks: ["-d", "-i", "-l", "-a", "--arg-file", "--null", "--no-run-if-empty", "--verbose", "--exit", "--interactive", "--open-tty", "--show-limits", "--help", "--version", "--max-args", "--max-procs", "--delimiter", "--max-chars", "--process-slot-var", "--replace", "--eof", "--max-lines"] }), launcher: {}, reentry: { kind: "argv", defaultProgram: "echo" }, witness: BINARY },
|
|
382
393
|
launcher("busybox"),
|
|
383
|
-
launcher("sudo", { assignmentOperands: true }, launcherGrammar({
|
|
394
|
+
launcher("sudo", { assignmentOperands: true, shellCommand: "escaped" }, launcherGrammar({
|
|
384
395
|
switches: ["-A", "-b", "-E", "-H", "-K", "-k", "-l", "-n", "-P", "-S", "-V", "-v", "--askpass", "--background", "--preserve-env", "--set-home", "--remove-timestamp", "--reset-timestamp", "--list", "--non-interactive", "--preserve-groups", "--stdin", "--version", "--validate", "--bell", "--help"],
|
|
385
396
|
shells: ["-i", "-s", "--login", "--shell"],
|
|
386
397
|
values: {
|
|
387
398
|
"-u": LABEL, "-g": LABEL, "-U": LABEL, "-C": COUNT, "-D": WORKDIR, "-h": LABEL, "-p": LABEL, "-r": LABEL, "-R": ROOTDIR, "-t": LABEL, "-T": COUNT, "-a": LABEL,
|
|
388
399
|
"--user": LABEL, "--group": LABEL, "--other-user": LABEL, "--close-from": COUNT, "--chdir": WORKDIR, "--host": LABEL, "--prompt": LABEL, "--role": LABEL, "--chroot": ROOTDIR, "--type": LABEL, "--command-timeout": COUNT, "--auth-type": LABEL,
|
|
389
400
|
},
|
|
390
|
-
}), { witness: PRIVILEGED }),
|
|
391
|
-
launcher("doas", {}, launcherGrammar({ switches: ["-L", "-n"], shells: ["-s"], values: { "-a": LABEL, "-u": LABEL, "-C": READ } }), { witness: PRIVILEGED }),
|
|
401
|
+
}, { stepsDashDash: true }), { witness: PRIVILEGED }),
|
|
402
|
+
launcher("doas", {}, launcherGrammar({ switches: ["-L", "-n"], shells: ["-s"], values: { "-a": LABEL, "-u": LABEL, "-C": READ } }, { stepsDashDash: true }), { witness: PRIVILEGED }),
|
|
392
403
|
launcher("pkexec", { whenNoProgram: "shell" }, launcherGrammar({ switches: ["--disable-internal-agent", "--keep-cwd", "--version", "--help"], values: { "--user": LABEL } }), { witness: PRIVILEGED }),
|
|
393
|
-
|
|
394
|
-
|
|
404
|
+
{ key: "su", grammars: implementations(SU, { afterFirstOperand: "options" }, { lacks: SU_LINUX_ONLY, afterFirstOperand: "operands" }), launcher: { shellText: "user" }, witness: PRIVILEGED },
|
|
405
|
+
{ key: "runuser", grammars: [launcherGrammar({ ...SU_OPTIONS, values: { ...SU_OPTIONS.values, "-u": LABEL, "--user": LABEL } }, { afterFirstOperand: "options", stepsDashDash: true })], launcher: { shellText: "user", programWith: ["-u", "--user"] }, witness: PRIVILEGED },
|
|
395
406
|
launcher("chroot", { requiredOperands: 1, whenNoProgram: "shell" }, undefined, { witness: PRIVILEGED }),
|
|
396
407
|
launcher("setpriv", {}, undefined, { witness: PRIVILEGED }),
|
|
397
408
|
launcher("nohup", {}, grammar({}, BANNERS)),
|
|
@@ -409,7 +420,7 @@ const launchers = [
|
|
|
409
420
|
{ key: "stdbuf", grammars: implementations(grammar({ i: val(LABEL), o: val(LABEL), e: val(LABEL) }, { input: val(LABEL), output: val(LABEL), error: val(LABEL), ...BANNERS }), {}, { lacks: ["--input", "--output", "--error", "--help", "--version"] }), launcher: {}, witness: BINARY },
|
|
410
421
|
launcher("timeout", { requiredOperands: 1, requiredOperandSpelling: DURATION_SPELLING }, grammar({ v: SWITCH, k: val(COUNT), s: val(LABEL) }, { foreground: SWITCH, "preserve-status": SWITCH, verbose: SWITCH, "kill-after": val(COUNT), signal: val(LABEL), ...BANNERS })),
|
|
411
422
|
{ key: "time", grammars: implementations(grammar({ p: SWITCH, a: SWITCH, v: SWITCH, q: SWITCH, f: val(LABEL), o: val(WRITE) }, { portability: SWITCH, append: SWITCH, verbose: SWITCH, quiet: SWITCH, help: SWITCH, version: SWITCH, format: val(LABEL), output: val(WRITE) }, { stepsDashDash: true }), {}, { lacks: ["-v", "-q", "-f", "--portability", "--append", "--verbose", "--quiet", "--help", "--version", "--format", "--output"] }), launcher: {}, witness: BINARY },
|
|
412
|
-
launcher("watch", { shellText:
|
|
423
|
+
launcher("watch", { shellText: "joined" }, undefined, { witness: SHELL_TEXT_LAUNCHER }),
|
|
413
424
|
launcher("setsid", {}, launcherGrammar({ switches: ["-c", "-f", "-w", "--ctty", "--fork", "--wait", "--help", "--version"] })),
|
|
414
425
|
launcher("flock", { requiredOperands: 1 }, launcherGrammar({
|
|
415
426
|
switches: ["-s", "-x", "-u", "-n", "-o", "-F", "--shared", "--exclusive", "--unlock", "--nonblock", "--nb", "--close", "--no-fork", "--verbose", "--help", "--version"],
|
|
@@ -425,7 +436,7 @@ const launchers = [
|
|
|
425
436
|
values: { "-t": COUNT, "-S": LABEL, "-G": LABEL, "--target": COUNT, "--setuid": LABEL, "--setgid": LABEL },
|
|
426
437
|
optional: { "-r": ROOTDIR, "-w": WORKDIR, "--root": ROOTDIR, "--wd": WORKDIR },
|
|
427
438
|
}), { witness: LINUX_NAMESPACE }),
|
|
428
|
-
launcher("script", { shellText:
|
|
439
|
+
launcher("script", { shellText: "joined" }, launcherGrammar({ shellText: ["-c", "--command"] }), { witness: SHELL_TEXT_LAUNCHER }),
|
|
429
440
|
launcher("numactl", {}, undefined, { witness: LINUX_NAMESPACE }),
|
|
430
441
|
launcher("prlimit", {}, undefined, { witness: LINUX_NAMESPACE }),
|
|
431
442
|
launcher("systemd-run", {}, undefined, { witness: { declared: "hands the program to a service manager" } }),
|
|
@@ -2,7 +2,8 @@ import { planShellReads } from "./shell-read-plan.js";
|
|
|
2
2
|
import { literalTextOf } from "../../shell/lex.js";
|
|
3
3
|
import { createShellReadEstablisher, establishShellReadsLexically } from "./shell-read-establish.js";
|
|
4
4
|
import { isAbsolutePathForm, isWinFormPath, normalizeAbsPathLexically } from "./safety.js";
|
|
5
|
-
import { reentryText } from "./shell-reentry-text.js";
|
|
5
|
+
import { launchedCommands, launchedReadings, reentryText, spellLaunched } from "./shell-reentry-text.js";
|
|
6
|
+
import { readProgramPosition } from "./bash-program-position.js";
|
|
6
7
|
import { bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
|
|
7
8
|
import { programArgs } from "./shell-read-write-targets.js";
|
|
8
9
|
import { implementationsOf, programRow, runsShellText, selectedOptions, writeReadings } from "./program-read-model.js";
|
|
@@ -167,10 +168,19 @@ function runtimeScriptOf(segment) {
|
|
|
167
168
|
const program = segment.verb;
|
|
168
169
|
if (segment.reentry !== "shell" || program === undefined || !runsShellText(program.basename))
|
|
169
170
|
return undefined;
|
|
170
|
-
const at = segment.argv.findIndex((w) => w.folded === program.name);
|
|
171
171
|
const MARK = "\u0000";
|
|
172
|
-
const
|
|
173
|
-
|
|
172
|
+
const spell = (k, from = 0) => {
|
|
173
|
+
const w = segment.argv[k];
|
|
174
|
+
const literal = literalTextOf(w.pieces);
|
|
175
|
+
return literal !== undefined ? literal.slice(from) : `${MARK}${w.raw.slice(from)}`;
|
|
176
|
+
};
|
|
177
|
+
const pos = readProgramPosition(segment.argv);
|
|
178
|
+
const named = pos.program !== undefined && pos.refusal === undefined ? [{ shell: program.basename, words: segment.argv.slice(pos.program.index + 1).map((_, k) => spell(pos.program.index + 1 + k)) }] : [];
|
|
179
|
+
const launched = launchedCommands(segment.argv, pos).flatMap((c) => {
|
|
180
|
+
const shell = launchedReadings(segment.argv, c).shell;
|
|
181
|
+
return shell === undefined ? [] : spellLaunched(segment.argv, c.args, { word: spell }).map((words) => ({ shell, words }));
|
|
182
|
+
});
|
|
183
|
+
const texts = [...named, ...launched].flatMap(({ shell, words }) => [reentryText(shell, words), ...(segment.stdin ?? []).map((s) => reentryText(shell, words, s.literal ?? `${MARK}${s.from}`))]);
|
|
174
184
|
const text = texts.find((t) => t?.includes(MARK) === true);
|
|
175
185
|
return text === undefined ? undefined : text.split(MARK).join("");
|
|
176
186
|
}
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { type LexedWord } from "../../shell/lex.js";
|
|
9
9
|
import { type Read } from "./shell-read-word.js";
|
|
10
|
-
import { type BindingTable, type
|
|
10
|
+
import { type BindingTable, type FloorValue } from "./shell-read-bindings.js";
|
|
11
11
|
import { subcommandOf, type ArgRole, type ProgramRow } from "./program-read-model.js";
|
|
12
12
|
import type { ReadCandidate, ReadCandidateProvenance } from "./shell-read-plan.js";
|
|
13
13
|
/** A candidate before the threading fills its bases. `alternatives`: the other spellings its word may expand to (a name
|
|
14
|
-
* bound to several values) — one candidate each once the segment's reads
|
|
14
|
+
* bound to several values), each with the pattern its names are matched by — one candidate each once the segment's reads
|
|
15
|
+
* are read. */
|
|
15
16
|
export type Bare = Omit<ReadCandidate, "bases"> & {
|
|
16
|
-
readonly alternatives?: readonly
|
|
17
|
-
readonly floors?: readonly
|
|
17
|
+
readonly alternatives?: readonly Pick<ReadCandidate, "spelled" | "pattern">[];
|
|
18
|
+
readonly floors?: readonly FloorValue[];
|
|
18
19
|
};
|
|
19
20
|
/** What reading a segment's words found beside its candidates. */
|
|
20
21
|
export interface WordContext {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { literalTildeSpelling } from "../../shell/lex.js";
|
|
2
|
-
import { expandsOf, readOfText, rewritten } from "./shell-read-word.js";
|
|
2
|
+
import { expandsOf, patternOf, readOfText, rewritten, spellingOf } from "./shell-read-word.js";
|
|
3
3
|
import { arithmeticSubstitutions } from "./shell-read-bindings.js";
|
|
4
4
|
import { argvProgramOf, attachedOptionPayloads, isPathShapedToken } from "./bash-readonly-classifier.js";
|
|
5
5
|
import { assignmentArgumentsAreNotPaths, evaluatesOperands, messageWords, moduleTailOf, objectPathsOf, opensNoOperand, programRow, programTextMayOpen, programTextOpensOf, readArgs, selectsContentsTraversal, slotValueCandidates, subcommandOf, valuesAreLiteral, walksCwdWithoutOperand, wordsReadBySomeImplementations, } from "./program-read-model.js";
|
|
@@ -10,14 +10,16 @@ export function pathSubcommandOf(row, args) {
|
|
|
10
10
|
export const programTextSentence = (language) => `runs inline ${language} program text, which may open files this station does not read`;
|
|
11
11
|
export function bare(r, provenance, spelled = r.folded) {
|
|
12
12
|
const whole = spelled === r.folded;
|
|
13
|
-
|
|
13
|
+
const alternatives = r.values !== undefined && r.values.length > 1 ? r.values.slice(1).map((v) => { const pattern = patternOf(v); return { spelled: spellingOf(v), ...(pattern !== undefined ? { pattern } : {}) }; }) : undefined;
|
|
14
|
+
if (!whole && alternatives !== undefined)
|
|
14
15
|
return { spelled, raw: r.raw, provenance, expands: "unresolvable" };
|
|
15
16
|
return {
|
|
16
17
|
spelled,
|
|
17
18
|
raw: r.raw,
|
|
18
19
|
provenance,
|
|
19
20
|
expands: r.expands,
|
|
20
|
-
...(whole && r.
|
|
21
|
+
...(whole && r.pattern !== undefined ? { pattern: r.pattern } : {}),
|
|
22
|
+
...(whole && alternatives !== undefined ? { alternatives } : {}),
|
|
21
23
|
...(whole && r.homeFloor !== undefined ? { floorSpelled: r.homeFloor } : {}),
|
|
22
24
|
...(whole && r.floorValues !== undefined ? { floors: r.floorValues } : {}),
|
|
23
25
|
};
|
|
@@ -26,9 +26,34 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { type LexedRedirect, type LexedSegment, type LexedToken, type LexedWord } from "../../shell/lex.js";
|
|
28
28
|
import { type DeclaredAttribute } from "./program-read-model.js";
|
|
29
|
-
/** A value a name may hold
|
|
30
|
-
* list word the shell globs before the loop binds it
|
|
31
|
-
|
|
29
|
+
/** A value a name may hold — the text the program receives ({@link spelled}: what a shell re-parsing it reads), literal
|
|
30
|
+
* throughout, or a PATTERN whose matches are the values (a `for` list word the shell globs before the loop binds it;
|
|
31
|
+
* `spelled` is then the pattern as the program receives it where it matches nothing): `glob`, with its `parts` — the
|
|
32
|
+
* value in runs: a literal run (quoted or literal text, a settled value) and a pattern run (glob pieces as written,
|
|
33
|
+
* quoting kept, and as a reader judges them — {@link WordExpansion.pattern}). The one producer is the piece walk
|
|
34
|
+
* ({@link valueOf}). */
|
|
35
|
+
export type BoundValue = {
|
|
36
|
+
readonly spelled: string;
|
|
37
|
+
readonly glob?: never;
|
|
38
|
+
readonly parts?: never;
|
|
39
|
+
} | {
|
|
40
|
+
readonly spelled: string;
|
|
41
|
+
readonly glob: true;
|
|
42
|
+
readonly parts: readonly ValuePart[];
|
|
43
|
+
};
|
|
44
|
+
/** One run of a pattern {@link BoundValue}: literal text, or a pattern — the shell expands its characters — `text` as
|
|
45
|
+
* written (what a shell re-reading it matches by), `judged` as a reader judges the names it matches. */
|
|
46
|
+
export type ValuePart = {
|
|
47
|
+
readonly text: string;
|
|
48
|
+
readonly pattern: false;
|
|
49
|
+
} | {
|
|
50
|
+
readonly text: string;
|
|
51
|
+
readonly pattern: true;
|
|
52
|
+
readonly judged: string;
|
|
53
|
+
};
|
|
54
|
+
/** A spelling the deny judge alone reads ({@link Binding.floor}, {@link wordFloorValues}): a value, or a word as written
|
|
55
|
+
* where its names are unknown — nothing re-reads it, so a pattern says no more than that it is one. */
|
|
56
|
+
export interface FloorValue {
|
|
32
57
|
readonly spelled: string;
|
|
33
58
|
readonly glob?: true;
|
|
34
59
|
}
|
|
@@ -58,7 +83,7 @@ export interface Binding {
|
|
|
58
83
|
readonly scope?: "local" | "unread";
|
|
59
84
|
/** Spellings the name may hold that the deny judge still reads where the name is otherwise unknown (an array's
|
|
60
85
|
* elements: what `${A[k]}` expands to is one of them). */
|
|
61
|
-
readonly floor?: readonly
|
|
86
|
+
readonly floor?: readonly FloorValue[];
|
|
62
87
|
}
|
|
63
88
|
/** An attribute a declaration gives a name — or every name (`name` absent: the declaration computed it): a later
|
|
64
89
|
* assignment to it is evaluated as arithmetic (`integer`), stores a case change or an array element (`transformed`), or
|
|
@@ -104,27 +129,29 @@ export declare function projectSegment(segment: LexedSegment): ProjectedSegment;
|
|
|
104
129
|
* token, a head whose body opens in a later segment). */
|
|
105
130
|
export declare function dropDefinitionHeads(tokens: LexedToken[]): readonly number[];
|
|
106
131
|
/**
|
|
107
|
-
* What a word holds through the names it refers to — `$NAME`, `${NAME}`, a leading `~` (the value of `HOME`):
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* pattern, and the value is then a pattern);
|
|
111
|
-
* · `"unknown"` — a name it refers to is not settled here, it carries an expansion no name answers (a modifier, a
|
|
112
|
-
* substitution, a brace list, `~user`), an UNQUOTED expansion's value is not one word or `IFS` may have changed
|
|
113
|
-
* (`quoted` = the whole word is read as one value: an assignment's value, which is never split), or it would list
|
|
114
|
-
* more than {@link WORD_VALUES_MAX} values.
|
|
115
|
-
* `table.stateOf` is read after `overlay` (the names the same segment bound earlier).
|
|
132
|
+
* What a word holds through the names it refers to — `$NAME`, `${NAME}`, a leading `~` (the value of `HOME`): `undefined`
|
|
133
|
+
* where it refers to no name (it reads as written), else {@link valueOf}. `table.stateOf` is read after `overlay` (the
|
|
134
|
+
* names the same segment bound earlier).
|
|
116
135
|
*/
|
|
117
136
|
export declare function wordValues(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined, quoted: boolean, overlay?: ReadonlyMap<string, NameState>): readonly BoundValue[] | "unknown" | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* What a VALUE word holds (an assignment's value, a `for` list word, a word handed to a program) — THE piece walk, the one
|
|
139
|
+
* producer of {@link BoundValue}s: every combination of the values its pieces take — a literal piece its text, a glob
|
|
140
|
+
* piece its pattern (as the program receives it, as written for a shell re-reading it, and as a reader judges it — the
|
|
141
|
+
* lexer's three texts, never re-cut here), `~` and `$NAME` the values the table gives:
|
|
142
|
+
* · its values — a piece the shell globs makes the value a pattern;
|
|
143
|
+
* · `"unknown"` — a name it refers to is not settled here, it carries an expansion no name answers (a modifier, a
|
|
144
|
+
* substitution, a brace list, `~user`, an expansion inside a bracket pattern), an UNQUOTED expansion's value is not one
|
|
145
|
+
* word or `IFS` may have changed (`quoted` = the whole word is read as one value: an assignment's value, which is never
|
|
146
|
+
* split), or it would list more than {@link WORD_VALUES_MAX} values.
|
|
147
|
+
*/
|
|
148
|
+
export declare function valueOf(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined, quoted: boolean, overlay?: ReadonlyMap<string, NameState>): NameState;
|
|
118
149
|
/**
|
|
119
150
|
* The spellings a word the table leaves unknown may still take through the deny floors of the names it refers to
|
|
120
151
|
* ({@link BindingTable.floorOf}) — `${A[k]}`, `${A[@]}`, `$A` reading an array's elements — or through their values where
|
|
121
152
|
* known: for the deny judge alone. Empty where some name it refers to leaves neither.
|
|
122
153
|
*/
|
|
123
|
-
export declare function wordFloorValues(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined): readonly
|
|
124
|
-
/** What a VALUE word holds (an assignment's value, a `for` list word): {@link wordValues}, or — a word referring to no
|
|
125
|
-
* name — its own text (a word the shell globs is a pattern; one carrying any other expansion holds what the text does
|
|
126
|
-
* not say). */
|
|
127
|
-
export declare function valueOf(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined, quoted: boolean, overlay?: ReadonlyMap<string, NameState>): NameState;
|
|
154
|
+
export declare function wordFloorValues(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined): readonly FloorValue[];
|
|
128
155
|
/**
|
|
129
156
|
* What ONE segment does to the names (see the module header). `argv` = the segment's argv projection (structure
|
|
130
157
|
* dropped); `readValue` folds a value word against the table (a reader that answers `unknown` gives the names alone).
|
|
@@ -164,7 +191,7 @@ export interface BindingTable {
|
|
|
164
191
|
/** `HOME` may hold something other than the declared home here. */
|
|
165
192
|
readonly homeTouched: boolean;
|
|
166
193
|
/** Every spelling any binding of this name before here left for the deny judge alone ({@link Binding.floor}). */
|
|
167
|
-
floorOf(name: string): readonly
|
|
194
|
+
floorOf(name: string): readonly FloorValue[];
|
|
168
195
|
/** What a substitution body here starts with. */
|
|
169
196
|
readonly inherited: InheritedNames;
|
|
170
197
|
}
|