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
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
|
|
}
|