Skip to content

Commit 945f1f3

Browse files
committed
interp: implement the enable, compgen, history and logout builtins
enable -n disables a builtin so the name falls through to the exec handler, which is the point of the builtin in bash too — running the real /bin/echo, say. `builtin` still bypasses that, as it does in bash, and subshells inherit the set of disabled builtins. compgen generates completions from the shell's own aliases, functions, variables, keywords and $PATH — the interpreter is the only thing that knows them, so a line editor above it had no way to ask before. The -A job and -A signal actions yield nothing for now; they fill in when the job control builtins land. history reads the list the embedder supplies with the new interp.History option, since the interpreter never reads input lines itself and so has no history of its own. logout is bash's error for a shell that is not a login shell.
1 parent 9a79a44 commit 945f1f3

6 files changed

Lines changed: 549 additions & 5 deletions

File tree

‎interp/api.go‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,16 @@ type Runner struct {
234234
// TODO: close a file once "exec" replaces it, like other shells do.
235235
keptFiles []io.Closer
236236

237+
// disabledBuiltins holds the names turned off by `enable -n`, which are
238+
// then looked up as external commands like bash does.
239+
disabledBuiltins map[string]bool
240+
241+
// historyList and historyClear back the history builtin. They are nil
242+
// unless the embedder supplied them with [History]; this interpreter does
243+
// not read input lines itself, so only a line editor above it knows them.
244+
historyList func() []string
245+
historyClear func()
246+
237247
// Fake signal callbacks
238248
callbackErr string
239249
callbackExit string
@@ -476,6 +486,20 @@ func Dir(path string) RunnerOption {
476486
}
477487
}
478488

489+
// History supplies the command history to the history builtin. list returns the
490+
// history oldest first, and clear, which may be nil, discards it.
491+
//
492+
// The interpreter never reads input lines itself, so it has no history of its
493+
// own; only the line editor above it has one. Without this option, history
494+
// reports that no history list is available.
495+
func History(list func() []string, clear func()) RunnerOption {
496+
return func(r *Runner) error {
497+
r.historyList = list
498+
r.historyClear = clear
499+
return nil
500+
}
501+
}
502+
479503
// Interactive configures the interpreter to behave like an interactive shell,
480504
// akin to Bash. Currently, this only enables the expansion of aliases,
481505
// but later on it should also change other behavior.
@@ -1023,6 +1047,8 @@ func (r *Runner) Reset() {
10231047
accessHandler: r.accessHandler,
10241048
procSubstHandler: r.procSubstHandler,
10251049
procSubsts: r.procSubsts,
1050+
historyList: r.historyList,
1051+
historyClear: r.historyClear,
10261052

10271053
// These can be set by functions like [Dir] or [Params], but
10281054
// builtins can overwrite them; reset the fields to whatever the
@@ -1301,9 +1327,12 @@ func (r *Runner) subshell(background bool) *Runner {
13011327
evalDepth: r.evalDepth,
13021328
stmtDepth: r.stmtDepth,
13031329
procSubstUses: slices.Clip(r.procSubstUses),
1330+
historyList: r.historyList,
1331+
historyClear: r.historyClear,
13041332

13051333
origStdout: r.origStdout, // used for process substitutions
13061334
}
1335+
r2.disabledBuiltins = maps.Clone(r.disabledBuiltins)
13071336
r2.writeEnv = newOverlayEnviron(r.writeEnv, background)
13081337
// Funcs are copied, since they might be modified.
13091338
r2.Funcs = maps.Clone(r.Funcs)

0 commit comments

Comments
 (0)