2016-02-19 05:52:05 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"syscall"
|
2016-02-21 21:32:13 +08:00
|
|
|
|
2016-04-05 12:20:18 +08:00
|
|
|
"github.com/elves/elvish/parse"
|
2016-02-21 21:32:13 +08:00
|
|
|
"github.com/elves/elvish/util"
|
2016-02-19 05:52:05 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// FdNil is a special impossible fd value used for "close fd" in
|
|
|
|
// syscall.ProcAttr.Files.
|
|
|
|
const fdNil uintptr = ^uintptr(0)
|
|
|
|
|
2016-09-15 05:34:10 +08:00
|
|
|
var (
|
|
|
|
ErrExternalCmdOpts = errors.New("external commands don't accept elvish options")
|
|
|
|
ErrCdNoArg = errors.New("implicit cd accepts no arguments")
|
|
|
|
)
|
2016-02-19 05:52:05 +08:00
|
|
|
|
|
|
|
// ExternalCmd is an external command.
|
|
|
|
type ExternalCmd struct {
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ExternalCmd) Kind() string {
|
|
|
|
return "fn"
|
|
|
|
}
|
|
|
|
|
2017-08-31 01:47:50 +08:00
|
|
|
func (e ExternalCmd) Equal(a interface{}) bool {
|
2017-07-14 08:15:14 +08:00
|
|
|
return e == a
|
|
|
|
}
|
|
|
|
|
2016-02-20 03:11:31 +08:00
|
|
|
func (e ExternalCmd) Repr(int) string {
|
2016-04-05 12:20:18 +08:00
|
|
|
return "<external " + parse.Quote(e.Name) + ">"
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call calls an external command.
|
2016-09-15 05:34:10 +08:00
|
|
|
func (e ExternalCmd) Call(ec *EvalCtx, argVals []Value, opts map[string]Value) {
|
|
|
|
if len(opts) > 0 {
|
|
|
|
throw(ErrExternalCmdOpts)
|
|
|
|
}
|
2016-02-21 21:32:13 +08:00
|
|
|
if util.DontSearch(e.Name) {
|
2016-02-19 05:52:05 +08:00
|
|
|
stat, err := os.Stat(e.Name)
|
|
|
|
if err == nil && stat.IsDir() {
|
|
|
|
// implicit cd
|
|
|
|
if len(argVals) > 0 {
|
|
|
|
throw(ErrCdNoArg)
|
|
|
|
}
|
|
|
|
cdInner(e.Name, ec)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
files := make([]uintptr, len(ec.ports))
|
|
|
|
for i, port := range ec.ports {
|
|
|
|
if port == nil || port.File == nil {
|
|
|
|
files[i] = fdNil
|
|
|
|
} else {
|
|
|
|
files[i] = port.File.Fd()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
args := make([]string, len(argVals)+1)
|
|
|
|
for i, a := range argVals {
|
|
|
|
// NOTE Maybe we should enfore string arguments instead of coercing all
|
|
|
|
// args into string
|
|
|
|
args[i+1] = ToString(a)
|
|
|
|
}
|
|
|
|
|
2017-01-13 22:50:14 +08:00
|
|
|
sys := syscall.SysProcAttr{Setpgid: ec.background}
|
2016-02-19 05:52:05 +08:00
|
|
|
attr := syscall.ProcAttr{Env: os.Environ(), Files: files[:], Sys: &sys}
|
|
|
|
|
|
|
|
path, err := ec.Search(e.Name)
|
|
|
|
if err != nil {
|
2016-02-22 03:17:06 +08:00
|
|
|
throw(err)
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
args[0] = path
|
|
|
|
pid, err := syscall.ForkExec(path, args, &attr)
|
|
|
|
if err != nil {
|
|
|
|
throw(errors.New("forkExec: " + err.Error()))
|
|
|
|
}
|
|
|
|
|
|
|
|
var ws syscall.WaitStatus
|
2016-02-22 06:01:30 +08:00
|
|
|
_, err = syscall.Wait4(pid, &ws, syscall.WUNTRACED, nil)
|
2016-03-17 19:32:48 +08:00
|
|
|
|
2016-02-19 05:52:05 +08:00
|
|
|
if err != nil {
|
|
|
|
throw(fmt.Errorf("wait: %s", err.Error()))
|
|
|
|
} else {
|
2016-07-25 23:50:02 +08:00
|
|
|
maybeThrow(NewExternalCmdExit(e.Name, ws, pid))
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
}
|