1// Package install provide installation functions of command completion. 2package install 3 4import ( 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 "os/user" 10 "path/filepath" 11 "runtime" 12 "strings" 13 14 "github.com/hashicorp/go-multierror" 15) 16 17var binPath string 18 19func init() { 20 binPath, _ = getBinaryPath() 21} 22 23func Run(name string, uninstall, yes bool, out io.Writer, in io.Reader) { 24 action := "install" 25 if uninstall { 26 action = "uninstall" 27 } 28 if !yes { 29 fmt.Fprintf(out, "%s completion for %s? ", action, name) 30 var answer string 31 fmt.Fscanln(in, &answer) 32 switch strings.ToLower(answer) { 33 case "y", "yes": 34 default: 35 fmt.Fprintf(out, "Cancelling...\n") 36 return 37 } 38 } 39 fmt.Fprintf(out, action+"ing...\n") 40 41 var err error 42 if uninstall { 43 err = Uninstall(name) 44 } else { 45 err = Install(name) 46 } 47 if err != nil { 48 fmt.Fprintf(out, "%s failed: %s\n", action, err) 49 os.Exit(1) 50 } 51} 52 53type installer interface { 54 IsInstalled(cmd string) bool 55 Install(cmd string) error 56 Uninstall(cmd string) error 57} 58 59// Install complete command given: 60// cmd: is the command name 61func Install(cmd string) (err error) { 62 is := installers() 63 if len(is) == 0 { 64 return errors.New("Did not find any shells to install") 65 } 66 67 for _, i := range is { 68 errI := i.Install(cmd) 69 if errI != nil { 70 err = multierror.Append(err, errI) 71 } 72 } 73 74 return err 75} 76 77// IsInstalled returns true if the completion 78// for the given cmd is installed. 79func IsInstalled(cmd string) bool { 80 for _, i := range installers() { 81 installed := i.IsInstalled(cmd) 82 if installed { 83 return true 84 } 85 } 86 87 return false 88} 89 90// Uninstall complete command given: 91// cmd: is the command name 92func Uninstall(cmd string) (err error) { 93 is := installers() 94 if len(is) == 0 { 95 return errors.New("Did not find any shells to uninstall") 96 } 97 98 for _, i := range is { 99 errI := i.Uninstall(cmd) 100 if errI != nil { 101 err = multierror.Append(err, errI) 102 } 103 } 104 105 return err 106} 107 108func installers() (i []installer) { 109 // The list of bash config files candidates where it is 110 // possible to install the completion command. 111 var bashConfFiles []string 112 switch runtime.GOOS { 113 case "darwin": 114 bashConfFiles = []string{".bash_profile"} 115 default: 116 bashConfFiles = []string{".bashrc", ".bash_profile", ".bash_login", ".profile"} 117 } 118 for _, rc := range bashConfFiles { 119 if f := rcFile(rc); f != "" { 120 i = append(i, bash{f}) 121 break 122 } 123 } 124 if f := rcFile(".zshrc"); f != "" { 125 i = append(i, zsh{f}) 126 } 127 if d := fishConfigDir(); d != "" { 128 i = append(i, fish{d}) 129 } 130 return 131} 132 133func fishConfigDir() string { 134 configDir := filepath.Join(getConfigHomePath(), "fish") 135 if configDir == "" { 136 return "" 137 } 138 if info, err := os.Stat(configDir); err != nil || !info.IsDir() { 139 return "" 140 } 141 return configDir 142} 143 144func getConfigHomePath() string { 145 u, err := user.Current() 146 if err != nil { 147 return "" 148 } 149 150 configHome := os.Getenv("XDG_CONFIG_HOME") 151 if configHome == "" { 152 return filepath.Join(u.HomeDir, ".config") 153 } 154 return configHome 155} 156 157func getBinaryPath() (string, error) { 158 bin, err := os.Executable() 159 if err != nil { 160 return "", err 161 } 162 return filepath.Abs(bin) 163} 164 165func rcFile(name string) string { 166 u, err := user.Current() 167 if err != nil { 168 return "" 169 } 170 path := filepath.Join(u.HomeDir, name) 171 if _, err := os.Stat(path); err != nil { 172 return "" 173 } 174 return path 175} 176