github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/core/elvish/eval/external_cmd.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "syscall" 9 10 "github.com/u-root/u-root/cmds/core/elvish/eval/vals" 11 "github.com/u-root/u-root/cmds/core/elvish/hash" 12 "github.com/u-root/u-root/cmds/core/elvish/parse" 13 "github.com/u-root/u-root/cmds/core/elvish/util" 14 ) 15 16 var ( 17 ErrExternalCmdOpts = errors.New("external commands don't accept elvish options") 18 ErrCdNoArg = errors.New("implicit cd accepts no arguments") 19 ) 20 21 // ExternalCmd is an external command. 22 type ExternalCmd struct { 23 Name string 24 } 25 26 func (ExternalCmd) Kind() string { 27 return "fn" 28 } 29 30 func (e ExternalCmd) Equal(a interface{}) bool { 31 return e == a 32 } 33 34 func (e ExternalCmd) Hash() uint32 { 35 return hash.Hash(e.Name) 36 } 37 38 func (e ExternalCmd) Repr(int) string { 39 return "<external " + parse.Quote(e.Name) + ">" 40 } 41 42 // Call calls an external command. 43 func (e ExternalCmd) Call(fm *Frame, argVals []interface{}, opts map[string]interface{}) error { 44 if len(opts) > 0 { 45 return ErrExternalCmdOpts 46 } 47 if util.DontSearch(e.Name) { 48 stat, err := os.Stat(e.Name) 49 if err == nil && stat.IsDir() { 50 // implicit cd 51 if len(argVals) > 0 { 52 return ErrCdNoArg 53 } 54 return fm.Chdir(e.Name) 55 } 56 } 57 58 files := make([]*os.File, len(fm.ports)) 59 for i, port := range fm.ports { 60 files[i] = port.File 61 } 62 63 args := make([]string, len(argVals)+1) 64 for i, a := range argVals { 65 // NOTE Maybe we should enfore string arguments instead of coercing all 66 // args into string 67 args[i+1] = vals.ToString(a) 68 } 69 70 path, err := exec.LookPath(e.Name) 71 if err != nil { 72 return err 73 } 74 75 args[0] = path 76 77 sys := makeSysProcAttr(fm.background) 78 proc, err := os.StartProcess(path, args, &os.ProcAttr{Files: files, Sys: sys}) 79 80 if err != nil { 81 return err 82 } 83 84 state, err := proc.Wait() 85 86 if err != nil { 87 return err 88 } 89 return NewExternalCmdExit(e.Name, state.Sys().(syscall.WaitStatus), proc.Pid) 90 } 91 92 // EachExternal calls f for each name that can resolve to an external 93 // command. 94 // TODO(xiaq): Windows support 95 func EachExternal(f func(string)) { 96 for _, dir := range searchPaths() { 97 // XXX Ignore error 98 infos, _ := ioutil.ReadDir(dir) 99 for _, info := range infos { 100 if !info.IsDir() && (info.Mode()&0111 != 0) { 101 f(info.Name()) 102 } 103 } 104 } 105 }