interp

package
v0.0.0-...-d1567f4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package interp builds and configures the mvdan.cc/sh/v3/interp runner with the gobash customization hooks. It is the bridge between the public gobash.Bash surface and mvdan/sh's tree-walking interpreter:

  • VFS-backed Open/Stat/ReadDir handlers route every file operation through the FileSystem on the caller's *Bash instead of the host disk.
  • An ExecHandlers middleware dispatches every non-mvdan-builtin command through the gobash command Registry. The middleware NEVER falls through to mvdan/sh's DefaultExecHandler; unregistered commands trigger a `command not found` stderr write plus ExitStatus(127), closing the os/exec gap that existed in Phases 5–7. The Phase 8 acceptance test `TestPhase8UnknownCommandIsNotFound` locks this in.
  • Env, Cwd, and the three stdio streams are passed in via Config; the caller is responsible for wrapping stdout/stderr in the MaxOutputSize ringbuf writer before BuildRunner sees them.

Import cycle avoidance

Phase 5's kickoff suggested a signature of `BuildRunner(ctx, *Bash, ExecOptions)`. Because gobash imports this package and *gobash.Bash lives in the root package, taking *Bash here would create a cycle. Instead we accept a flat Config struct that the caller (gobash.(*Bash).Exec) assembles from the *Bash + ExecOptions inputs. The semantic intent (wire env / cwd / streams / FS / callHandler into the runner) is identical.

Phase 3 mvdan/sh quirks (still load-bearing)

  • interp.Dir(path) runs an os.Stat(path) on the host disk at runner-init time. We never use it; we set runner.Dir = cwd directly after interp.New returns.
  • interp.HandlerCtx(ctx) panics when ctx has no HandlerContext. mvdan/sh's Runner.stat / Runner.lstat path call our handlers with the bare ctx during early init. HandlerDir below recovers and falls back to empty, which gbfs.Resolve handles correctly for absolute inputs.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildRunner

func BuildRunner(ctx context.Context, cfg Config) (*mvinterp.Runner, error)

BuildRunner constructs a fully-configured *mvinterp.Runner.

ctx is reserved for future use (mvdan/sh's constructor takes no ctx today) and is intentionally part of the public signature so the eventual richer hookpoint — e.g. cancelling pending I/O during runner-init — does not require a breaking change.

func HandlerDir

func HandlerDir(ctx context.Context) string

HandlerDir returns the current Dir from the HandlerContext stored in ctx, or empty string if mvdan/sh has not injected one yet.

mvdan/sh's Runner.stat / Runner.lstat call our StatHandler with the bare ctx (no HandlerContext attached), and interp.HandlerCtx panics in that case. The defer/recover absorbs the panic and falls back to empty — which gbfs.Resolve treats correctly for absolute paths.

Exported so tests in the gobash root package can re-use the exact same guard.

Types

type Config

type Config struct {
	// Env is the initial environment as a slice of KEY=VAL pairs,
	// matching expand.ListEnviron's input format. Nil is treated as
	// an empty environment.
	Env []string

	// Cwd is the script's working directory. Resolved through the VFS
	// (NOT the host disk) — see the package-level Phase 3 quirk note.
	// Empty string leaves the runner's default Dir unchanged.
	Cwd string

	// Stdin / Stdout / Stderr are the three streams handed to
	// interp.StdIO. Nil Stdin is treated as an EOF reader so scripts
	// that try to read still terminate cleanly. Nil Stdout / Stderr
	// are illegal — the caller must supply at least a discard sink
	// (e.g. the MaxOutputSize ringbuf writer wrapping io.Discard).
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer

	// FS is the virtual filesystem backing the Open / Stat / ReadDir
	// handlers. Required: nil triggers an explicit error so a missing
	// wire-up surfaces immediately instead of silently shelling out to
	// the host disk.
	FS gbfs.FileSystem

	// CallHandler is the per-command interception point used today by
	// the limit accounting (MaxLoopIterations / MaxCommandCount /
	// MaxCallDepth). Nil disables call-handler wiring; mvdan/sh's
	// default behavior takes over.
	CallHandler mvinterp.CallHandlerFunc

	// Registry is the command dispatch registry. Nil is treated as an
	// empty registry — every non-mvdan-builtin command will resolve
	// to `command not found` (ExitStatus 127). The runtime supplies a
	// fully-populated *command.Registry; tests in this package omit
	// the field deliberately so the not-found path is exercised in
	// isolation.
	Registry *command.Registry

	// Fetch is the resolved network Doer. When nil, command.Context.Fetch
	// is also nil and network-touching commands must error cleanly.
	// The spec: the runtime decides whether to supply a SecureFetch or
	// honor a host-provided override; the dispatch bridge just passes
	// through whatever the host gave it.
	Fetch network.Doer

	// Sleep is the host-supplied sleep hook surfaced to commands via
	// command.Context.Sleep (Phase 10 Wave A's `sleep` is the first
	// consumer). Nil leaves command.Context.Sleep nil; the builtin
	// falls back to time.Sleep.
	Sleep command.SleepFunc

	// Trace is the host-supplied trace hook surfaced to commands via
	// command.Context.Trace. Wave A does not emit events; the field
	// is plumbed today so later waves can without a follow-up
	// Context surface bump.
	Trace command.TraceFunc

	// Limits is the per-Exec resolved limit set surfaced to commands
	// via command.Context.Limits. Wave A built-ins ignore it; Wave D
	// and the optional runtimes are the first real consumers.
	Limits command.Limits

	// ExportedEnv is the per-dispatch exported-env view surfaced to
	// commands via command.Context.ExportedEnv. Wave A does not
	// consume it; landing zone for env/printenv/export (Wave G).
	ExportedEnv map[string]string

	// Aliases is the per-Bash alias table surfaced to commands via
	// command.Context.Aliases (Phase 10 Wave G `alias` / `unalias`).
	// The runtime always supplies a live table; a nil interface is
	// not safe to call through.
	Aliases command.AliasTable

	// History is the per-Bash command-history ring surfaced to
	// commands via command.Context.History (Phase 10 Wave G
	// `history`).
	History command.HistoryRing

	// Exec is the sub-shell invocation hook surfaced to commands via
	// command.Context.Exec (Phase 10 Wave G `bash` / `sh` /
	// `timeout`). The spec reserves this for Phase 11; Wave G needs
	// it earlier.
	Exec command.SubExecFunc

	// SourceDepth is the depth this dispatch starts at. Used by the
	// Phase 11 source / eval / . / bash / sh / timeout builtins to
	// enforce Limits.MaxSourceDepth across nested invocations.
	SourceDepth int

	// Shopt is the per-Bash shopt table surfaced to commands via
	// command.Context.Shopt (Phase 11). Concrete implementation in
	// internal/runtimestate.
	Shopt command.ShoptTable

	// ReadDirHook, if non-nil, is invoked before every VFS ReadDir.
	// Returning a non-nil error fails the ReadDir without consulting
	// the underlying FS — this is the Phase 6 wire-up point for the
	// MaxGlobOperations limit (every ReadDir contributes one glob op).
	// The spec routes pathname expansion through this hook because
	// mvdan/sh does not expose a glob-specific counter.
	ReadDirHook func(ctx context.Context, path string) error
}

Config carries everything BuildRunner needs to construct a runner. The caller — typically gobash.(*Bash).Exec — assembles this from the *Bash + ExecOptions inputs. All fields except CallHandler are required; the zero value of Config is not usable.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL