diff options
Diffstat (limited to 'src/pkg/exec')
-rw-r--r-- | src/pkg/exec/Makefile | 16 | ||||
-rw-r--r-- | src/pkg/exec/exec.go | 46 | ||||
-rw-r--r-- | src/pkg/exec/exec_test.go | 46 | ||||
-rw-r--r-- | src/pkg/exec/lp_unix.go | 45 | ||||
-rw-r--r-- | src/pkg/exec/lp_windows.go | 66 |
5 files changed, 170 insertions, 49 deletions
diff --git a/src/pkg/exec/Makefile b/src/pkg/exec/Makefile index 9d88b8d8a..262ecac85 100644 --- a/src/pkg/exec/Makefile +++ b/src/pkg/exec/Makefile @@ -2,10 +2,24 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=exec GOFILES=\ exec.go\ +GOFILES_freebsd=\ + lp_unix.go\ + +GOFILES_darwin=\ + lp_unix.go\ + +GOFILES_linux=\ + lp_unix.go\ + +GOFILES_windows=\ + lp_windows.go\ + +GOFILES+=$(GOFILES_$(GOOS)) + include ../../Make.pkg diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index ee3cec686..ba9bd2472 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -7,7 +7,6 @@ package exec import ( "os" - "strings" ) // Arguments to Run. @@ -39,7 +38,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { if fd == 0 { rw = os.O_RDONLY } - f, err := os.Open("/dev/null", rw, 0) + f, err := os.Open(os.DevNull, rw, 0) return f, nil, err case PassThrough: switch fd { @@ -63,7 +62,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { return nil, nil, os.EINVAL } -// Run starts the binary prog running with +// Run starts the named binary running with // arguments argv and environment envv. // It returns a pointer to a new Cmd representing // the command or an error. @@ -78,7 +77,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { // If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr) // of the returned Cmd is the other end of the pipe. // Otherwise the field in Cmd is nil. -func Run(argv0 string, argv, envv []string, dir string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { +func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { p = new(Cmd) var fd [3]*os.File @@ -95,7 +94,7 @@ func Run(argv0 string, argv, envv []string, dir string, stdin, stdout, stderr in } // Run command. - p.Pid, err = os.ForkExec(argv0, argv, envv, dir, fd[0:]) + p.Pid, err = os.ForkExec(name, argv, envv, dir, fd[0:]) if err != nil { goto Error } @@ -182,40 +181,3 @@ func (p *Cmd) Close() os.Error { } return err } - -func canExec(file string) bool { - d, err := os.Stat(file) - if err != nil { - return false - } - return d.IsRegular() && d.Permission()&0111 != 0 -} - -// 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. -// -// TODO(rsc): Does LookPath belong in os instead? -func LookPath(file string) (string, os.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.Index(file, "/") >= 0 { - if canExec(file) { - return file, nil - } - return "", os.ENOENT - } - pathenv := os.Getenv("PATH") - for _, dir := range strings.Split(pathenv, ":", -1) { - if dir == "" { - // Unix shell semantics: path element "" means "." - dir = "." - } - if canExec(dir + "/" + file) { - return dir + "/" + file, nil - } - } - return "", os.ENOENT -} diff --git a/src/pkg/exec/exec_test.go b/src/pkg/exec/exec_test.go index 3e4ab7d78..3a3d3b1a5 100644 --- a/src/pkg/exec/exec_test.go +++ b/src/pkg/exec/exec_test.go @@ -8,11 +8,24 @@ import ( "io" "io/ioutil" "testing" + "os" + "runtime" ) +func run(argv []string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { + if runtime.GOOS == "windows" { + argv = append([]string{"cmd", "/c"}, argv...) + } + exe, err := LookPath(argv[0]) + if err != nil { + return nil, err + } + p, err = Run(exe, argv, nil, "", stdin, stdout, stderr) + return p, err +} + func TestRunCat(t *testing.T) { - cmd, err := Run("/bin/cat", []string{"cat"}, nil, "", - Pipe, Pipe, DevNull) + cmd, err := run([]string{"cat"}, Pipe, Pipe, DevNull) if err != nil { t.Fatal("run:", err) } @@ -31,7 +44,7 @@ func TestRunCat(t *testing.T) { } func TestRunEcho(t *testing.T) { - cmd, err := Run("/bin/echo", []string{"echo", "hello", "world"}, nil, "", + cmd, err := run([]string{"sh", "-c", "echo hello world"}, DevNull, Pipe, DevNull) if err != nil { t.Fatal("run:", err) @@ -49,7 +62,7 @@ func TestRunEcho(t *testing.T) { } func TestStderr(t *testing.T) { - cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo hello world 1>&2"}, nil, "", + cmd, err := run([]string{"sh", "-c", "echo hello world 1>&2"}, DevNull, DevNull, Pipe) if err != nil { t.Fatal("run:", err) @@ -66,9 +79,8 @@ func TestStderr(t *testing.T) { } } - func TestMergeWithStdout(t *testing.T) { - cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo hello world 1>&2"}, nil, "", + cmd, err := run([]string{"sh", "-c", "echo hello world 1>&2"}, DevNull, Pipe, MergeWithStdout) if err != nil { t.Fatal("run:", err) @@ -84,3 +96,25 @@ func TestMergeWithStdout(t *testing.T) { t.Fatal("close:", err) } } + +func TestAddEnvVar(t *testing.T) { + err := os.Setenv("NEWVAR", "hello world") + if err != nil { + t.Fatal("setenv:", err) + } + cmd, err := run([]string{"sh", "-c", "echo $NEWVAR"}, + DevNull, Pipe, DevNull) + if err != nil { + t.Fatal("run:", err) + } + buf, err := ioutil.ReadAll(cmd.Stdout) + if err != nil { + t.Fatal("read:", err) + } + if string(buf) != "hello world\n" { + t.Fatalf("read: got %q", buf) + } + if err = cmd.Close(); err != nil { + t.Fatal("close:", err) + } +} diff --git a/src/pkg/exec/lp_unix.go b/src/pkg/exec/lp_unix.go new file mode 100644 index 000000000..292e24fcc --- /dev/null +++ b/src/pkg/exec/lp_unix.go @@ -0,0 +1,45 @@ +// 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 ( + "os" + "strings" +) + +func canExec(file string) bool { + d, err := os.Stat(file) + if err != nil { + return false + } + return d.IsRegular() && d.Permission()&0111 != 0 +} + +// 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, os.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, "/") { + if canExec(file) { + return file, nil + } + return "", &os.PathError{"lookpath", file, os.ENOENT} + } + pathenv := os.Getenv("PATH") + for _, dir := range strings.Split(pathenv, ":", -1) { + if dir == "" { + // Unix shell semantics: path element "" means "." + dir = "." + } + if canExec(dir + "/" + file) { + return dir + "/" + file, nil + } + } + return "", &os.PathError{"lookpath", file, os.ENOENT} +} diff --git a/src/pkg/exec/lp_windows.go b/src/pkg/exec/lp_windows.go new file mode 100644 index 000000000..7b56afa85 --- /dev/null +++ b/src/pkg/exec/lp_windows.go @@ -0,0 +1,66 @@ +// 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 ( + "os" + "strings" +) + +func chkStat(file string) bool { + d, err := os.Stat(file) + if err != nil { + return false + } + return d.IsRegular() +} + +func canExec(file string, exts []string) (string, bool) { + 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) { + return f, true + } + } + return ``, false +} + +func LookPath(file string) (string, os.Error) { + exts := []string{} + if x := os.Getenv(`PATHEXT`); x != `` { + exts = strings.Split(strings.ToLower(x), `;`, -1) + for i, e := range exts { + if e == `` || e[0] != '.' { + exts[i] = `.` + e + } + } + } + if strings.Contains(file, `\`) || strings.Contains(file, `/`) { + if f, ok := canExec(file, exts); ok { + return f, nil + } + return ``, &os.PathError{"lookpath", file, os.ENOENT} + } + if pathenv := os.Getenv(`PATH`); pathenv == `` { + if f, ok := canExec(`.\`+file, exts); ok { + return f, nil + } + } else { + for _, dir := range strings.Split(pathenv, `;`, -1) { + if f, ok := canExec(dir+`\`+file, exts); ok { + return f, nil + } + } + } + return ``, &os.PathError{"lookpath", file, os.ENOENT} +} |