package eval

import (
	
	
	
	

	
	
	
	
)

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
}

// NewExternalCmd returns a callable that executes the named external command.
//
// An external command converts all arguments to strings, and does not accept
// any option.
func ( string) Callable {
	return externalCmd{}
}

func ( externalCmd) () string {
	return "fn"
}

func ( externalCmd) ( interface{}) bool {
	return  == 
}

func ( externalCmd) () uint32 {
	return hash.String(.Name)
}

func ( externalCmd) (int) string {
	return "<external " + parse.Quote(.Name) + ">"
}

// Call calls an external command.
func ( externalCmd) ( *Frame,  []interface{},  map[string]interface{}) error {
	if len() > 0 {
		return ErrExternalCmdOpts
	}
	if fsutil.DontSearch(.Name) {
		,  := os.Stat(.Name)
		if  == nil && .IsDir() {
			// implicit cd
			if len() > 0 {
				return ErrImplicitCdNoArg
			}
			return .Evaler.Chdir(.Name)
		}
	}

	 := make([]*os.File, len(.ports))
	for ,  := range .ports {
		if  != nil {
			[] = .File
		}
	}

	 := make([]string, len()+1)
	for ,  := range  {
		// TODO: Maybe we should enforce string arguments instead of coercing
		// all args to strings.
		[+1] = vals.ToString()
	}

	,  := exec.LookPath(.Name)
	if  != nil {
		return 
	}

	[0] = 

	 := makeSysProcAttr(.background)
	,  := os.StartProcess(, , &os.ProcAttr{Files: , Sys: })
	if  != nil {
		return 
	}

	,  := .Wait()
	if  != nil {
		// This should be a can't happen situation. Nonetheless, treat it as a
		// soft error rather than panicking since the Go documentation is not
		// explicit that this can only happen if we make a mistake. Such as
		// calling `Wait` twice on a particular process object.
		return 
	}
	return NewExternalCmdExit(.Name, .Sys().(syscall.WaitStatus), .Pid)
}