executable_path.go 2.3 KB

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