-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LookPath like impelementations for OS specific executable searches
Signed-off-by: Arthur Sengileyev <[email protected]>
- Loading branch information
Showing
3 changed files
with
162 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || zos | ||
// +build aix darwin dragonfly freebsd netbsd openbsd solaris zos | ||
|
||
package config | ||
|
||
import ( | ||
"errors" | ||
"io/fs" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"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 m := d.Mode(); !m.IsDir() && m&0111 != 0 { | ||
return nil | ||
} | ||
return fs.ErrPermission | ||
} | ||
|
||
// lookPathExplicit searches for an executable named file in the | ||
// directories listed in dirs parameter. | ||
// If file contains a slash, it is tried directly and the PATH is not consulted. | ||
// The result may be an absolute path or a path relative to the current directory. | ||
func lookPathExplicit(file string, dirs []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 "", &exec.Error{file, err} | ||
} | ||
for _, dir := range dirs { | ||
if dir == "" { | ||
// Unix shell semantics: path element "" means "." | ||
dir = "." | ||
} | ||
path := filepath.Join(dir, file) | ||
if err := findExecutable(path); err == nil { | ||
return path, nil | ||
} | ||
} | ||
return "", &exec.Error{file, ErrNotFound} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package config | ||
|
||
import ( | ||
"errors" | ||
"io/fs" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"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.IsDir() { | ||
return fs.ErrPermission | ||
} | ||
return nil | ||
} | ||
|
||
func hasExt(file string) bool { | ||
i := strings.LastIndex(file, ".") | ||
if i < 0 { | ||
return false | ||
} | ||
return strings.LastIndexAny(file, `:\/`) < i | ||
} | ||
|
||
func findExecutable(file string, exts []string) (string, error) { | ||
if len(exts) == 0 { | ||
return file, chkStat(file) | ||
} | ||
if hasExt(file) { | ||
if chkStat(file) == nil { | ||
return file, nil | ||
} | ||
} | ||
for _, e := range exts { | ||
if f := file + e; chkStat(f) == nil { | ||
return f, nil | ||
} | ||
} | ||
return "", fs.ErrNotExist | ||
} | ||
|
||
// lookPathExplicit searches for an executable named file in the | ||
// directories listed in dirs parameter. | ||
// If file contains a slash, it is tried directly and the PATH is not consulted. | ||
// LookPath also uses PATHEXT environment variable to match | ||
// a suitable candidate. | ||
// The result may be an absolute path or a path relative to the current directory. | ||
func lookPathExplicit(file string, dirs []string) (string, error) { | ||
var exts []string | ||
x := os.Getenv(`PATHEXT`) | ||
if x != "" { | ||
for _, e := range strings.Split(strings.ToLower(x), `;`) { | ||
if e == "" { | ||
continue | ||
} | ||
if e[0] != '.' { | ||
e = "." + e | ||
} | ||
exts = append(exts, e) | ||
} | ||
} else { | ||
exts = []string{".com", ".exe", ".bat", ".cmd"} | ||
} | ||
|
||
if strings.ContainsAny(file, `:\/`) { | ||
if f, err := findExecutable(file, exts); err == nil { | ||
return f, nil | ||
} else { | ||
return "", &exec.Error{file, err} | ||
} | ||
} | ||
if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { | ||
return f, nil | ||
} | ||
for _, dir := range dirs { | ||
if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { | ||
return f, nil | ||
} | ||
} | ||
return "", &exec.Error{file, ErrNotFound} | ||
} |