package jsh import ( "errors" "fmt" "math" "os" "os/exec" "strings" "unicode" ) // FieldsN slices s into substrings after each instance of a whitespace // character (according to unicode.IsSpace) and returns a slice of those // substrings. The slice's length is guaranteed to be at most maxN, and // all leftover fields are put into the last substring. func FieldsN(s string, maxN int) []string { // First count the fields. n := 0 inField := false for _, rune := range s { wasInField := inField inField = !unicode.IsSpace(rune) if inField && !wasInField { n++ } } n = int(math.Min(float64(n), float64(maxN))) // Now create them. a := make([]string, n) na := 0 fieldStart := -1 // Set to -1 when looking for start of field. for i, rune := range s { if unicode.IsSpace(rune) && na+1 < maxN { if fieldStart >= 0 { a[na] = s[fieldStart:i] na++ fieldStart = -1 } } else if fieldStart == -1 { fieldStart = i } } if fieldStart >= 0 { // Last field might end at EOF. a[na] = s[fieldStart:] } return a } // Falls back to procps-ng passing the space-separated arguments in the given // args slice. If args is nil, we default to the arguments passed to the command // line using os.Args func FallbackWithArgs(program string, args []string) *[]byte { if args == nil { args = os.Args[1:] } executable, err := findExecutable(program) if err != nil { panic(err) } out, err := exec.Command(executable, args...).Output() if err != nil { panic(err) } return &out } func Fallback(program string) *[]byte { return FallbackWithArgs(program, nil) } // Finds a given executable on the system path, returning the absolute path the program if found, and an error // if it's not found func findExecutable(programName string) (string, error) { // First, find our binary location binLocWithName, err := os.Readlink("/proc/self/exe") if err != nil { return "", err } // Get our binary name and remove it from the binary path, leaving just our binary location // Also trim the extra path separator binName := os.Args[0] binLoc := strings.TrimSuffix(binLocWithName, binName) binLoc = strings.TrimSuffix(binLoc, string(os.PathSeparator)) // Get the system path and split it by the system path separator pathString := os.Getenv("PATH") path := strings.Split(pathString, string(os.PathListSeparator)) // Loop until we find our location on the path, so we know where to start looking after for the // next implementation of the given program name var foundPath bool var pathNum int for index, pathEl := range path { // Trim the path separator from this element, if it has it pathEl = strings.TrimSuffix(pathEl, string(os.PathSeparator)) if pathEl == binLoc { foundPath = true pathNum = index break } } // If we found our path on the real path, then search the remaining path elements. Otherwise, search all elements if foundPath { return searchPath(programName, path[pathNum+1:len(path)-1]) } else { return searchPath(programName, path) } } // Searches the given set of paths for a the given program, returning when it finds it or returning an error if the // program is not found func searchPath(programName string, path []string) (string, error) { // Loop through each path element, test for the existance of the file, and then return if we find it for _, pathEl := range path { fullProgram := pathEl + string(os.PathSeparator) + programName _, err := os.Stat(fullProgram) if err == nil { return fullProgram, nil } } return "", errors.New(fmt.Sprintf("Could not find program %s on the PATH: %s", programName, path)) }