198 lines
4.4 KiB
Go
198 lines
4.4 KiB
Go
|
// +build windows
|
||
|
|
||
|
// We both need to
|
||
|
// * use the registry editor directly to avoid possible PATH truncation
|
||
|
// ( https://stackoverflow.com/questions/9546324/adding-directory-to-path-environment-variable-in-windows )
|
||
|
// ( https://superuser.com/questions/387619/overcoming-the-1024-character-limit-with-setx )
|
||
|
// * explicitly send WM_SETTINGCHANGE
|
||
|
// ( https://github.com/golang/go/issues/18680#issuecomment-275582179 )
|
||
|
// * also install as a service
|
||
|
// ( https://github.com/golang/sys/blob/master/windows/svc/example/install.go )
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/sys/windows/registry"
|
||
|
)
|
||
|
|
||
|
var sendmsg func()
|
||
|
|
||
|
func usage() {
|
||
|
fmt.Fprintf(os.Stderr, "Usage: winpath show|append|prepend|remove <path>\n")
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
fmt.Println("PATH:", os.Getenv("PATH"))
|
||
|
//fpath, err := exec.LookPath("reg")
|
||
|
//fmt.Println("LookPath(\"reg\"):", fpath, err)
|
||
|
|
||
|
shell := os.Getenv("SHELL")
|
||
|
if "" == shell {
|
||
|
if strings.HasSuffix(os.Getenv("COMSPEC"), "/cmd.exe") {
|
||
|
shell = "cmd"
|
||
|
}
|
||
|
}
|
||
|
fmt.Println("SHELL?", shell)
|
||
|
fmt.Println("os.PathListSeparator:", string(os.PathListSeparator))
|
||
|
// WM_SETTING_CHANGE
|
||
|
// https://gist.github.com/microo8/c1b9525efab9bb462adf9d123e855c52
|
||
|
// os.Setenv("PATH")
|
||
|
|
||
|
// TODO --system to add to the system PATH rather than the user PATH
|
||
|
if len(os.Args) < 2 {
|
||
|
usage()
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
action := os.Args[1]
|
||
|
|
||
|
paths, err := Paths()
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(2)
|
||
|
}
|
||
|
|
||
|
if "show" == action {
|
||
|
if len(os.Args) > 2 {
|
||
|
usage()
|
||
|
}
|
||
|
fmt.Println()
|
||
|
for i := range paths {
|
||
|
fmt.Println("\t" + paths[i])
|
||
|
}
|
||
|
fmt.Println()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if len(os.Args) < 3 {
|
||
|
usage()
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
pathname := os.Args[2]
|
||
|
abspath, err := filepath.Abs(pathname)
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(2)
|
||
|
}
|
||
|
if "remove" != action {
|
||
|
stat, err := os.Stat(pathname)
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(2)
|
||
|
}
|
||
|
if !stat.IsDir() {
|
||
|
fmt.Fprintf(os.Stderr, "%q is not a directory (folder)\n", pathname)
|
||
|
os.Exit(2)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
index := -1
|
||
|
for i := range paths {
|
||
|
if pathname == paths[i] {
|
||
|
index = i
|
||
|
break
|
||
|
}
|
||
|
if abspath == paths[i] {
|
||
|
index = i
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch action {
|
||
|
default:
|
||
|
usage()
|
||
|
os.Exit(1)
|
||
|
case "append":
|
||
|
if index >= 0 {
|
||
|
fmt.Fprintf(os.Stderr, "%q is already in PATH at position %d. Remove it first to re-order.\n", pathname, index)
|
||
|
os.Exit(3)
|
||
|
}
|
||
|
paths = append(paths, pathname)
|
||
|
fmt.Println("Run this to cause settings to take affect immediately:")
|
||
|
fmt.Println("\tPATH %PATH%;" + pathname)
|
||
|
case "prepend":
|
||
|
if index >= 0 {
|
||
|
fmt.Fprintf(os.Stderr, "%q is already in PATH at position %d. Remove it first to re-order.\n", pathname, index)
|
||
|
os.Exit(3)
|
||
|
}
|
||
|
paths = append([]string{pathname}, paths...)
|
||
|
fmt.Println("Run this to cause settings to take affect immediately:")
|
||
|
fmt.Println("\tPATH " + pathname + ";%PATH%")
|
||
|
case "remove":
|
||
|
if index < 0 {
|
||
|
fmt.Fprintf(os.Stderr, "%q is NOT in PATH.\n", pathname)
|
||
|
os.Exit(3)
|
||
|
}
|
||
|
oldpaths := paths
|
||
|
paths = []string{}
|
||
|
for i := range oldpaths {
|
||
|
if i != index {
|
||
|
paths = append(paths, oldpaths[i])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.SET_VALUE)
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(4)
|
||
|
}
|
||
|
defer k.Close()
|
||
|
// ";" on Windows
|
||
|
err = k.SetStringValue(`Path`, strings.Join(paths, string(os.PathListSeparator)))
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(4)
|
||
|
}
|
||
|
err = k.Close()
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(4)
|
||
|
|
||
|
}
|
||
|
|
||
|
err = os.Setenv(`PATH`, strings.Join(paths, string(os.PathListSeparator)))
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||
|
os.Exit(4)
|
||
|
}
|
||
|
if nil != sendmsg {
|
||
|
fmt.Println("Open a new Terminal for the updated PATH")
|
||
|
sendmsg()
|
||
|
} else {
|
||
|
fmt.Println("You'll need to reboot for setting to take affect")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Paths() ([]string, error) {
|
||
|
k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer k.Close()
|
||
|
|
||
|
// This is case insensitive (PATH, Path, path)
|
||
|
s, _, err := k.GetStringValue("Path")
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
// ";" on Windows
|
||
|
return strings.Split(s, string(os.PathListSeparator)), nil
|
||
|
|
||
|
/*
|
||
|
shstr := loader()
|
||
|
if "" == shstr {
|
||
|
shstr = "NO_SHELL_DETECTED"
|
||
|
}
|
||
|
if "" == s {
|
||
|
s = "NO_WINREG_PATH"
|
||
|
}
|
||
|
|
||
|
return fmt.Sprintf("%s\n%s", s, shstr)
|
||
|
*/
|
||
|
}
|