No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.go 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package jsh
  2. import (
  3. "errors"
  4. "fmt"
  5. "math"
  6. "os"
  7. "os/exec"
  8. "strings"
  9. "unicode"
  10. )
  11. // FieldsN slices s into substrings after each instance of a whitespace
  12. // character (according to unicode.IsSpace) and returns a slice of those
  13. // substrings. The slice's length is guaranteed to be at most maxN, and
  14. // all leftover fields are put into the last substring.
  15. func FieldsN(s string, maxN int) []string {
  16. // First count the fields.
  17. n := 0
  18. inField := false
  19. for _, rune := range s {
  20. wasInField := inField
  21. inField = !unicode.IsSpace(rune)
  22. if inField && !wasInField {
  23. n++
  24. }
  25. }
  26. n = int(math.Min(float64(n), float64(maxN)))
  27. // Now create them.
  28. a := make([]string, n)
  29. na := 0
  30. fieldStart := -1 // Set to -1 when looking for start of field.
  31. for i, rune := range s {
  32. if unicode.IsSpace(rune) && na+1 < maxN {
  33. if fieldStart >= 0 {
  34. a[na] = s[fieldStart:i]
  35. na++
  36. fieldStart = -1
  37. }
  38. } else if fieldStart == -1 {
  39. fieldStart = i
  40. }
  41. }
  42. if fieldStart >= 0 { // Last field might end at EOF.
  43. a[na] = s[fieldStart:]
  44. }
  45. return a
  46. }
  47. // Falls back to procps-ng passing the space-separated arguments in the given
  48. // args slice. If args is nil, we default to the arguments passed to the command
  49. // line using os.Args
  50. func FallbackWithArgs(program string, args []string) *[]byte {
  51. if args == nil {
  52. args = os.Args[1:]
  53. }
  54. executable, err := findExecutable(program)
  55. if err != nil {
  56. panic(err)
  57. }
  58. out, err := exec.Command(executable, args...).Output()
  59. if err != nil {
  60. panic(err)
  61. }
  62. return &out
  63. }
  64. func Fallback(program string) *[]byte {
  65. return FallbackWithArgs(program, nil)
  66. }
  67. // Finds a given executable on the system path, returning the absolute path the program if found, and an error
  68. // if it's not found
  69. func findExecutable(programName string) (string, error) {
  70. // First, find our binary location
  71. binLocWithName, err := os.Readlink("/proc/self/exe")
  72. if err != nil {
  73. return "", err
  74. }
  75. // Get our binary name and remove it from the binary path, leaving just our binary location
  76. // Also trim the extra path separator
  77. binName := os.Args[0]
  78. binLoc := strings.TrimSuffix(binLocWithName, binName)
  79. binLoc = strings.TrimSuffix(binLoc, string(os.PathSeparator))
  80. // Get the system path and split it by the system path separator
  81. pathString := os.Getenv("PATH")
  82. path := strings.Split(pathString, string(os.PathListSeparator))
  83. // Loop until we find our location on the path, so we know where to start looking after for the
  84. // next implementation of the given program name
  85. var foundPath bool
  86. var pathNum int
  87. for index, pathEl := range path {
  88. // Trim the path separator from this element, if it has it
  89. pathEl = strings.TrimSuffix(pathEl, string(os.PathSeparator))
  90. if pathEl == binLoc {
  91. foundPath = true
  92. pathNum = index
  93. break
  94. }
  95. }
  96. // If we found our path on the real path, then search the remaining path elements. Otherwise, search all elements
  97. if foundPath {
  98. return searchPath(programName, path[pathNum+1:len(path)-1])
  99. } else {
  100. return searchPath(programName, path)
  101. }
  102. }
  103. // Searches the given set of paths for a the given program, returning when it finds it or returning an error if the
  104. // program is not found
  105. func searchPath(programName string, path []string) (string, error) {
  106. // Loop through each path element, test for the existance of the file, and then return if we find it
  107. for _, pathEl := range path {
  108. fullProgram := pathEl + string(os.PathSeparator) + programName
  109. _, err := os.Stat(fullProgram)
  110. if err == nil {
  111. return fullProgram, nil
  112. }
  113. }
  114. return "", errors.New(fmt.Sprintf("Could not find program %s on the PATH: %s", programName, path))
  115. }