blob: d6161bcb08bbecae79f716de1b53acc29ebe3ff2 [file] [log] [blame]
Dan Willemsend2797482017-07-26 13:13:13 -07001// Copyright 2017 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
Dan Willemsencc753b72021-08-31 13:25:42 -07005//go:build aix || openbsd
Dan Willemsend2797482017-07-26 13:13:13 -07006
7package os
8
9// We query the working directory at init, to use it later to search for the
10// executable file
11// errWd will be checked later, if we need to use initWd
12var initWd, errWd = Getwd()
13
14func executable() (string, error) {
15 var exePath string
16 if len(Args) == 0 || Args[0] == "" {
17 return "", ErrNotExist
18 }
19 if IsPathSeparator(Args[0][0]) {
20 // Args[0] is an absolute path, so it is the executable.
21 // Note that we only need to worry about Unix paths here.
22 exePath = Args[0]
23 } else {
24 for i := 1; i < len(Args[0]); i++ {
25 if IsPathSeparator(Args[0][i]) {
26 // Args[0] is a relative path: prepend the
27 // initial working directory.
28 if errWd != nil {
29 return "", errWd
30 }
31 exePath = initWd + string(PathSeparator) + Args[0]
32 break
33 }
34 }
35 }
36 if exePath != "" {
37 if err := isExecutable(exePath); err != nil {
38 return "", err
39 }
40 return exePath, nil
41 }
42 // Search for executable in $PATH.
43 for _, dir := range splitPathList(Getenv("PATH")) {
44 if len(dir) == 0 {
45 dir = "."
46 }
47 if !IsPathSeparator(dir[0]) {
48 if errWd != nil {
49 return "", errWd
50 }
51 dir = initWd + string(PathSeparator) + dir
52 }
53 exePath = dir + string(PathSeparator) + Args[0]
54 switch isExecutable(exePath) {
55 case nil:
56 return exePath, nil
57 case ErrPermission:
58 return "", ErrPermission
59 }
60 }
61 return "", ErrNotExist
62}
63
64// isExecutable returns an error if a given file is not an executable.
65func isExecutable(path string) error {
66 stat, err := Stat(path)
67 if err != nil {
68 return err
69 }
70 mode := stat.Mode()
71 if !mode.IsRegular() {
72 return ErrPermission
73 }
74 if (mode & 0111) == 0 {
75 return ErrPermission
76 }
77 return nil
78}
79
80// splitPathList splits a path list.
81// This is based on genSplit from strings/strings.go
82func splitPathList(pathList string) []string {
83 if pathList == "" {
84 return nil
85 }
86 n := 1
87 for i := 0; i < len(pathList); i++ {
88 if pathList[i] == PathListSeparator {
89 n++
90 }
91 }
92 start := 0
93 a := make([]string, n)
94 na := 0
95 for i := 0; i+1 <= len(pathList) && na+1 < n; i++ {
96 if pathList[i] == PathListSeparator {
97 a[na] = pathList[start:i]
98 na++
99 start = i + 1
100 }
101 }
102 a[na] = pathList[start:]
103 return a[:na+1]
104}