elvish/pkg/eval/external_cmd.go
Kurtis Rader 28798ac053 Convert "XXX" to "TODO" comments
I stumbled across a comment that began with "XXX". It was clearly meant as
a "TODO" comment. This changes all such occurrences. However, a few "XXX"
comments are ambiguous and a better prefix might be "WARNING". The "TODO"
prefix at least ensures someone, eventually, looks into the situation
and either rewords the comment or fixes the problem. This change means
everyone can assume searching for "// TODO" will find all such comments
rather than requiring they also know to search for "// XXX".
2020-08-16 16:29:34 +01:00

113 lines
2.4 KiB
Go

package eval
import (
"errors"
"io/ioutil"
"os"
"os/exec"
"syscall"
"github.com/elves/elvish/pkg/eval/vals"
"github.com/elves/elvish/pkg/parse"
"github.com/elves/elvish/pkg/util"
"github.com/xiaq/persistent/hash"
)
var (
// ErrExternalCmdOpts is thrown when an external command is passed Elvish
// options.
//
// TODO: Catch this kind of errors at compilation time.
ErrExternalCmdOpts = errors.New("external commands don't accept elvish options")
// ErrImplicitCdNoArg is thrown when an implicit cd form is passed arguments.
ErrImplicitCdNoArg = errors.New("implicit cd accepts no arguments")
)
// ExternalCmd is an external command.
type ExternalCmd struct {
Name string
}
func (ExternalCmd) Kind() string {
return "fn"
}
func (e ExternalCmd) Equal(a interface{}) bool {
return e == a
}
func (e ExternalCmd) Hash() uint32 {
return hash.String(e.Name)
}
func (e ExternalCmd) Repr(int) string {
return "<external " + parse.Quote(e.Name) + ">"
}
// Call calls an external command.
func (e ExternalCmd) Call(fm *Frame, argVals []interface{}, opts map[string]interface{}) error {
if len(opts) > 0 {
return ErrExternalCmdOpts
}
if util.DontSearch(e.Name) {
stat, err := os.Stat(e.Name)
if err == nil && stat.IsDir() {
// implicit cd
if len(argVals) > 0 {
return ErrImplicitCdNoArg
}
return fm.Chdir(e.Name)
}
}
files := make([]*os.File, len(fm.ports))
for i, port := range fm.ports {
if port != nil {
files[i] = port.File
}
}
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] = vals.ToString(a)
}
path, err := exec.LookPath(e.Name)
if err != nil {
return err
}
args[0] = path
sys := makeSysProcAttr(fm.background)
proc, err := os.StartProcess(path, args, &os.ProcAttr{Files: files, Sys: sys})
if err != nil {
return err
}
state, err := proc.Wait()
if err != nil {
return err
}
return NewExternalCmdExit(e.Name, state.Sys().(syscall.WaitStatus), proc.Pid)
}
// EachExternal calls f for each name that can resolve to an external
// command.
// TODO(xiaq): Windows support
func EachExternal(f func(string)) {
for _, dir := range searchPaths() {
// TODO(xiaq): Ignore error.
infos, _ := ioutil.ReadDir(dir)
for _, info := range infos {
if !info.IsDir() && (info.Mode()&0111 != 0) {
f(info.Name())
}
}
}
}