mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-14 19:27:58 +08:00
be3431bbbf
Instead of a separate ShouldRun method, use a special error value to signify that a Program should not be run.
139 lines
3.1 KiB
Go
139 lines
3.1 KiB
Go
// Package shell is the entry point for the terminal interface of Elvish.
|
|
package shell
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"time"
|
|
|
|
"src.elv.sh/pkg/cli/term"
|
|
"src.elv.sh/pkg/daemon/daemondefs"
|
|
"src.elv.sh/pkg/env"
|
|
"src.elv.sh/pkg/eval"
|
|
"src.elv.sh/pkg/logutil"
|
|
"src.elv.sh/pkg/mods"
|
|
"src.elv.sh/pkg/mods/unix"
|
|
"src.elv.sh/pkg/parse"
|
|
"src.elv.sh/pkg/prog"
|
|
"src.elv.sh/pkg/sys"
|
|
)
|
|
|
|
var logger = logutil.GetLogger("[shell] ")
|
|
|
|
// Program is the shell subprogram.
|
|
type Program struct {
|
|
ActivateDaemon daemondefs.ActivateFunc
|
|
}
|
|
|
|
func (p Program) Run(fds [3]*os.File, f *prog.Flags, args []string) error {
|
|
cleanup1 := IncSHLVL()
|
|
defer cleanup1()
|
|
cleanup2 := initTTYAndSignal(fds[2])
|
|
defer cleanup2()
|
|
|
|
ev := MakeEvaler(fds[2])
|
|
|
|
if len(args) > 0 {
|
|
exit := script(
|
|
ev, fds, args, &scriptCfg{
|
|
Cmd: f.CodeInArg, CompileOnly: f.CompileOnly, JSON: f.JSON})
|
|
return prog.Exit(exit)
|
|
}
|
|
|
|
var spawnCfg *daemondefs.SpawnConfig
|
|
if p.ActivateDaemon != nil {
|
|
var err error
|
|
spawnCfg, err = daemonPaths(f)
|
|
if err != nil {
|
|
fmt.Fprintln(fds[2], "Warning:", err)
|
|
fmt.Fprintln(fds[2], "Storage daemon may not function.")
|
|
}
|
|
}
|
|
|
|
rc := ""
|
|
switch {
|
|
case f.NoRc:
|
|
// Leave rc empty
|
|
case f.RC != "":
|
|
// Use explicit -rc flag value
|
|
rc = f.RC
|
|
default:
|
|
// Use default path to rc.elv
|
|
var err error
|
|
rc, err = rcPath()
|
|
if err != nil {
|
|
fmt.Fprintln(fds[2], "Warning:", err)
|
|
}
|
|
}
|
|
|
|
interact(ev, fds, &interactCfg{
|
|
RC: rc,
|
|
ActivateDaemon: p.ActivateDaemon, SpawnConfig: spawnCfg})
|
|
return nil
|
|
}
|
|
|
|
// MakeEvaler creates an Evaler, sets the module search directories and installs
|
|
// all the standard builtin modules. It writes a warning message to the supplied
|
|
// Writer if it could not initialize module search directories.
|
|
func MakeEvaler(stderr io.Writer) *eval.Evaler {
|
|
ev := eval.NewEvaler()
|
|
libs, libInstall, err := libPaths()
|
|
if err != nil {
|
|
fmt.Fprintln(stderr, "Warning:", err)
|
|
}
|
|
ev.SetLibDirs(libs)
|
|
ev.SetLibInstallDir(libInstall)
|
|
mods.AddTo(ev)
|
|
if unix.ExposeUnixNs {
|
|
ev.AddModule("unix", unix.Ns)
|
|
}
|
|
return ev
|
|
}
|
|
|
|
// IncSHLVL increments the SHLVL environment variable. It returns a function to
|
|
// restore the original value of SHLVL.
|
|
func IncSHLVL() func() {
|
|
oldValue, hadValue := os.LookupEnv(env.SHLVL)
|
|
i, err := strconv.Atoi(oldValue)
|
|
if err != nil {
|
|
i = 0
|
|
}
|
|
os.Setenv(env.SHLVL, strconv.Itoa(i+1))
|
|
|
|
if hadValue {
|
|
return func() { os.Setenv(env.SHLVL, oldValue) }
|
|
} else {
|
|
return func() { os.Unsetenv(env.SHLVL) }
|
|
}
|
|
}
|
|
|
|
func initTTYAndSignal(stderr io.Writer) func() {
|
|
restoreTTY := term.SetupGlobal()
|
|
|
|
sigCh := sys.NotifySignals()
|
|
go func() {
|
|
for sig := range sigCh {
|
|
logger.Println("signal", sig)
|
|
handleSignal(sig, stderr)
|
|
}
|
|
}()
|
|
|
|
return func() {
|
|
signal.Stop(sigCh)
|
|
restoreTTY()
|
|
}
|
|
}
|
|
|
|
func evalInTTY(ev *eval.Evaler, fds [3]*os.File, src parse.Source) (float64, error) {
|
|
start := time.Now()
|
|
ports, cleanup := eval.PortsFromFiles(fds, ev.ValuePrefix())
|
|
defer cleanup()
|
|
err := ev.Eval(src, eval.EvalCfg{
|
|
Ports: ports, Interrupt: eval.ListenInterrupts, PutInFg: true})
|
|
end := time.Now()
|
|
return end.Sub(start).Seconds(), err
|
|
}
|