1// Copyright 2011 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 findExecutable(file string) error {
19	d, err := os.Stat(file)
20	if err != nil {
21		return err
22	}
23	if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
24		return nil
25	}
26	return fs.ErrPermission
27}
28
29// LookPath searches for an executable named file in the
30// directories named by the path environment variable.
31// If file begins with "/", "#", "./", or "../", it is tried
32// directly and the path is not consulted.
33// On success, the result is an absolute path.
34//
35// In older versions of Go, LookPath could return a path relative to the current directory.
36// As of Go 1.19, LookPath will instead return that path along with an error satisfying
37// [errors.Is](err, [ErrDot]). See the package documentation for more details.
38func LookPath(file string) (string, error) {
39	// skip the path lookup for these prefixes
40	skip := []string{"/", "#", "./", "../"}
41
42	for _, p := range skip {
43		if strings.HasPrefix(file, p) {
44			err := findExecutable(file)
45			if err == nil {
46				return file, nil
47			}
48			return "", &Error{file, err}
49		}
50	}
51
52	path := os.Getenv("path")
53	for _, dir := range filepath.SplitList(path) {
54		path := filepath.Join(dir, file)
55		if err := findExecutable(path); err == nil {
56			if !filepath.IsAbs(path) {
57				if execerrdot.Value() != "0" {
58					return path, &Error{file, ErrDot}
59				}
60				execerrdot.IncNonDefault()
61			}
62			return path, nil
63		}
64	}
65	return "", &Error{file, ErrNotFound}
66}
67
68// lookExtensions is a no-op on non-Windows platforms, since
69// they do not restrict executables to specific extensions.
70func lookExtensions(path, dir string) (string, error) {
71	return path, nil
72}
73