Browse Source

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.
Fredric Silberberg 5 years ago
parent
commit
f21df51bd2
4 changed files with 71 additions and 7 deletions
  1. 1
    1
      free/main.go
  2. 3
    3
      free/main_test.go
  3. 2
    2
      ps/main.go
  4. 65
    1
      utils.go

+ 1
- 1
free/main.go View File

@@ -95,7 +95,7 @@ func main() {
95 95
 	flag.Parse()
96 96
 
97 97
 	if !*jsonModePtr {
98
-		fmt.Printf("%s", jsh.Fallback("/usr/bin/free"))
98
+		fmt.Printf("%s", jsh.Fallback("free"))
99 99
 	} else {
100 100
 		runJsonMode()
101 101
 	}

+ 3
- 3
free/main_test.go View File

@@ -2,8 +2,8 @@ package main
2 2
 
3 3
 import (
4 4
 	"jsh"
5
-	"testing"
6 5
 	"reflect"
6
+	"testing"
7 7
 )
8 8
 
9 9
 func testUnitError(expected jsh.Unit, t *testing.T) {
@@ -45,14 +45,14 @@ func TestConvertBadUnit(t *testing.T) {
45 45
 
46 46
 func TestParseGoodLine(t *testing.T) {
47 47
 	goodLine := "MemTotal:       16370344 kB"
48
-	expectedStat := jsh.MemStat { 16370344, jsh.KB }
48
+	expectedStat := jsh.MemStat{16370344, jsh.KB}
49 49
 	expectedKey := "MemTotal"
50 50
 	key, stat, err := ParseLine(goodLine)
51 51
 	if err != nil {
52 52
 		t.Error(err)
53 53
 	}
54 54
 	if key != expectedKey {
55
-		t.Errorf("Expected key %s, got key %s", expectedKey, key) 
55
+		t.Errorf("Expected key %s, got key %s", expectedKey, key)
56 56
 	}
57 57
 	if !reflect.DeepEqual(expectedStat, stat) {
58 58
 		t.Error("Expected stat ", expectedStat, " got ", stat)

+ 2
- 2
ps/main.go View File

@@ -25,7 +25,7 @@ func PsOutputToProcesses(out string) *[]jsh.Process {
25 25
 
26 26
 func runJsonMode() {
27 27
 	// Run procps-ng "ps" with full output
28
-	psOut := string(*jsh.FallbackWithArgs("/usr/bin/ps", []string{"auxww"}))
28
+	psOut := string(*jsh.FallbackWithArgs("ps", []string{"auxww"}))
29 29
 	processesPtr := PsOutputToProcesses(psOut)
30 30
 	frame := jsh.JshFrame{*processesPtr, []string{}}
31 31
 
@@ -43,7 +43,7 @@ func main() {
43 43
 	flag.Parse()
44 44
 
45 45
 	if !*jsonModePtr {
46
-		fmt.Printf("%s", jsh.Fallback("/usr/bin/ps"))
46
+		fmt.Printf("%s", jsh.Fallback("ps"))
47 47
 	} else {
48 48
 		runJsonMode()
49 49
 	}

+ 65
- 1
utils.go View File

@@ -1,9 +1,12 @@
1 1
 package jsh
2 2
 
3 3
 import (
4
+	"errors"
5
+	"fmt"
4 6
 	"math"
5 7
 	"os"
6 8
 	"os/exec"
9
+	"strings"
7 10
 	"unicode"
8 11
 )
9 12
 
@@ -52,7 +55,13 @@ func FallbackWithArgs(program string, args []string) *[]byte {
52 55
 	if args == nil {
53 56
 		args = os.Args[1:]
54 57
 	}
55
-	out, err := exec.Command(program, args...).Output()
58
+	executable, err := findExecutable(program)
59
+
60
+	if err != nil {
61
+		panic(err)
62
+	}
63
+
64
+	out, err := exec.Command(executable, args...).Output()
56 65
 	if err != nil {
57 66
 		panic(err)
58 67
 	}
@@ -62,3 +71,58 @@ func FallbackWithArgs(program string, args []string) *[]byte {
62 71
 func Fallback(program string) *[]byte {
63 72
 	return FallbackWithArgs(program, nil)
64 73
 }
74
+
75
+// Finds a given executable on the system path, returning the absolute path the program if found, and an error
76
+// if it's not found
77
+func findExecutable(programName string) (string, error) {
78
+	// First, find our binary location
79
+	binLocWithName, err := os.Readlink("/proc/self/exe")
80
+	if err != nil {
81
+		return "", err
82
+	}
83
+	// Get our binary name and remove it from the binary path, leaving just our binary location
84
+	// Also trim the extra path separator
85
+	binName := os.Args[0]
86
+	binLoc := strings.TrimSuffix(binLocWithName, binName)
87
+	binLoc = strings.TrimSuffix(binLoc, string(os.PathSeparator))
88
+
89
+	// Get the system path and split it by the system path separator
90
+	pathString := os.Getenv("PATH")
91
+	path := strings.Split(pathString, string(os.PathListSeparator))
92
+
93
+	// Loop until we find our location on the path, so we know where to start looking after for the
94
+	// next implementation of the given program name
95
+	var foundPath bool
96
+	var pathNum int
97
+	for index, pathEl := range path {
98
+		// Trim the path separator from this element, if it has it
99
+		pathEl = strings.TrimSuffix(pathEl, string(os.PathSeparator))
100
+		if pathEl == binLoc {
101
+			foundPath = true
102
+			pathNum = index
103
+			break
104
+		}
105
+	}
106
+
107
+	// If we found our path on the real path, then search the remaining path elements. Otherwise, search all elements
108
+	if foundPath {
109
+		return searchPath(programName, path[pathNum+1:len(path)-1])
110
+	} else {
111
+		return searchPath(programName, path)
112
+	}
113
+}
114
+
115
+// Searches the given set of paths for a the given program, returing when it finds it or returning an error if the
116
+// program is not found
117
+func searchPath(programName string, path []string) (string, error) {
118
+	// Loop through each path element, test for the existance of the file, and then return if we find it
119
+	for _, pathEl := range path {
120
+		fullProgram := pathEl + string(os.PathSeparator) + programName
121
+		_, err := os.Stat(fullProgram)
122
+		if err == nil {
123
+			return fullProgram, nil
124
+		}
125
+	}
126
+
127
+	return "", errors.New(fmt.Sprintf("Could not find program %s on the PATH: %s", programName, path))
128
+}

Loading…
Cancel
Save