|
|
@ -3,12 +3,10 @@ package main |
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
"io/ioutil" |
|
|
|
"math/rand" |
|
|
|
"os" |
|
|
|
"path/filepath" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
) |
|
|
|
|
|
|
|
// ppsep is used as the replacement for slashes in path
|
|
|
@ -25,7 +23,8 @@ import ( |
|
|
|
const ppsep = "-" |
|
|
|
|
|
|
|
func usage() { |
|
|
|
fmt.Fprintf(os.Stderr, "Usage: envpath show|add|append|remove <path>\n") |
|
|
|
//fmt.Fprintf(os.Stderr, "Usage: envpath show|add|append|remove <path>\n")
|
|
|
|
fmt.Fprintf(os.Stderr, "Usage: envpath show|add|remove <path>\n") |
|
|
|
} |
|
|
|
|
|
|
|
func main() { |
|
|
@ -38,11 +37,7 @@ func main() { |
|
|
|
} |
|
|
|
action := os.Args[1] |
|
|
|
|
|
|
|
paths, err := Paths() |
|
|
|
if nil != err { |
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", err) |
|
|
|
os.Exit(2) |
|
|
|
} |
|
|
|
paths := Paths() |
|
|
|
|
|
|
|
if "show" == action { |
|
|
|
if len(os.Args) > 2 { |
|
|
@ -79,16 +74,18 @@ func main() { |
|
|
|
default: |
|
|
|
usage() |
|
|
|
os.Exit(1) |
|
|
|
case "append": |
|
|
|
newpath, _, err := addPath(pathentry, appendOrder) |
|
|
|
if nil != err { |
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", err) |
|
|
|
return |
|
|
|
} |
|
|
|
fmt.Println("New sessions will have " + pathentry + " in their PATH.") |
|
|
|
fmt.Println("To update this session run\n") |
|
|
|
//fmt.Println("\tsource", pathfile)
|
|
|
|
fmt.Printf(`%sexport PATH="$PATH:%s"%s`, "\t", newpath, "\n") |
|
|
|
/* |
|
|
|
case "append": |
|
|
|
newpath, _, err := addPath(pathentry, appendOrder) |
|
|
|
if nil != err { |
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", err) |
|
|
|
return |
|
|
|
} |
|
|
|
fmt.Println("New sessions will have " + pathentry + " in their PATH.") |
|
|
|
fmt.Println("To update this session run\n") |
|
|
|
//fmt.Println("\tsource", pathfile)
|
|
|
|
fmt.Printf(`%sexport PATH="$PATH:%s"%s`, "\t", newpath, "\n") |
|
|
|
*/ |
|
|
|
case "add": |
|
|
|
_, pathfile, err := addPath(pathentry, prependOrder) |
|
|
|
if nil != err { |
|
|
@ -99,27 +96,19 @@ func main() { |
|
|
|
fmt.Printf("\tsource %s\n\n", pathfile) |
|
|
|
//fmt.Printf(`%sexport PATH="%s:$PATH"%s`, "\t", newpath, "\n\n")
|
|
|
|
case "remove": |
|
|
|
_, err := removePath(pathentry) |
|
|
|
msg, err := removePath(pathentry) |
|
|
|
if nil != err { |
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", err) |
|
|
|
return |
|
|
|
} |
|
|
|
fmt.Println(msg) |
|
|
|
} |
|
|
|
|
|
|
|
// TODO support both of these usages:
|
|
|
|
// 1. export PATH="$(envpath append /opt/whatever/bin)"
|
|
|
|
// 2. envpath append /opt/whatever/bin
|
|
|
|
// export PATH="$PATH:/opt/whatever/bin"
|
|
|
|
} |
|
|
|
|
|
|
|
// Paths returns the slice of PATHs from the Environment
|
|
|
|
func Paths() ([]string, error) { |
|
|
|
func Paths() []string { |
|
|
|
// ":" on *nix
|
|
|
|
return strings.Split(os.Getenv("PATH"), string(os.PathListSeparator)), nil |
|
|
|
} |
|
|
|
|
|
|
|
func init() { |
|
|
|
rand.Seed(time.Now().UnixNano()) |
|
|
|
return strings.Split(os.Getenv("PATH"), string(os.PathListSeparator)) |
|
|
|
} |
|
|
|
|
|
|
|
type setOrder bool |
|
|
@ -168,8 +157,12 @@ func addPath(oldpathentry string, order setOrder) (string, string, error) { |
|
|
|
return "", "", err |
|
|
|
} |
|
|
|
|
|
|
|
if err := ensureNotInPath(home, pathentry); nil != err { |
|
|
|
return "", "", err |
|
|
|
if index, ok := isInPath(home, pathentry); ok { |
|
|
|
return "", "", fmt.Errorf( |
|
|
|
"%q is in your PATH at position %d and must be removed manually to re-order\n", |
|
|
|
pathentry, |
|
|
|
index, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// ex: 100-opt»foo»bin.sh
|
|
|
@ -189,13 +182,77 @@ func addPath(oldpathentry string, order setOrder) (string, string, error) { |
|
|
|
return pathentry, fullname, nil |
|
|
|
} |
|
|
|
|
|
|
|
// TODO don't check in parser before add/append functions actually run
|
|
|
|
func ensureNotInPath(home, pathentry string) error { |
|
|
|
paths, err := Paths() |
|
|
|
func removePath(oldpathentry string) (string, error) { |
|
|
|
home, err := os.UserHomeDir() |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
home = filepath.ToSlash(home) |
|
|
|
|
|
|
|
pathentry, fname, err := normalizeEntryAndFile(home, oldpathentry) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
envpathd := filepath.Join(home, ".config/envpath/path.d") |
|
|
|
err = os.MkdirAll(envpathd, 0755) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
nodes, err := ioutil.ReadDir(envpathd) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
var fullname string |
|
|
|
for i := range nodes { |
|
|
|
node := nodes[i] |
|
|
|
// 000-foo-bin.sh vs foo-bin.sh
|
|
|
|
if strings.HasSuffix(node.Name(), "-"+fname) { |
|
|
|
if len(strings.Split(node.Name(), "-"))-1 == len(strings.Split(fname, "-")) { |
|
|
|
fullname = node.Name() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
paths := Paths() |
|
|
|
index, exists := isInPath(home, pathentry) |
|
|
|
|
|
|
|
if "" == fullname { |
|
|
|
if exists { |
|
|
|
return "", fmt.Errorf("%q is in your PATH, but is NOT managed by envpath", pathentry) |
|
|
|
} |
|
|
|
return "", fmt.Errorf("%q is NOT in your PATH, and NOT managed by envpath", pathentry) |
|
|
|
} |
|
|
|
|
|
|
|
err = os.Remove(filepath.Join(envpathd, fullname)) |
|
|
|
if nil != err { |
|
|
|
return err |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
if !exists { |
|
|
|
return fmt.Sprintf("Removed %s", filepath.Join(envpathd, fullname)), nil |
|
|
|
} |
|
|
|
|
|
|
|
newpaths := []string{} |
|
|
|
for i := range paths { |
|
|
|
if i == index { |
|
|
|
continue |
|
|
|
} |
|
|
|
newpaths = append(newpaths, paths[i]) |
|
|
|
} |
|
|
|
return fmt.Sprintf( |
|
|
|
"Removed %s. To update the current shell re-export the new PATH:\n\n"+ |
|
|
|
"\texport PATH=%q\n", |
|
|
|
fullname, |
|
|
|
strings.Join(newpaths, ":"), |
|
|
|
), nil |
|
|
|
} |
|
|
|
|
|
|
|
func isInPath(home, pathentry string) (int, bool) { |
|
|
|
paths := Paths() |
|
|
|
|
|
|
|
index := -1 |
|
|
|
for i := range paths { |
|
|
|
entry, _ := normalizePathEntry(home, paths[i]) |
|
|
@ -205,15 +262,9 @@ func ensureNotInPath(home, pathentry string) error { |
|
|
|
} |
|
|
|
} |
|
|
|
if index >= 0 { |
|
|
|
fmt.Fprintf( |
|
|
|
os.Stderr, |
|
|
|
"%q is in your PATH at position %d and must be removed manually to re-order\n", |
|
|
|
pathentry, |
|
|
|
index, |
|
|
|
) |
|
|
|
os.Exit(3) |
|
|
|
return index, true |
|
|
|
} |
|
|
|
return nil |
|
|
|
return -1, false |
|
|
|
} |
|
|
|
|
|
|
|
func normalizeEntryAndFile(home, pathentry string) (string, string, error) { |
|
|
@ -321,59 +372,3 @@ func getOrder(nodes []os.FileInfo, pathentry, fname, envpathd string, fn sorter) |
|
|
|
|
|
|
|
return priority, nil |
|
|
|
} |
|
|
|
|
|
|
|
func removePath(oldpathentry string) (string, error) { |
|
|
|
// TODO Show current PATH sans this item
|
|
|
|
/* |
|
|
|
oldpaths := paths |
|
|
|
paths = []string{} |
|
|
|
for i := range oldpaths { |
|
|
|
if i != index { |
|
|
|
paths = append(paths, oldpaths[i]) |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
home, err := os.UserHomeDir() |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
home = filepath.ToSlash(home) |
|
|
|
|
|
|
|
pathentry, fname, err := normalizeEntryAndFile(home, oldpathentry) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
envpathd := filepath.Join(home, ".config/envpath/path.d") |
|
|
|
err = os.MkdirAll(envpathd, 0755) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
err = ensureNotInPath(home, pathentry) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
nodes, err := ioutil.ReadDir(envpathd) |
|
|
|
if nil != err { |
|
|
|
return "", err |
|
|
|
} |
|
|
|
|
|
|
|
// TODO rename getOrder to getNext
|
|
|
|
_, err = getOrder(nodes, pathentry, fname, envpathd, sortForward) |
|
|
|
if nil == err { |
|
|
|
fmt.Fprintf(os.Stderr, "%q is not managed by envpath.\n", pathentry) |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
if index < 0 { |
|
|
|
fmt.Fprintf(os.Stderr, "%q is NOT in PATH.\n", pathentry) |
|
|
|
os.Exit(3) |
|
|
|
} |
|
|
|
*/ |
|
|
|
return "", nil |
|
|
|
} |
|
|
|