mirror of
https://github.com/golang/go
synced 2024-11-25 05:57:57 -07:00
exec: change exec.PathError to exec.Error
There were two issues: 1) It might not be a path error, it might be 'permission denied'. 2) The concept of $PATH is Unix-specific. R=alex.brainman, rsc, r, mattn.jp CC=golang-dev https://golang.org/cl/4530096
This commit is contained in:
parent
31c79c4eff
commit
4e9e925002
@ -14,14 +14,15 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PathError records the name of a binary that was not
|
// Error records the name of a binary that failed to be be executed
|
||||||
// found on the current $PATH.
|
// and the reason it failed.
|
||||||
type PathError struct {
|
type Error struct {
|
||||||
Name string
|
Name string
|
||||||
|
Error os.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *PathError) String() string {
|
func (e *Error) String() string {
|
||||||
return "command " + strconv.Quote(e.Name) + " not found in $PATH"
|
return "exec: " + strconv.Quote(e.Name) + ": " + e.Error.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cmd represents an external command being prepared or run.
|
// Cmd represents an external command being prepared or run.
|
||||||
@ -32,8 +33,8 @@ type Cmd struct {
|
|||||||
// value.
|
// value.
|
||||||
Path string
|
Path string
|
||||||
|
|
||||||
// Args is the command line arguments, including the command as Args[0].
|
// Args holds command line arguments, including the command as Args[0].
|
||||||
// If Args is empty, Run uses {Path}.
|
// If the Args field is empty or nil, Run uses {Path}.
|
||||||
//
|
//
|
||||||
// In typical use, both Path and Args are set by calling Command.
|
// In typical use, both Path and Args are set by calling Command.
|
||||||
Args []string
|
Args []string
|
||||||
@ -44,7 +45,7 @@ type Cmd struct {
|
|||||||
|
|
||||||
// Dir specifies the working directory of the command.
|
// Dir specifies the working directory of the command.
|
||||||
// If Dir is the empty string, Run runs the command in the
|
// If Dir is the empty string, Run runs the command in the
|
||||||
// process's current directory.
|
// calling process's current directory.
|
||||||
Dir string
|
Dir string
|
||||||
|
|
||||||
// Stdin specifies the process's standard input.
|
// Stdin specifies the process's standard input.
|
||||||
@ -81,7 +82,7 @@ type Cmd struct {
|
|||||||
// resolve the path to a complete name if possible. Otherwise it uses
|
// resolve the path to a complete name if possible. Otherwise it uses
|
||||||
// name directly.
|
// name directly.
|
||||||
//
|
//
|
||||||
// The returned Cmd's Args is constructed from the command name
|
// The returned Cmd's Args field is constructed from the command name
|
||||||
// followed by the elements of arg, so arg should not include the
|
// followed by the elements of arg, so arg should not include the
|
||||||
// command name itself. For example, Command("echo", "hello")
|
// command name itself. For example, Command("echo", "hello")
|
||||||
func Command(name string, arg ...string) *Cmd {
|
func Command(name string, arg ...string) *Cmd {
|
||||||
@ -97,7 +98,7 @@ func Command(name string, arg ...string) *Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// interfaceEqual protects against panics from doing equality tests on
|
// interfaceEqual protects against panics from doing equality tests on
|
||||||
// two interface with non-comparable underlying types
|
// two interfaces with non-comparable underlying types
|
||||||
func interfaceEqual(a, b interface{}) bool {
|
func interfaceEqual(a, b interface{}) bool {
|
||||||
defer func() {
|
defer func() {
|
||||||
recover()
|
recover()
|
||||||
|
@ -22,12 +22,12 @@ func TestLookPathNotFound(t *testing.T) {
|
|||||||
if path != "" {
|
if path != "" {
|
||||||
t.Fatalf("LookPath path == %q when err != nil", path)
|
t.Fatalf("LookPath path == %q when err != nil", path)
|
||||||
}
|
}
|
||||||
perr, ok := err.(*PathError)
|
perr, ok := err.(*Error)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("LookPath error is not a PathError")
|
t.Fatal("LookPath error is not an exec.Error")
|
||||||
}
|
}
|
||||||
if perr.Name != name {
|
if perr.Name != name {
|
||||||
t.Fatalf("want PathError name %q, got %q", name, perr.Name)
|
t.Fatalf("want Error name %q, got %q", name, perr.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func canExec(file string) bool {
|
// ErrNotFound is the error resulting if a path search failed to find an executable file.
|
||||||
|
var ErrNotFound = os.ErrorString("executable file not found in $PATH")
|
||||||
|
|
||||||
|
func findExecutable(file string) os.Error {
|
||||||
d, err := os.Stat(file)
|
d, err := os.Stat(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
return d.IsRegular() && d.Permission()&0111 != 0
|
if d.IsRegular() && d.Permission()&0111 != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.EPERM
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookPath searches for an executable binary named file
|
// LookPath searches for an executable binary named file
|
||||||
@ -26,10 +32,11 @@ func LookPath(file string) (string, os.Error) {
|
|||||||
// but that would not match all the Unix shells.
|
// but that would not match all the Unix shells.
|
||||||
|
|
||||||
if strings.Contains(file, "/") {
|
if strings.Contains(file, "/") {
|
||||||
if canExec(file) {
|
err := findExecutable(file)
|
||||||
|
if err == nil {
|
||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
return "", &PathError{file}
|
return "", &Error{file, err}
|
||||||
}
|
}
|
||||||
pathenv := os.Getenv("PATH")
|
pathenv := os.Getenv("PATH")
|
||||||
for _, dir := range strings.Split(pathenv, ":", -1) {
|
for _, dir := range strings.Split(pathenv, ":", -1) {
|
||||||
@ -37,9 +44,9 @@ func LookPath(file string) (string, os.Error) {
|
|||||||
// Unix shell semantics: path element "" means "."
|
// Unix shell semantics: path element "" means "."
|
||||||
dir = "."
|
dir = "."
|
||||||
}
|
}
|
||||||
if canExec(dir + "/" + file) {
|
if err := findExecutable(dir + "/" + file); err == nil {
|
||||||
return dir + "/" + file, nil
|
return dir + "/" + file, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", &PathError{file}
|
return "", &Error{file, ErrNotFound}
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func chkStat(file string) bool {
|
// ErrNotFound is the error resulting if a path search failed to find an executable file.
|
||||||
|
var ErrNotFound = os.ErrorString("executable file not found in %PATH%")
|
||||||
|
|
||||||
|
func chkStat(file string) os.Error {
|
||||||
d, err := os.Stat(file)
|
d, err := os.Stat(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
return d.IsRegular()
|
if d.IsRegular() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.EPERM
|
||||||
}
|
}
|
||||||
|
|
||||||
func canExec(file string, exts []string) (string, bool) {
|
func findExecutable(file string, exts []string) (string, os.Error) {
|
||||||
if len(exts) == 0 {
|
if len(exts) == 0 {
|
||||||
return file, chkStat(file)
|
return file, chkStat(file)
|
||||||
}
|
}
|
||||||
@ -28,14 +34,14 @@ func canExec(file string, exts []string) (string, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, e := range exts {
|
for _, e := range exts {
|
||||||
if f := file + e; chkStat(f) {
|
if f := file + e; chkStat(f) == nil {
|
||||||
return f, true
|
return f, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ``, false
|
return ``, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookPath(file string) (string, os.Error) {
|
func LookPath(file string) (f string, err os.Error) {
|
||||||
exts := []string{}
|
exts := []string{}
|
||||||
if x := os.Getenv(`PATHEXT`); x != `` {
|
if x := os.Getenv(`PATHEXT`); x != `` {
|
||||||
exts = strings.Split(strings.ToLower(x), `;`, -1)
|
exts = strings.Split(strings.ToLower(x), `;`, -1)
|
||||||
@ -46,21 +52,21 @@ func LookPath(file string) (string, os.Error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.Contains(file, `\`) || strings.Contains(file, `/`) {
|
if strings.Contains(file, `\`) || strings.Contains(file, `/`) {
|
||||||
if f, ok := canExec(file, exts); ok {
|
if f, err = findExecutable(file, exts); err == nil {
|
||||||
return f, nil
|
return
|
||||||
}
|
}
|
||||||
return ``, &PathError{file}
|
return ``, &Error{file, err}
|
||||||
}
|
}
|
||||||
if pathenv := os.Getenv(`PATH`); pathenv == `` {
|
if pathenv := os.Getenv(`PATH`); pathenv == `` {
|
||||||
if f, ok := canExec(`.\`+file, exts); ok {
|
if f, err = findExecutable(`.\`+file, exts); err == nil {
|
||||||
return f, nil
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, dir := range strings.Split(pathenv, `;`, -1) {
|
for _, dir := range strings.Split(pathenv, `;`, -1) {
|
||||||
if f, ok := canExec(dir+`\`+file, exts); ok {
|
if f, err = findExecutable(dir+`\`+file, exts); err == nil {
|
||||||
return f, nil
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ``, &PathError{file}
|
return ``, &Error{file, ErrNotFound}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user