This repository has been archived on 2015-04-29. You can view files and clone it, but cannot push or open issues or pull requests.
jsh/utils.go
Fredric Silberberg f21df51bd2 Added the PATH searching algorithm we talked about at the last meeting with Ciaraldi. It will find the location of our program
and search the PATH for that location. If found, it will search all remaining elements for the requested program. If the location
is not found on the PATH, then it will search the entire PATH for the requested program. If found, it returns the absolute path
to the program. If not, err.
2014-09-24 23:32:11 -04:00

129 lines
3.5 KiB
Go

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, returing 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))
}