| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build aix || fuchsia || openbsd |
| |
| package os |
| |
| // We query the working directory at init, to use it later to search for the |
| // executable file |
| // errWd will be checked later, if we need to use initWd |
| var initWd, errWd = Getwd() |
| |
| func executable() (string, error) { |
| var exePath string |
| if len(Args) == 0 || Args[0] == "" { |
| return "", ErrNotExist |
| } |
| if IsPathSeparator(Args[0][0]) { |
| // Args[0] is an absolute path, so it is the executable. |
| // Note that we only need to worry about Unix paths here. |
| exePath = Args[0] |
| } else { |
| for i := 1; i < len(Args[0]); i++ { |
| if IsPathSeparator(Args[0][i]) { |
| // Args[0] is a relative path: prepend the |
| // initial working directory. |
| if errWd != nil { |
| return "", errWd |
| } |
| exePath = initWd + string(PathSeparator) + Args[0] |
| break |
| } |
| } |
| } |
| if exePath != "" { |
| if err := isExecutable(exePath); err != nil { |
| return "", err |
| } |
| return exePath, nil |
| } |
| // Search for executable in $PATH. |
| for _, dir := range splitPathList(Getenv("PATH")) { |
| if len(dir) == 0 { |
| dir = "." |
| } |
| if !IsPathSeparator(dir[0]) { |
| if errWd != nil { |
| return "", errWd |
| } |
| dir = initWd + string(PathSeparator) + dir |
| } |
| exePath = dir + string(PathSeparator) + Args[0] |
| switch isExecutable(exePath) { |
| case nil: |
| return exePath, nil |
| case ErrPermission: |
| return "", ErrPermission |
| } |
| } |
| return "", ErrNotExist |
| } |
| |
| // isExecutable returns an error if a given file is not an executable. |
| func isExecutable(path string) error { |
| stat, err := Stat(path) |
| if err != nil { |
| return err |
| } |
| mode := stat.Mode() |
| if !mode.IsRegular() { |
| return ErrPermission |
| } |
| if (mode & 0111) == 0 { |
| return ErrPermission |
| } |
| return nil |
| } |
| |
| // splitPathList splits a path list. |
| // This is based on genSplit from strings/strings.go |
| func splitPathList(pathList string) []string { |
| if pathList == "" { |
| return nil |
| } |
| n := 1 |
| for i := 0; i < len(pathList); i++ { |
| if pathList[i] == PathListSeparator { |
| n++ |
| } |
| } |
| start := 0 |
| a := make([]string, n) |
| na := 0 |
| for i := 0; i+1 <= len(pathList) && na+1 < n; i++ { |
| if pathList[i] == PathListSeparator { |
| a[na] = pathList[start:i] |
| na++ |
| start = i + 1 |
| } |
| } |
| a[na] = pathList[start:] |
| return a[:na+1] |
| } |