exec.LookPath() unix/windows separation

R=brainman, rsc, vcc, rsc1
CC=golang-dev
https://golang.org/cl/2068041
This commit is contained in:
Joe Poirier 2010-09-12 17:38:36 +10:00 committed by Alex Brainman
parent e181bf6e2f
commit b155a76a40
5 changed files with 153 additions and 44 deletions

View File

@ -8,4 +8,21 @@ TARG=exec
GOFILES=\
exec.go\
GOFILES_freebsd=\
lp_unix.go\
GOFILES_darwin=\
lp_unix.go\
GOFILES_linux=\
lp_unix.go\
GOFILES_nacl=\
lp_unix.go\
GOFILES_windows=\
lp_windows.go\
GOFILES+=$(GOFILES_$(GOOS))
include ../../Make.pkg

View File

@ -7,7 +7,6 @@ package exec
import (
"os"
"strings"
)
// Arguments to Run.
@ -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
}

View File

@ -12,7 +12,11 @@ import (
)
func TestRunCat(t *testing.T) {
cmd, err := Run("/bin/cat", []string{"cat"}, nil, "",
cat, err := LookPath("cat")
if err != nil {
t.Fatal("cat: ", err)
}
cmd, err := Run(cat, []string{"cat"}, nil, "",
Pipe, Pipe, DevNull)
if err != nil {
t.Fatal("run:", err)
@ -32,7 +36,11 @@ func TestRunCat(t *testing.T) {
}
func TestRunEcho(t *testing.T) {
cmd, err := Run("/bin/echo", []string{"echo", "hello", "world"}, nil, "",
echo, err := LookPath("echo")
if err != nil {
t.Fatal("echo: ", err)
}
cmd, err := Run(echo, []string{"echo", "hello", "world"}, nil, "",
DevNull, Pipe, DevNull)
if err != nil {
t.Fatal("run:", err)
@ -50,7 +58,11 @@ func TestRunEcho(t *testing.T) {
}
func TestStderr(t *testing.T) {
cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo hello world 1>&2"}, nil, "",
sh, err := LookPath("sh")
if err != nil {
t.Fatal("sh: ", err)
}
cmd, err := Run(sh, []string{"sh", "-c", "echo hello world 1>&2"}, nil, "",
DevNull, DevNull, Pipe)
if err != nil {
t.Fatal("run:", err)
@ -67,9 +79,12 @@ func TestStderr(t *testing.T) {
}
}
func TestMergeWithStdout(t *testing.T) {
cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo hello world 1>&2"}, nil, "",
sh, err := LookPath("sh")
if err != nil {
t.Fatal("sh: ", err)
}
cmd, err := Run(sh, []string{"sh", "-c", "echo hello world 1>&2"}, nil, "",
DevNull, Pipe, MergeWithStdout)
if err != nil {
t.Fatal("run:", err)
@ -91,7 +106,11 @@ func TestAddEnvVar(t *testing.T) {
if err != nil {
t.Fatal("setenv:", err)
}
cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo $NEWVAR"}, nil, "",
sh, err := LookPath("sh")
if err != nil {
t.Fatal("sh: ", err)
}
cmd, err := Run(sh, []string{"sh", "-c", "echo $NEWVAR"}, nil, "",
DevNull, Pipe, DevNull)
if err != nil {
t.Fatal("run:", err)

45
src/pkg/exec/lp_unix.go Normal file
View File

@ -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.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
}

View File

@ -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.Index(file, `\`) >= 0 || strings.Index(file, `/`) >= 0 {
if f, ok := canExec(file, exts); ok {
return f, nil
}
return ``, 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.ENOENT
}