You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
2.8 KiB
Go

package shell
import (
"fmt"
"io"
"log"
"strconv"
)
type State interface {
Variable(string) string
Command(string) CommandExecutor
Env(string) string
Pushd(string) error
Popd() error
Cwd() string
SetVariable(string, string)
SetCommand(string, CommandExecutor)
SetEnv(string, string)
Eval(Evalable, io.Reader, io.Writer, io.Writer) uint8
Expand(Expandable, io.Reader, io.Writer) []string
ExecuteCommand(string, []string, io.Reader, io.Writer, io.Writer) uint8
Clone() State
}
func NewShellState() *ShellState {
return &ShellState{
variables: map[string]string{},
commands: map[string]CommandExecutor{},
env: map[string]string{},
dirstack: []string{},
}
}
type ShellState struct {
variables map[string]string
commands map[string]CommandExecutor
env map[string]string
dirstack []string
}
func (s *ShellState) Variable(n string) string {
if v, ok := s.variables[n]; ok {
return v
}
return ""
}
func (s *ShellState) Command(n string) CommandExecutor {
if v, ok := s.commands[n]; ok {
return v
}
return nil
}
func (s *ShellState) Env(n string) string {
if v, ok := s.env[n]; ok {
return v
}
return ""
}
func (s *ShellState) SetVariable(n string, v string) {
s.variables[n] = v
}
func (s *ShellState) SetCommand(n string, e CommandExecutor) {
s.commands[n] = e
}
func (s *ShellState) SetEnv(n string, v string) {
s.env[n] = v
}
func (s *ShellState) Pushd(dir string) error {
s.dirstack = append([]string{dir}, s.dirstack...)
return nil
}
func (s *ShellState) Popd() error {
if len(s.dirstack) == 0 {
return nil
}
s.dirstack = s.dirstack[1:]
return nil
}
func (s *ShellState) Cwd() string {
if len(s.dirstack) == 0 {
return ""
}
return s.dirstack[0]
}
func (s *ShellState) Clone() State {
state := NewShellState()
for k, v := range s.commands {
state.SetCommand(k, v)
}
for k, v := range s.variables {
state.SetVariable(k, v)
}
for k, v := range s.env {
state.SetEnv(k, v)
}
state.dirstack = make([]string, len(s.dirstack))
copy(s.dirstack, state.dirstack)
return state
}
func (s *ShellState) Eval(e Evalable, stdin io.Reader, stdout io.Writer, stderr io.Writer) uint8 {
ret := e.Evaluate(s, stdin, stdout, stderr)
s.SetVariable("?", strconv.Itoa(int(ret)))
return ret
}
func (s *ShellState) Expand(e Expandable, stdin io.Reader, stderr io.Writer) []string {
out, ret := e.Expand(s, stdin, stderr)
s.SetVariable("?", strconv.Itoa(int(ret)))
return out
}
func (s *ShellState) ExecuteCommand(
name string,
args []string,
stdin io.Reader,
stdout,
stderr io.Writer,
) uint8 {
if comm, ok := s.commands[name]; ok {
return comm.Execute(args, stdin, stdout, stderr)
}
logstr := fmt.Sprintf("Shell: command %s not found", name)
log.Println(logstr)
stderr.Write([]byte(logstr))
return 127
}