github.com/rogpeppe/go-internal@v1.12.1-0.20240509064211-c8567cf8e95f/internal/os/execpath/lp_unix.go (about) 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 5 //go:build aix || darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris 6 7 package execpath 8 9 import ( 10 "os" 11 "path/filepath" 12 "strings" 13 ) 14 15 func findExecutable(file string) error { 16 d, err := os.Stat(file) 17 if err != nil { 18 return err 19 } 20 if m := d.Mode(); !m.IsDir() && m&0111 != 0 { 21 return nil 22 } 23 return os.ErrPermission 24 } 25 26 // Look searches for an executable named file, using getenv to look up 27 // environment variables. If getenv is nil, os.Getenv will be used. If file 28 // contains a slash, it is tried directly and getenv will not be called. The 29 // result may be an absolute path or a path relative to the current directory. 30 func Look(file string, getenv func(string) string) (string, error) { 31 if getenv == nil { 32 getenv = os.Getenv 33 } 34 35 // NOTE(rsc): I wish we could use the Plan 9 behavior here 36 // (only bypass the path if file begins with / or ./ or ../) 37 // but that would not match all the Unix shells. 38 39 if strings.Contains(file, "/") { 40 err := findExecutable(file) 41 if err == nil { 42 return file, nil 43 } 44 return "", &Error{file, err} 45 } 46 path := getenv("PATH") 47 for _, dir := range filepath.SplitList(path) { 48 if dir == "" { 49 // Unix shell semantics: path element "" means "." 50 dir = "." 51 } 52 path := filepath.Join(dir, file) 53 if err := findExecutable(path); err == nil { 54 return path, nil 55 } 56 } 57 return "", &Error{file, ErrNotFound} 58 }