update deps

This commit is contained in:
Andrey Nering
2017-06-24 16:00:10 -03:00
parent f8a5825083
commit 7e06ba1728
18 changed files with 289 additions and 204 deletions

View File

@@ -17,7 +17,7 @@ func isBuiltin(name string) bool {
switch name {
case "true", ":", "false", "exit", "set", "shift", "unset",
"echo", "printf", "break", "continue", "pwd", "cd",
"wait", "builtin", "trap", "type", "source", "command",
"wait", "builtin", "trap", "type", "source", ".", "command",
"pushd", "popd", "umask", "alias", "unalias", "fg", "bg",
"getopts", "eval", "test", "[":
return true
@@ -45,7 +45,12 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int {
r.lastExit()
return r.exit
case "set":
r.args = args
rest, err := r.FromArgs(args...)
if err != nil {
r.errf("set: %v", err)
return 2
}
r.Params = rest
case "shift":
n := 1
switch len(args) {
@@ -60,17 +65,18 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int {
r.errf("usage: shift [n]\n")
return 2
}
if len(r.args) < n {
n = len(r.args)
if n >= len(r.Params) {
r.Params = nil
} else {
r.Params = r.Params[n:]
}
r.args = r.args[n:]
case "unset":
for _, arg := range args {
r.delVar(arg)
}
case "echo":
newline := true
opts:
echoOpts:
for len(args) > 0 {
switch args[0] {
case "-n":
@@ -80,7 +86,7 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int {
// exactly what is the difference in
// what we write?
default:
break opts
break echoOpts
}
args = args[1:]
}
@@ -206,6 +212,27 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int {
r2.File = file
r2.Run()
return r2.exit
case "source", ".":
if len(args) < 1 {
r.runErr(pos, "source: need filename")
}
f, err := os.Open(args[0])
if err != nil {
r.errf("eval: %v\n", err)
return 1
}
defer f.Close()
p := syntax.NewParser()
file, err := p.Parse(f, args[0])
if err != nil {
r.errf("eval: %v\n", err)
return 1
}
r2 := *r
r2.Params = args[1:]
r2.File = file
r2.Run()
return r2.exit
case "[":
if len(args) == 0 || args[len(args)-1] != "]" {
r.runErr(pos, "[: missing matching ]")
@@ -223,7 +250,7 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int {
p.next()
expr := p.classicTest("[", false)
return oneIf(r.bashTest(expr) == "")
case "trap", "source", "command", "pushd", "popd",
case "trap", "command", "pushd", "popd",
"umask", "alias", "unalias", "fg", "bg", "getopts":
r.runErr(pos, "unhandled builtin: %s", name)
}

View File

@@ -44,6 +44,11 @@ type Runner struct {
// process's current directory.
Dir string
// Params are the current parameters, e.g. from running a shell
// file or calling a function. Accessible via the $@/$* family
// of vars.
Params []string
// Separate maps, note that bash allows a name to be both a var
// and a func simultaneously
vars map[string]varValue
@@ -52,9 +57,6 @@ type Runner struct {
// like vars, but local to a cmd i.e. "foo=bar prog args..."
cmdVars map[string]varValue
// Current arguments, if executing a function
args []string
// >0 to break or continue out of N enclosing loops
breakEnclosing, contnEnclosing int
@@ -71,6 +73,8 @@ type Runner struct {
// Context can be used to cancel the interpreter before it finishes
Context context.Context
stopOnCmdErr bool // set -e
}
// varValue can hold a string, an indexed array ([]string) or an
@@ -184,6 +188,33 @@ func (r *Runner) setFunc(name string, body *syntax.Stmt) {
r.funcs[name] = body
}
// FromArgs populates the shell options and returns the remaining
// arguments. For example, running FromArgs("-e", "--", "foo") will set
// the "-e" option and return []string{"foo"}.
//
// This is similar to what the interpreter's "set" builtin does.
func (r *Runner) FromArgs(args ...string) ([]string, error) {
opts:
for len(args) > 0 {
opt := args[0]
if opt == "" || (opt[0] != '-' && opt[0] != '+') {
break
}
enable := opt[0] == '-'
switch opt[1:] {
case "-":
args = args[1:]
break opts
case "e":
r.stopOnCmdErr = enable
default:
return nil, fmt.Errorf("invalid option: %q", opt)
}
args = args[1:]
}
return args, nil
}
// Run starts the interpreter and returns any error.
func (r *Runner) Run() error {
if r.Context == nil {
@@ -531,6 +562,9 @@ func (r *Runner) cmd(cm syntax.Command) {
default:
r.runErr(cm.Pos(), "unhandled command node: %T", x)
}
if r.exit != 0 && r.stopOnCmdErr {
r.lastExit()
}
}
func (r *Runner) stmts(stmts []*syntax.Stmt) {
@@ -581,7 +615,11 @@ func (r *Runner) redir(rd *syntax.Redirect) (io.Closer, error) {
case syntax.RdrOut, syntax.RdrAll:
mode = os.O_RDWR | os.O_CREATE | os.O_TRUNC
}
f, err := os.OpenFile(arg, mode, 0644)
path := arg
if !filepath.IsAbs(path) {
path = filepath.Join(r.Dir, path)
}
f, err := os.OpenFile(path, mode, 0644)
if err != nil {
// TODO: print to stderr?
return nil, err
@@ -719,10 +757,10 @@ func (r *Runner) wordFields(wps []syntax.WordPart, quoted bool) [][]fieldPart {
func (r *Runner) call(pos syntax.Pos, name string, args []string) {
if body := r.funcs[name]; body != nil {
// stack them to support nested func calls
oldArgs := r.args
r.args = args
oldParams := r.Params
r.Params = args
r.stmt(body)
r.args = oldArgs
r.Params = oldParams
return
}
if isBuiltin(name) {

View File

@@ -17,7 +17,7 @@ func (r *Runner) quotedElems(pe *syntax.ParamExp) []string {
return nil
}
if pe.Param.Value == "@" {
return r.args
return r.Params
}
w, _ := pe.Index.(*syntax.Word)
if w == nil || len(w.Parts) != 1 {
@@ -41,15 +41,15 @@ func (r *Runner) paramExp(pe *syntax.ParamExp) string {
set := false
switch name {
case "#":
val = strconv.Itoa(len(r.args))
val = strconv.Itoa(len(r.Params))
case "*", "@":
val = strings.Join(r.args, " ")
val = strings.Join(r.Params, " ")
case "?":
val = strconv.Itoa(r.exit)
default:
if n, err := strconv.Atoi(name); err == nil {
if i := n - 1; i < len(r.args) {
val, set = r.args[i], true
if i := n - 1; i < len(r.Params) {
val, set = r.Params[i], true
}
} else {
val, set = r.lookupVar(name)