|
|
@@ -0,0 +1,111 @@
|
|
|
+/*
|
|
|
+ Copyright 2020 Docker Compose CLI authors
|
|
|
+
|
|
|
+ Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ you may not use this file except in compliance with the License.
|
|
|
+ You may obtain a copy of the License at
|
|
|
+
|
|
|
+ http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+
|
|
|
+ Unless required by applicable law or agreed to in writing, software
|
|
|
+ distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ See the License for the specific language governing permissions and
|
|
|
+ limitations under the License.
|
|
|
+*/
|
|
|
+
|
|
|
+package mobycli
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "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 os.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 "", os.ErrNotExist
|
|
|
+}
|
|
|
+
|
|
|
+// LookPath searches for an executable 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.
|
|
|
+// 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 LookPath(file 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}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // DO NOT lookup current folder
|
|
|
+ //if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
|
|
|
+ // return f, nil
|
|
|
+ //}
|
|
|
+ path := os.Getenv("path")
|
|
|
+ for _, dir := range filepath.SplitList(path) {
|
|
|
+ // empty dir means dupicate semicolon in PATH, should not resolve files in current working dir...
|
|
|
+ if strings.TrimSpace(dir) == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
|
|
|
+ return f, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "", &exec.Error{file, ErrNotFound}
|
|
|
+}
|