diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-12-07 01:11:29 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-12-07 01:11:29 +0000 |
commit | 9c63abc9a1d127f95162756467284cf76b47aff8 (patch) | |
tree | 84f27a6ab44d932e4b0455f18390b070b4de626e /libgo/go/exec | |
parent | 374280238f934fa851273e2ee16ba53be890c6b8 (diff) | |
download | gcc-9c63abc9a1d127f95162756467284cf76b47aff8.zip gcc-9c63abc9a1d127f95162756467284cf76b47aff8.tar.gz gcc-9c63abc9a1d127f95162756467284cf76b47aff8.tar.bz2 |
libgo: Update to weekly 2011-11-09.
From-SVN: r182073
Diffstat (limited to 'libgo/go/exec')
-rw-r--r-- | libgo/go/exec/exec.go | 393 | ||||
-rw-r--r-- | libgo/go/exec/exec_test.go | 257 | ||||
-rw-r--r-- | libgo/go/exec/lp_plan9.go | 52 | ||||
-rw-r--r-- | libgo/go/exec/lp_test.go | 33 | ||||
-rw-r--r-- | libgo/go/exec/lp_unix.go | 55 | ||||
-rw-r--r-- | libgo/go/exec/lp_windows.go | 78 |
6 files changed, 0 insertions, 868 deletions
diff --git a/libgo/go/exec/exec.go b/libgo/go/exec/exec.go deleted file mode 100644 index ebdfd54..0000000 --- a/libgo/go/exec/exec.go +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package exec runs external commands. It wraps os.StartProcess to make it -// easier to remap stdin and stdout, connect I/O with pipes, and do other -// adjustments. -package exec - -import ( - "bytes" - "errors" - "io" - "os" - "strconv" - "syscall" -) - -// Error records the name of a binary that failed to be be executed -// and the reason it failed. -type Error struct { - Name string - Err error -} - -func (e *Error) Error() string { - return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error() -} - -// Cmd represents an external command being prepared or run. -type Cmd struct { - // Path is the path of the command to run. - // - // This is the only field that must be set to a non-zero - // value. - Path string - - // Args holds command line arguments, including the command as Args[0]. - // If the Args field is empty or nil, Run uses {Path}. - // - // In typical use, both Path and Args are set by calling Command. - Args []string - - // Env specifies the environment of the process. - // If Env is nil, Run uses the current process's environment. - Env []string - - // Dir specifies the working directory of the command. - // If Dir is the empty string, Run runs the command in the - // calling process's current directory. - Dir string - - // Stdin specifies the process's standard input. - // If Stdin is nil, the process reads from DevNull. - Stdin io.Reader - - // Stdout and Stderr specify the process's standard output and error. - // - // If either is nil, Run connects the - // corresponding file descriptor to /dev/null. - // - // If Stdout and Stderr are are the same writer, at most one - // goroutine at a time will call Write. - Stdout io.Writer - Stderr io.Writer - - // ExtraFiles specifies additional open files to be inherited by the - // new process. It does not include standard input, standard output, or - // standard error. If non-nil, entry i becomes file descriptor 3+i. - ExtraFiles []*os.File - - // SysProcAttr holds optional, operating system-specific attributes. - // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. - SysProcAttr *syscall.SysProcAttr - - // Process is the underlying process, once started. - Process *os.Process - - err error // last error (from LookPath, stdin, stdout, stderr) - finished bool // when Wait was called - childFiles []*os.File - closeAfterStart []io.Closer - closeAfterWait []io.Closer - goroutine []func() error - errch chan error // one send per goroutine -} - -// Command returns the Cmd struct to execute the named program with -// the given arguments. -// -// It sets Path and Args in the returned structure and zeroes the -// other fields. -// -// If name contains no path separators, Command uses LookPath to -// resolve the path to a complete name if possible. Otherwise it uses -// name directly. -// -// The returned Cmd's Args field is constructed from the command name -// followed by the elements of arg, so arg should not include the -// command name itself. For example, Command("echo", "hello") -func Command(name string, arg ...string) *Cmd { - aname, err := LookPath(name) - if err != nil { - aname = name - } - return &Cmd{ - Path: aname, - Args: append([]string{name}, arg...), - err: err, - } -} - -// interfaceEqual protects against panics from doing equality tests on -// two interfaces with non-comparable underlying types -func interfaceEqual(a, b interface{}) bool { - defer func() { - recover() - }() - return a == b -} - -func (c *Cmd) envv() []string { - if c.Env != nil { - return c.Env - } - return os.Environ() -} - -func (c *Cmd) argv() []string { - if len(c.Args) > 0 { - return c.Args - } - return []string{c.Path} -} - -func (c *Cmd) stdin() (f *os.File, err error) { - if c.Stdin == nil { - f, err = os.Open(os.DevNull) - c.closeAfterStart = append(c.closeAfterStart, f) - return - } - - if f, ok := c.Stdin.(*os.File); ok { - return f, nil - } - - pr, pw, err := os.Pipe() - if err != nil { - return - } - - c.closeAfterStart = append(c.closeAfterStart, pr) - c.closeAfterWait = append(c.closeAfterWait, pw) - c.goroutine = append(c.goroutine, func() error { - _, err := io.Copy(pw, c.Stdin) - if err1 := pw.Close(); err == nil { - err = err1 - } - return err - }) - return pr, nil -} - -func (c *Cmd) stdout() (f *os.File, err error) { - return c.writerDescriptor(c.Stdout) -} - -func (c *Cmd) stderr() (f *os.File, err error) { - if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { - return c.childFiles[1], nil - } - return c.writerDescriptor(c.Stderr) -} - -func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { - if w == nil { - f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) - c.closeAfterStart = append(c.closeAfterStart, f) - return - } - - if f, ok := w.(*os.File); ok { - return f, nil - } - - pr, pw, err := os.Pipe() - if err != nil { - return - } - - c.closeAfterStart = append(c.closeAfterStart, pw) - c.closeAfterWait = append(c.closeAfterWait, pr) - c.goroutine = append(c.goroutine, func() error { - _, err := io.Copy(w, pr) - return err - }) - return pw, nil -} - -// Run starts the specified command and waits for it to complete. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (c *Cmd) Run() error { - if err := c.Start(); err != nil { - return err - } - return c.Wait() -} - -// Start starts the specified command but does not wait for it to complete. -func (c *Cmd) Start() error { - if c.err != nil { - return c.err - } - if c.Process != nil { - return errors.New("exec: already started") - } - - type F func(*Cmd) (*os.File, error) - for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { - fd, err := setupFd(c) - if err != nil { - return err - } - c.childFiles = append(c.childFiles, fd) - } - c.childFiles = append(c.childFiles, c.ExtraFiles...) - - var err error - c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ - Dir: c.Dir, - Files: c.childFiles, - Env: c.envv(), - Sys: c.SysProcAttr, - }) - if err != nil { - return err - } - - for _, fd := range c.closeAfterStart { - fd.Close() - } - - c.errch = make(chan error, len(c.goroutine)) - for _, fn := range c.goroutine { - go func(fn func() error) { - c.errch <- fn() - }(fn) - } - - return nil -} - -// An ExitError reports an unsuccessful exit by a command. -type ExitError struct { - *os.Waitmsg -} - -func (e *ExitError) Error() string { - return e.Waitmsg.String() -} - -// Wait waits for the command to exit. -// It must have been started by Start. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (c *Cmd) Wait() error { - if c.Process == nil { - return errors.New("exec: not started") - } - if c.finished { - return errors.New("exec: Wait was already called") - } - c.finished = true - msg, err := c.Process.Wait(0) - - var copyError error - for _ = range c.goroutine { - if err := <-c.errch; err != nil && copyError == nil { - copyError = err - } - } - - for _, fd := range c.closeAfterWait { - fd.Close() - } - - if err != nil { - return err - } else if !msg.Exited() || msg.ExitStatus() != 0 { - return &ExitError{msg} - } - - return copyError -} - -// Output runs the command and returns its standard output. -func (c *Cmd) Output() ([]byte, error) { - if c.Stdout != nil { - return nil, errors.New("exec: Stdout already set") - } - var b bytes.Buffer - c.Stdout = &b - err := c.Run() - return b.Bytes(), err -} - -// CombinedOutput runs the command and returns its combined standard -// output and standard error. -func (c *Cmd) CombinedOutput() ([]byte, error) { - if c.Stdout != nil { - return nil, errors.New("exec: Stdout already set") - } - if c.Stderr != nil { - return nil, errors.New("exec: Stderr already set") - } - var b bytes.Buffer - c.Stdout = &b - c.Stderr = &b - err := c.Run() - return b.Bytes(), err -} - -// StdinPipe returns a pipe that will be connected to the command's -// standard input when the command starts. -func (c *Cmd) StdinPipe() (io.WriteCloser, error) { - if c.Stdin != nil { - return nil, errors.New("exec: Stdin already set") - } - if c.Process != nil { - return nil, errors.New("exec: StdinPipe after process started") - } - pr, pw, err := os.Pipe() - if err != nil { - return nil, err - } - c.Stdin = pr - c.closeAfterStart = append(c.closeAfterStart, pr) - c.closeAfterWait = append(c.closeAfterWait, pw) - return pw, nil -} - -// StdoutPipe returns a pipe that will be connected to the command's -// standard output when the command starts. -// The pipe will be closed automatically after Wait sees the command exit. -func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { - if c.Stdout != nil { - return nil, errors.New("exec: Stdout already set") - } - if c.Process != nil { - return nil, errors.New("exec: StdoutPipe after process started") - } - pr, pw, err := os.Pipe() - if err != nil { - return nil, err - } - c.Stdout = pw - c.closeAfterStart = append(c.closeAfterStart, pw) - c.closeAfterWait = append(c.closeAfterWait, pr) - return pr, nil -} - -// StderrPipe returns a pipe that will be connected to the command's -// standard error when the command starts. -// The pipe will be closed automatically after Wait sees the command exit. -func (c *Cmd) StderrPipe() (io.ReadCloser, error) { - if c.Stderr != nil { - return nil, errors.New("exec: Stderr already set") - } - if c.Process != nil { - return nil, errors.New("exec: StderrPipe after process started") - } - pr, pw, err := os.Pipe() - if err != nil { - return nil, err - } - c.Stderr = pw - c.closeAfterStart = append(c.closeAfterStart, pw) - c.closeAfterWait = append(c.closeAfterWait, pr) - return pr, nil -} diff --git a/libgo/go/exec/exec_test.go b/libgo/go/exec/exec_test.go deleted file mode 100644 index 6d5e893..0000000 --- a/libgo/go/exec/exec_test.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package exec - -import ( - "bufio" - "bytes" - "fmt" - "io" - "io/ioutil" - "testing" - "os" - "runtime" - "strconv" - "strings" -) - -func helperCommand(s ...string) *Cmd { - cs := []string{"-test.run=exec.TestHelperProcess", "--"} - cs = append(cs, s...) - cmd := Command(os.Args[0], cs...) - cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) - return cmd -} - -func TestEcho(t *testing.T) { - bs, err := helperCommand("echo", "foo bar", "baz").Output() - if err != nil { - t.Errorf("echo: %v", err) - } - if g, e := string(bs), "foo bar baz\n"; g != e { - t.Errorf("echo: want %q, got %q", e, g) - } -} - -func TestCatStdin(t *testing.T) { - // Cat, testing stdin and stdout. - input := "Input string\nLine 2" - p := helperCommand("cat") - p.Stdin = strings.NewReader(input) - bs, err := p.Output() - if err != nil { - t.Errorf("cat: %v", err) - } - s := string(bs) - if s != input { - t.Errorf("cat: want %q, got %q", input, s) - } -} - -func TestCatGoodAndBadFile(t *testing.T) { - // Testing combined output and error values. - bs, err := helperCommand("cat", "/bogus/file.foo", "exec_test.go").CombinedOutput() - if _, ok := err.(*ExitError); !ok { - t.Errorf("expected *ExitError from cat combined; got %T: %v", err, err) - } - s := string(bs) - sp := strings.SplitN(s, "\n", 2) - if len(sp) != 2 { - t.Fatalf("expected two lines from cat; got %q", s) - } - errLine, body := sp[0], sp[1] - if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") { - t.Errorf("expected stderr to complain about file; got %q", errLine) - } - if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") { - t.Errorf("expected test code; got %q (len %d)", body, len(body)) - } -} - -func TestNoExistBinary(t *testing.T) { - // Can't run a non-existent binary - err := Command("/no-exist-binary").Run() - if err == nil { - t.Error("expected error from /no-exist-binary") - } -} - -func TestExitStatus(t *testing.T) { - // Test that exit values are returned correctly - err := helperCommand("exit", "42").Run() - if werr, ok := err.(*ExitError); ok { - if s, e := werr.Error(), "exit status 42"; s != e { - t.Errorf("from exit 42 got exit %q, want %q", s, e) - } - } else { - t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err) - } -} - -func TestPipes(t *testing.T) { - check := func(what string, err error) { - if err != nil { - t.Fatalf("%s: %v", what, err) - } - } - // Cat, testing stdin and stdout. - c := helperCommand("pipetest") - stdin, err := c.StdinPipe() - check("StdinPipe", err) - stdout, err := c.StdoutPipe() - check("StdoutPipe", err) - stderr, err := c.StderrPipe() - check("StderrPipe", err) - - outbr := bufio.NewReader(stdout) - errbr := bufio.NewReader(stderr) - line := func(what string, br *bufio.Reader) string { - line, _, err := br.ReadLine() - if err != nil { - t.Fatalf("%s: %v", what, err) - } - return string(line) - } - - err = c.Start() - check("Start", err) - - _, err = stdin.Write([]byte("O:I am output\n")) - check("first stdin Write", err) - if g, e := line("first output line", outbr), "O:I am output"; g != e { - t.Errorf("got %q, want %q", g, e) - } - - _, err = stdin.Write([]byte("E:I am error\n")) - check("second stdin Write", err) - if g, e := line("first error line", errbr), "E:I am error"; g != e { - t.Errorf("got %q, want %q", g, e) - } - - _, err = stdin.Write([]byte("O:I am output2\n")) - check("third stdin Write 3", err) - if g, e := line("second output line", outbr), "O:I am output2"; g != e { - t.Errorf("got %q, want %q", g, e) - } - - stdin.Close() - err = c.Wait() - check("Wait", err) -} - -func TestExtraFiles(t *testing.T) { - if runtime.GOOS == "windows" { - t.Logf("no operating system support; skipping") - return - } - tf, err := ioutil.TempFile("", "") - if err != nil { - t.Fatalf("TempFile: %v", err) - } - defer os.Remove(tf.Name()) - defer tf.Close() - - const text = "Hello, fd 3!" - _, err = tf.Write([]byte(text)) - if err != nil { - t.Fatalf("Write: %v", err) - } - _, err = tf.Seek(0, os.SEEK_SET) - if err != nil { - t.Fatalf("Seek: %v", err) - } - - c := helperCommand("read3") - c.ExtraFiles = []*os.File{tf} - bs, err := c.CombinedOutput() - if err != nil { - t.Fatalf("CombinedOutput: %v", err) - } - if string(bs) != text { - t.Errorf("got %q; want %q", string(bs), text) - } -} - -// TestHelperProcess isn't a real test. It's used as a helper process -// for TestParameterRun. -func TestHelperProcess(*testing.T) { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { - return - } - defer os.Exit(0) - - args := os.Args - for len(args) > 0 { - if args[0] == "--" { - args = args[1:] - break - } - args = args[1:] - } - if len(args) == 0 { - fmt.Fprintf(os.Stderr, "No command\n") - os.Exit(2) - } - - cmd, args := args[0], args[1:] - switch cmd { - case "echo": - iargs := []interface{}{} - for _, s := range args { - iargs = append(iargs, s) - } - fmt.Println(iargs...) - case "cat": - if len(args) == 0 { - io.Copy(os.Stdout, os.Stdin) - return - } - exit := 0 - for _, fn := range args { - f, err := os.Open(fn) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - exit = 2 - } else { - defer f.Close() - io.Copy(os.Stdout, f) - } - } - os.Exit(exit) - case "pipetest": - bufr := bufio.NewReader(os.Stdin) - for { - line, _, err := bufr.ReadLine() - if err == io.EOF { - break - } else if err != nil { - os.Exit(1) - } - if bytes.HasPrefix(line, []byte("O:")) { - os.Stdout.Write(line) - os.Stdout.Write([]byte{'\n'}) - } else if bytes.HasPrefix(line, []byte("E:")) { - os.Stderr.Write(line) - os.Stderr.Write([]byte{'\n'}) - } else { - os.Exit(1) - } - } - case "read3": // read fd 3 - fd3 := os.NewFile(3, "fd3") - bs, err := ioutil.ReadAll(fd3) - if err != nil { - fmt.Printf("ReadAll from fd 3: %v", err) - os.Exit(1) - } - os.Stderr.Write(bs) - case "exit": - n, _ := strconv.Atoi(args[0]) - os.Exit(n) - default: - fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) - os.Exit(2) - } -} diff --git a/libgo/go/exec/lp_plan9.go b/libgo/go/exec/lp_plan9.go deleted file mode 100644 index d4ffc17..0000000 --- a/libgo/go/exec/lp_plan9.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package exec - -import ( - "errors" - "os" - "strings" -) - -// ErrNotFound is the error resulting if a path search failed to find an executable file. -var ErrNotFound = errors.New("executable file not found in $path") - -func findExecutable(file string) error { - d, err := os.Stat(file) - if err != nil { - return err - } - if d.IsRegular() && d.Permission()&0111 != 0 { - return nil - } - return os.EPERM -} - -// LookPath searches for an executable binary named file -// in the directories named by the path environment variable. -// If file begins with "/", "#", "./", or "../", it is tried -// directly and the path is not consulted. -func LookPath(file string) (string, error) { - // skip the path lookup for these prefixes - skip := []string{"/", "#", "./", "../"} - - for _, p := range skip { - if strings.HasPrefix(file, p) { - err := findExecutable(file) - if err == nil { - return file, nil - } - return "", &Error{file, err} - } - } - - path := os.Getenv("path") - for _, dir := range strings.Split(path, "\000") { - if err := findExecutable(dir + "/" + file); err == nil { - return dir + "/" + file, nil - } - } - return "", &Error{file, ErrNotFound} -} diff --git a/libgo/go/exec/lp_test.go b/libgo/go/exec/lp_test.go deleted file mode 100644 index 77d8e84..0000000 --- a/libgo/go/exec/lp_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package exec - -import ( - "testing" -) - -var nonExistentPaths = []string{ - "some-non-existent-path", - "non-existent-path/slashed", -} - -func TestLookPathNotFound(t *testing.T) { - for _, name := range nonExistentPaths { - path, err := LookPath(name) - if err == nil { - t.Fatalf("LookPath found %q in $PATH", name) - } - if path != "" { - t.Fatalf("LookPath path == %q when err != nil", path) - } - perr, ok := err.(*Error) - if !ok { - t.Fatal("LookPath error is not an exec.Error") - } - if perr.Name != name { - t.Fatalf("want Error name %q, got %q", name, perr.Name) - } - } -} diff --git a/libgo/go/exec/lp_unix.go b/libgo/go/exec/lp_unix.go deleted file mode 100644 index d234641..0000000 --- a/libgo/go/exec/lp_unix.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux openbsd - -package exec - -import ( - "errors" - "os" - "strings" -) - -// ErrNotFound is the error resulting if a path search failed to find an executable file. -var ErrNotFound = errors.New("executable file not found in $PATH") - -func findExecutable(file string) error { - d, err := os.Stat(file) - if err != nil { - return err - } - if d.IsRegular() && d.Permission()&0111 != 0 { - return nil - } - return os.EPERM -} - -// LookPath searches for an executable binary named file -// in the directories named by the PATH environment variable. -// If file contains a slash, it is tried directly and the PATH is not consulted. -func LookPath(file string) (string, error) { - // NOTE(rsc): I wish we could use the Plan 9 behavior here - // (only bypass the path if file begins with / or ./ or ../) - // but that would not match all the Unix shells. - - if strings.Contains(file, "/") { - err := findExecutable(file) - if err == nil { - return file, nil - } - return "", &Error{file, err} - } - pathenv := os.Getenv("PATH") - for _, dir := range strings.Split(pathenv, ":") { - if dir == "" { - // Unix shell semantics: path element "" means "." - dir = "." - } - if err := findExecutable(dir + "/" + file); err == nil { - return dir + "/" + file, nil - } - } - return "", &Error{file, ErrNotFound} -} diff --git a/libgo/go/exec/lp_windows.go b/libgo/go/exec/lp_windows.go deleted file mode 100644 index db32623..0000000 --- a/libgo/go/exec/lp_windows.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package exec - -import ( - "errors" - "os" - "strings" -) - -// ErrNotFound is the error resulting if a path search failed to find an executable file. -var ErrNotFound = errors.New("executable file not found in %PATH%") - -func chkStat(file string) error { - d, err := os.Stat(file) - if err != nil { - return err - } - if d.IsRegular() { - return nil - } - return os.EPERM -} - -func findExecutable(file string, exts []string) (string, error) { - if len(exts) == 0 { - return file, chkStat(file) - } - f := strings.ToLower(file) - for _, e := range exts { - if strings.HasSuffix(f, e) { - return file, chkStat(file) - } - } - for _, e := range exts { - if f := file + e; chkStat(f) == nil { - return f, nil - } - } - return ``, os.ENOENT -} - -func LookPath(file string) (f string, err error) { - x := os.Getenv(`PATHEXT`) - if x == `` { - x = `.COM;.EXE;.BAT;.CMD` - } - exts := []string{} - for _, e := range strings.Split(strings.ToLower(x), `;`) { - if e == "" { - continue - } - if e[0] != '.' { - e = "." + e - } - exts = append(exts, e) - } - if strings.IndexAny(file, `:\/`) != -1 { - if f, err = findExecutable(file, exts); err == nil { - return - } - return ``, &Error{file, err} - } - if pathenv := os.Getenv(`PATH`); pathenv == `` { - if f, err = findExecutable(`.\`+file, exts); err == nil { - return - } - } else { - for _, dir := range strings.Split(pathenv, `;`) { - if f, err = findExecutable(dir+`\`+file, exts); err == nil { - return - } - } - } - return ``, &Error{file, ErrNotFound} -} |