diff options
Diffstat (limited to 'libgo/go/os')
-rw-r--r-- | libgo/go/os/dir_unix.go | 2 | ||||
-rw-r--r-- | libgo/go/os/env_plan9.go | 2 | ||||
-rw-r--r-- | libgo/go/os/env_windows.go | 2 | ||||
-rw-r--r-- | libgo/go/os/error_plan9.go | 2 | ||||
-rw-r--r-- | libgo/go/os/exec/exec.go | 393 | ||||
-rw-r--r-- | libgo/go/os/exec/exec_test.go | 257 | ||||
-rw-r--r-- | libgo/go/os/exec/lp_plan9.go | 52 | ||||
-rw-r--r-- | libgo/go/os/exec/lp_test.go | 33 | ||||
-rw-r--r-- | libgo/go/os/exec/lp_unix.go | 55 | ||||
-rw-r--r-- | libgo/go/os/exec/lp_windows.go | 78 | ||||
-rw-r--r-- | libgo/go/os/exec_plan9.go | 3 | ||||
-rw-r--r-- | libgo/go/os/file_unix.go | 6 | ||||
-rw-r--r-- | libgo/go/os/path_test.go | 2 |
13 files changed, 878 insertions, 9 deletions
diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index e59c1af2ea7..a16bcf63f41 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -19,7 +19,7 @@ const ( // // If n > 0, Readdirnames returns at most n names. In this case, if // Readdirnames returns an empty slice, it will return a non-nil error -// explaining why. At the end of a directory, the error is os.EOF. +// explaining why. At the end of a directory, the error is io.EOF. // // If n <= 0, Readdirnames returns all the names from the directory in // a single slice. In this case, if Readdirnames succeeds (reads all diff --git a/libgo/go/os/env_plan9.go b/libgo/go/os/env_plan9.go index 762734a54c4..9757aa902af 100644 --- a/libgo/go/os/env_plan9.go +++ b/libgo/go/os/env_plan9.go @@ -7,7 +7,7 @@ package os import ( - "error" + "errors" "syscall" ) diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go index 795da21a44a..4e90385da96 100644 --- a/libgo/go/os/env_windows.go +++ b/libgo/go/os/env_windows.go @@ -9,7 +9,7 @@ package os import ( "errors" "syscall" - "utf16" + "unicode/utf16" "unsafe" ) diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go index 1e5114dc07f..e08707078ec 100644 --- a/libgo/go/os/error_plan9.go +++ b/libgo/go/os/error_plan9.go @@ -28,7 +28,7 @@ func NewSyscallError(syscall string, err syscall.Error) error { if err == nil { return nil } - return &SyscallError{syscall, err.String()} + return &SyscallError{syscall, err.Error()} } var ( diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go new file mode 100644 index 00000000000..ebdfd54a734 --- /dev/null +++ b/libgo/go/os/exec/exec.go @@ -0,0 +1,393 @@ +// 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/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go new file mode 100644 index 00000000000..8f63653c014 --- /dev/null +++ b/libgo/go/os/exec/exec_test.go @@ -0,0 +1,257 @@ +// 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" + "os" + "runtime" + "strconv" + "strings" + "testing" +) + +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/os/exec/lp_plan9.go b/libgo/go/os/exec/lp_plan9.go new file mode 100644 index 00000000000..d4ffc17c006 --- /dev/null +++ b/libgo/go/os/exec/lp_plan9.go @@ -0,0 +1,52 @@ +// 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/os/exec/lp_test.go b/libgo/go/os/exec/lp_test.go new file mode 100644 index 00000000000..77d8e848c74 --- /dev/null +++ b/libgo/go/os/exec/lp_test.go @@ -0,0 +1,33 @@ +// 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/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go new file mode 100644 index 00000000000..d234641acc4 --- /dev/null +++ b/libgo/go/os/exec/lp_unix.go @@ -0,0 +1,55 @@ +// 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/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go new file mode 100644 index 00000000000..db326236ee8 --- /dev/null +++ b/libgo/go/os/exec/lp_windows.go @@ -0,0 +1,78 @@ +// 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} +} diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index a815c99d68d..a1a335359dc 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -5,6 +5,7 @@ package os import ( + "errors" "runtime" "syscall" ) @@ -47,7 +48,7 @@ func (note Plan9Note) String() string { func (p *Process) Signal(sig Signal) error { if p.done { - return NewError("os: process already finished") + return errors.New("os: process already finished") } f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0) diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index f196f1f770b..a7ccb337eb5 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -39,7 +39,7 @@ func NewFile(fd int, name string) *File { // Auxiliary information if the File describes a directory type dirInfo struct { - buf []byte // buffer for directory I/O + buf []byte // buffer for directory I/O dir *syscall.DIR // from opendir } @@ -79,7 +79,7 @@ func (file *File) Close() error { } if file.dirinfo != nil { - if libc_closedir(file.dirinfo.dir) < 0 && err == nil { + if libc_closedir(file.dirinfo.dir) < 0 && err == nil { err = &PathError{"closedir", file.name, Errno(syscall.GetErrno())} } } @@ -142,7 +142,7 @@ func Lstat(name string) (fi *FileInfo, err error) { // // If n > 0, Readdir returns at most n FileInfo structures. In this case, if // Readdir returns an empty slice, it will return a non-nil error -// explaining why. At the end of a directory, the error is os.EOF. +// explaining why. At the end of a directory, the error is io.EOF. // // If n <= 0, Readdir returns all the FileInfo from the directory in // a single slice. In this case, if Readdir succeeds (reads all diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index f0da186ac9b..89d66c29ef9 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -7,9 +7,9 @@ package os_test import ( . "os" "path/filepath" - "testing" "runtime" "syscall" + "testing" ) func TestMkdirAll(t *testing.T) { |