1// Copyright 2010 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package exec 6 7import ( 8 "errors" 9 "io/fs" 10 "os" 11 "path/filepath" 12 "strings" 13) 14 15// ErrNotFound is the error resulting if a path search failed to find an executable file. 16var ErrNotFound = errors.New("executable file not found in %PATH%") 17 18func chkStat(file string) error { 19 d, err := os.Stat(file) 20 if err != nil { 21 return err 22 } 23 if d.IsDir() { 24 return fs.ErrPermission 25 } 26 return nil 27} 28 29func hasExt(file string) bool { 30 i := strings.LastIndex(file, ".") 31 if i < 0 { 32 return false 33 } 34 return strings.LastIndexAny(file, `:\/`) < i 35} 36 37func findExecutable(file string, exts []string) (string, error) { 38 if len(exts) == 0 { 39 return file, chkStat(file) 40 } 41 if hasExt(file) { 42 if chkStat(file) == nil { 43 return file, nil 44 } 45 } 46 for _, e := range exts { 47 if f := file + e; chkStat(f) == nil { 48 return f, nil 49 } 50 } 51 return "", fs.ErrNotExist 52} 53 54// LookPath searches for an executable named file in the 55// directories named by the PATH environment variable. 56// If file contains a slash, it is tried directly and the PATH is not consulted. 57// LookPath also uses PATHEXT environment variable to match 58// a suitable candidate. 59// The result may be an absolute path or a path relative to the current directory. 60func LookPath(file string) (string, error) { 61 var exts []string 62 x := os.Getenv(`PATHEXT`) 63 if x != "" { 64 for _, e := range strings.Split(strings.ToLower(x), `;`) { 65 if e == "" { 66 continue 67 } 68 if e[0] != '.' { 69 e = "." + e 70 } 71 exts = append(exts, e) 72 } 73 } else { 74 exts = []string{".com", ".exe", ".bat", ".cmd"} 75 } 76 77 if strings.ContainsAny(file, `:\/`) { 78 if f, err := findExecutable(file, exts); err == nil { 79 return f, nil 80 } else { 81 return "", &Error{file, err} 82 } 83 } 84 if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { 85 return f, nil 86 } 87 path := os.Getenv("path") 88 for _, dir := range filepath.SplitList(path) { 89 if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { 90 return f, nil 91 } 92 } 93 return "", &Error{file, ErrNotFound} 94} 95