go-examples/winpath/winpath.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)
*/
}