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// +build darwin dragonfly freebsd linux netbsd openbsd
6
7package exec
8
9import (
10	"errors"
11	"os"
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 os.ErrPermission
27}
28
29// LookPath searches for an executable binary named file
30// in the directories named by the PATH environment variable.
31// If file contains a slash, it is tried directly and the PATH is not consulted.
32// The result may be an absolute path or a path relative to the current directory.
33func LookPath(file string) (string, error) {
34	// NOTE(rsc): I wish we could use the Plan 9 behavior here
35	// (only bypass the path if file begins with / or ./ or ../)
36	// but that would not match all the Unix shells.
37
38	if strings.Contains(file, "/") {
39		err := findExecutable(file)
40		if err == nil {
41			return file, nil
42		}
43		return "", &Error{file, err}
44	}
45	pathenv := os.Getenv("PATH")
46	if pathenv == "" {
47		return "", &Error{file, ErrNotFound}
48	}
49	for _, dir := range strings.Split(pathenv, ":") {
50		if dir == "" {
51			// Unix shell semantics: path element "" means "."
52			dir = "."
53		}
54		path := dir + "/" + file
55		if err := findExecutable(path); err == nil {
56			return path, nil
57		}
58	}
59	return "", &Error{file, ErrNotFound}
60}
61