mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-12 17:27:50 +08:00
28798ac053
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".
113 lines
2.4 KiB
Go
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())
|
|
}
|
|
}
|
|
}
|
|
}
|