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  }