1
0
mirror of https://github.com/go-task/task.git synced 2024-12-16 10:59:23 +02:00
task/vendor/mvdan.cc/sh/interp/builtin.go

263 lines
5.0 KiB
Go
Raw Normal View History

2017-04-24 14:47:10 +02:00
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package interp
import (
"os"
2017-05-01 00:50:22 +02:00
"os/exec"
2017-04-24 14:47:10 +02:00
"path/filepath"
"strconv"
2017-05-17 19:49:27 +02:00
"strings"
2017-04-24 14:47:10 +02:00
"mvdan.cc/sh/syntax"
2017-04-24 14:47:10 +02:00
)
2017-05-01 00:50:22 +02:00
func isBuiltin(name string) bool {
switch name {
case "true", ":", "false", "exit", "set", "shift", "unset",
"echo", "printf", "break", "continue", "pwd", "cd",
2017-06-24 21:00:10 +02:00
"wait", "builtin", "trap", "type", "source", ".", "command",
2017-05-01 00:50:22 +02:00
"pushd", "popd", "umask", "alias", "unalias", "fg", "bg",
2017-06-04 21:06:04 +02:00
"getopts", "eval", "test", "[":
2017-05-01 00:50:22 +02:00
return true
}
return false
}
2017-05-17 19:49:27 +02:00
func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int {
2017-04-24 14:47:10 +02:00
switch name {
case "true", ":":
case "false":
2017-05-17 19:49:27 +02:00
return 1
2017-04-24 14:47:10 +02:00
case "exit":
switch len(args) {
case 0:
case 1:
if n, err := strconv.Atoi(args[0]); err != nil {
r.runErr(pos, "invalid exit code: %q", args[0])
} else {
2017-05-17 19:49:27 +02:00
r.exit = n
2017-04-24 14:47:10 +02:00
}
default:
r.runErr(pos, "exit cannot take multiple arguments")
}
2017-05-17 19:49:27 +02:00
r.lastExit()
return r.exit
2017-04-24 14:47:10 +02:00
case "set":
2017-06-24 21:00:10 +02:00
rest, err := r.FromArgs(args...)
if err != nil {
r.errf("set: %v", err)
return 2
}
r.Params = rest
2017-04-24 14:47:10 +02:00
case "shift":
n := 1
switch len(args) {
case 0:
case 1:
if n2, err := strconv.Atoi(args[0]); err == nil {
n = n2
break
}
fallthrough
default:
r.errf("usage: shift [n]\n")
2017-05-17 19:49:27 +02:00
return 2
2017-04-24 14:47:10 +02:00
}
2017-06-24 21:00:10 +02:00
if n >= len(r.Params) {
r.Params = nil
} else {
r.Params = r.Params[n:]
2017-04-24 14:47:10 +02:00
}
case "unset":
for _, arg := range args {
r.delVar(arg)
}
case "echo":
2017-07-31 00:11:34 +02:00
newline, expand := true, false
2017-06-24 21:00:10 +02:00
echoOpts:
2017-04-24 14:47:10 +02:00
for len(args) > 0 {
switch args[0] {
case "-n":
newline = false
2017-07-31 00:11:34 +02:00
case "-e":
expand = true
case "-E": // default
2017-04-24 14:47:10 +02:00
default:
2017-06-24 21:00:10 +02:00
break echoOpts
2017-04-24 14:47:10 +02:00
}
args = args[1:]
}
for i, arg := range args {
if i > 0 {
r.outf(" ")
}
2017-07-31 00:11:34 +02:00
if expand {
arg = r.expand(arg, true)
}
2017-04-24 14:47:10 +02:00
r.outf("%s", arg)
}
if newline {
r.outf("\n")
}
case "printf":
if len(args) == 0 {
r.errf("usage: printf format [arguments]\n")
2017-05-17 19:49:27 +02:00
return 2
2017-04-24 14:47:10 +02:00
}
2017-07-31 00:11:34 +02:00
r.outf("%s", r.expand(args[0], false, args[1:]...))
2017-04-24 14:47:10 +02:00
case "break":
if !r.inLoop {
r.errf("break is only useful in a loop")
break
}
switch len(args) {
case 0:
r.breakEnclosing = 1
case 1:
if n, err := strconv.Atoi(args[0]); err == nil {
r.breakEnclosing = n
break
}
fallthrough
default:
r.errf("usage: break [n]\n")
2017-05-17 19:49:27 +02:00
return 2
2017-04-24 14:47:10 +02:00
}
case "continue":
if !r.inLoop {
r.errf("continue is only useful in a loop")
break
}
switch len(args) {
case 0:
r.contnEnclosing = 1
case 1:
if n, err := strconv.Atoi(args[0]); err == nil {
r.contnEnclosing = n
break
}
fallthrough
default:
r.errf("usage: continue [n]\n")
2017-05-17 19:49:27 +02:00
return 2
2017-04-24 14:47:10 +02:00
}
case "pwd":
r.outf("%s\n", r.getVar("PWD"))
case "cd":
var dir string
2017-06-16 19:59:12 +02:00
switch len(args) {
case 0:
2017-04-24 14:47:10 +02:00
dir = r.getVar("HOME")
2017-06-16 19:59:12 +02:00
case 1:
2017-04-24 14:47:10 +02:00
dir = args[0]
2017-06-16 19:59:12 +02:00
default:
r.errf("usage: cd [dir]\n")
return 2
2017-04-24 14:47:10 +02:00
}
2017-07-06 01:46:05 +02:00
dir = r.relPath(dir)
2017-07-31 00:11:34 +02:00
info, err := os.Stat(dir)
if err != nil || !info.IsDir() {
2017-05-17 19:49:27 +02:00
return 1
2017-04-24 14:47:10 +02:00
}
r.Dir = dir
case "wait":
if len(args) > 0 {
2017-05-17 19:49:27 +02:00
r.runErr(pos, "wait with args not handled yet")
2017-04-24 14:47:10 +02:00
break
}
r.bgShells.Wait()
case "builtin":
if len(args) < 1 {
break
}
2017-05-01 00:50:22 +02:00
if !isBuiltin(args[0]) {
2017-05-17 19:49:27 +02:00
return 1
2017-05-01 00:50:22 +02:00
}
2017-05-17 19:49:27 +02:00
return r.builtinCode(pos, args[0], args[1:])
2017-05-01 00:50:22 +02:00
case "type":
2017-05-17 19:49:27 +02:00
anyNotFound := false
2017-05-01 00:50:22 +02:00
for _, arg := range args {
2017-05-17 19:49:27 +02:00
if _, ok := r.funcs[arg]; ok {
r.outf("%s is a function\n", arg)
continue
}
2017-05-01 00:50:22 +02:00
if isBuiltin(arg) {
r.outf("%s is a shell builtin\n", arg)
continue
}
if path, err := exec.LookPath(arg); err == nil {
r.outf("%s is %s\n", arg, path)
continue
}
r.errf("type: %s: not found\n", arg)
2017-05-17 19:49:27 +02:00
anyNotFound = true
}
if anyNotFound {
return 1
}
case "eval":
src := strings.Join(args, " ")
p := syntax.NewParser()
file, err := p.Parse(strings.NewReader(src), "")
if err != nil {
r.errf("eval: %v\n", err)
return 1
2017-04-24 14:47:10 +02:00
}
2017-05-17 19:49:27 +02:00
r2 := *r
r2.Reset()
r2.Run(file)
2017-05-17 19:49:27 +02:00
return r2.exit
2017-06-24 21:00:10 +02:00
case "source", ".":
if len(args) < 1 {
r.runErr(pos, "source: need filename")
}
2017-07-06 01:46:05 +02:00
f, err := os.Open(r.relPath(args[0]))
2017-06-24 21:00:10 +02:00
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.Reset()
r2.Run(file)
2017-06-24 21:00:10 +02:00
return r2.exit
2017-06-04 21:06:04 +02:00
case "[":
if len(args) == 0 || args[len(args)-1] != "]" {
r.runErr(pos, "[: missing matching ]")
break
}
args = args[:len(args)-1]
fallthrough
case "test":
p := testParser{
rem: args,
err: func(format string, a ...interface{}) {
r.runErr(pos, format, a...)
},
}
p.next()
expr := p.classicTest("[", false)
return oneIf(r.bashTest(expr) == "")
2017-06-24 21:00:10 +02:00
case "trap", "command", "pushd", "popd",
2017-04-24 14:47:10 +02:00
"umask", "alias", "unalias", "fg", "bg", "getopts":
2017-05-17 19:49:27 +02:00
r.runErr(pos, "unhandled builtin: %s", name)
2017-04-24 14:47:10 +02:00
}
2017-05-17 19:49:27 +02:00
return 0
2017-04-24 14:47:10 +02:00
}
2017-07-06 01:46:05 +02:00
func (r *Runner) relPath(path string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(r.Dir, path)
}