Parse global options
This commit is contained in:
parent
10b74d0703
commit
77f7ce82c0
1
go.mod
1
go.mod
@ -10,6 +10,7 @@ require (
|
|||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
3
go.sum
3
go.sum
@ -21,7 +21,10 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJ
|
|||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
|||||||
@ -201,7 +201,7 @@ global_options_spec = partial('''\
|
|||||||
An address for the kitty instance to control. Corresponds to the address given
|
An address for the kitty instance to control. Corresponds to the address given
|
||||||
to the kitty instance via the :option:`kitty --listen-on` option or the
|
to the kitty instance via the :option:`kitty --listen-on` option or the
|
||||||
:opt:`listen_on` setting in :file:`kitty.conf`. If not specified, the
|
:opt:`listen_on` setting in :file:`kitty.conf`. If not specified, the
|
||||||
environment variable :env:`KITTY_LISTEN_ON` is checked. If that is also not
|
environment variable :envvar:`KITTY_LISTEN_ON` is checked. If that is also not
|
||||||
found, messages are sent to the controlling terminal for this process, i.e.
|
found, messages are sent to the controlling terminal for this process, i.e.
|
||||||
they will only work if this process is run within a kitty window.
|
they will only work if this process is run within a kitty window.
|
||||||
|
|
||||||
|
|||||||
@ -30,17 +30,17 @@ func GetTTYSize() (*unix.Winsize, error) {
|
|||||||
return nil, fmt.Errorf("STDOUT is not a TTY")
|
return nil, fmt.Errorf("STDOUT is not a TTY")
|
||||||
}
|
}
|
||||||
|
|
||||||
func add_choices(cmd *cobra.Command, flags *pflag.FlagSet, choices []string, name string, usage string) {
|
func add_choices(cmd *cobra.Command, flags *pflag.FlagSet, choices []string, name string, usage string) *string {
|
||||||
flags.String(name, choices[0], usage)
|
|
||||||
cmd.Annotations["choices-"+name] = strings.Join(choices, "\000")
|
cmd.Annotations["choices-"+name] = strings.Join(choices, "\000")
|
||||||
|
return flags.String(name, choices[0], usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Choices(cmd *cobra.Command, name string, usage string, choices ...string) {
|
func Choices(cmd *cobra.Command, name string, usage string, choices ...string) *string {
|
||||||
add_choices(cmd, cmd.Flags(), choices, name, usage)
|
return add_choices(cmd, cmd.Flags(), choices, name, usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PersistentChoices(cmd *cobra.Command, name string, usage string, choices ...string) {
|
func PersistentChoices(cmd *cobra.Command, name string, usage string, choices ...string) *string {
|
||||||
add_choices(cmd, cmd.PersistentFlags(), choices, name, usage)
|
return add_choices(cmd, cmd.PersistentFlags(), choices, name, usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func key_in_slice(vals []string, key string) bool {
|
func key_in_slice(vals []string, key string) bool {
|
||||||
@ -333,43 +333,53 @@ func show_usage(cmd *cobra.Command) error {
|
|||||||
}
|
}
|
||||||
output_text := output.String()
|
output_text := output.String()
|
||||||
// fmt.Printf("%#v\n", output_text)
|
// fmt.Printf("%#v\n", output_text)
|
||||||
if stdout_is_terminal && cmd.Annotations["allow-pager"] != "no" {
|
if cmd.Annotations["use-pager-for-usage"] == "true" && stdout_is_terminal && cmd.Annotations["allow-pager"] != "no" {
|
||||||
pager := exec.Command(kitty.DefaultPager[0], kitty.DefaultPager[1:]...)
|
pager := exec.Command(kitty.DefaultPager[0], kitty.DefaultPager[1:]...)
|
||||||
pager.Stdin = strings.NewReader(output_text)
|
pager.Stdin = strings.NewReader(output_text)
|
||||||
pager.Stdout = os.Stdout
|
pager.Stdout = os.Stdout
|
||||||
pager.Stderr = os.Stderr
|
pager.Stderr = os.Stderr
|
||||||
pager.Run()
|
pager.Run()
|
||||||
} else {
|
} else {
|
||||||
print(output_text)
|
cmd.OutOrStdout().Write([]byte(output_text))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateCommand(cmd *cobra.Command) *cobra.Command {
|
func CreateCommand(cmd *cobra.Command) *cobra.Command {
|
||||||
cmd.Annotations = make(map[string]string)
|
cmd.Annotations = make(map[string]string)
|
||||||
if cmd.Run == nil {
|
if cmd.Run == nil && cmd.RunE == nil {
|
||||||
cmd.Run = SubCommandRequired
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(cmd.Commands()) > 0 {
|
||||||
|
return fmt.Errorf("%s. Use %s -h to get a list of available sub-commands", err_fmt("No sub-command specified"), full_command_name(cmd))
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.SilenceErrors = true
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
orig_pre_run := cmd.PersistentPreRunE
|
||||||
|
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := ValidateChoices(cmd, args)
|
||||||
|
if err != nil || orig_pre_run == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return orig_pre_run(cmd, args)
|
||||||
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().SortFlags = false
|
cmd.PersistentFlags().SortFlags = false
|
||||||
cmd.Flags().SortFlags = false
|
cmd.Flags().SortFlags = false
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func UsageAndError(cmd *cobra.Command, err string) {
|
func show_help(cmd *cobra.Command, args []string) {
|
||||||
cmd.Annotations["usage-suffix"] = err
|
cmd.Annotations["use-pager-for-usage"] = "true"
|
||||||
cmd.Usage()
|
show_usage(cmd)
|
||||||
}
|
|
||||||
|
|
||||||
func SubCommandRequired(cmd *cobra.Command, args []string) {
|
|
||||||
UsageAndError(cmd, "No command specified: "+exe_fmt(full_command_name(cmd))+err_fmt(" add-a-command-here"))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(root *cobra.Command) {
|
func Init(root *cobra.Command) {
|
||||||
stdout_is_terminal = isatty.IsTerminal(os.Stdout.Fd())
|
stdout_is_terminal = isatty.IsTerminal(os.Stdout.Fd())
|
||||||
RootCmd = root
|
RootCmd = root
|
||||||
root.Version = kitty.VersionString
|
root.Version = kitty.VersionString
|
||||||
root.PersistentPreRunE = ValidateChoices
|
|
||||||
root.SetUsageFunc(show_usage)
|
root.SetUsageFunc(show_usage)
|
||||||
root.SetHelpTemplate("{{.UsageString}}")
|
root.SetHelpFunc(show_help)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,15 @@
|
|||||||
package at
|
package at
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"golang.org/x/term"
|
||||||
|
|
||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
"kitty/tools/crypto"
|
"kitty/tools/crypto"
|
||||||
@ -9,36 +17,100 @@ import (
|
|||||||
|
|
||||||
var encrypt_cmd = crypto.Encrypt_cmd
|
var encrypt_cmd = crypto.Encrypt_cmd
|
||||||
|
|
||||||
|
type GlobalOptions struct {
|
||||||
|
to, password, use_password string
|
||||||
|
to_from_env bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var global_options GlobalOptions
|
||||||
|
|
||||||
|
func get_password(password string, password_file string, password_env string, use_password string) (string, error) {
|
||||||
|
if use_password == "never" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
ans := ""
|
||||||
|
if password != "" {
|
||||||
|
ans = password
|
||||||
|
}
|
||||||
|
if ans == "" && password_file != "" {
|
||||||
|
if password_file == "-" {
|
||||||
|
if isatty.IsTerminal(os.Stdin.Fd()) {
|
||||||
|
q, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
ans = string(q)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
q, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
ans = strings.TrimRight(string(q), " \n\t")
|
||||||
|
}
|
||||||
|
ttyf, err := os.Open("/dev/tty")
|
||||||
|
if err != nil {
|
||||||
|
err = unix.Dup2(int(ttyf.Fd()), int(os.Stdin.Fd()))
|
||||||
|
ttyf.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
q, err := ioutil.ReadFile(password_file)
|
||||||
|
if err != nil {
|
||||||
|
ans = strings.TrimRight(string(q), " \n\t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ans == "" && password_env != "" {
|
||||||
|
ans = os.Getenv(password_env)
|
||||||
|
}
|
||||||
|
if ans == "" && use_password == "always" {
|
||||||
|
return ans, fmt.Errorf("No password was found")
|
||||||
|
}
|
||||||
|
if len(ans) > 1024 {
|
||||||
|
return ans, fmt.Errorf("Specified password is too long")
|
||||||
|
}
|
||||||
|
return ans, nil
|
||||||
|
}
|
||||||
|
|
||||||
func EntryPoint(tool_root *cobra.Command) *cobra.Command {
|
func EntryPoint(tool_root *cobra.Command) *cobra.Command {
|
||||||
|
var to, password, password_file, password_env, use_password *string
|
||||||
var root = cli.CreateCommand(&cobra.Command{
|
var root = cli.CreateCommand(&cobra.Command{
|
||||||
Use: "@ [global options] command [command options] [command args]",
|
Use: "@ [global options] command [command options] [command args]",
|
||||||
Short: "Control kitty remotely",
|
Short: "Control kitty remotely",
|
||||||
Long: "Control kitty by sending it commands. Set the allow_remote_control option in :file:`kitty.conf` or use a password, for this to work.",
|
Long: "Control kitty by sending it commands. Set the allow_remote_control option in :file:`kitty.conf` or use a password, for this to work.",
|
||||||
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if *to == "" {
|
||||||
|
*to = os.Getenv("KITTY_LISTEN_ON")
|
||||||
|
global_options.to_from_env = true
|
||||||
|
}
|
||||||
|
global_options.to = *to
|
||||||
|
global_options.use_password = *use_password
|
||||||
|
q, err := get_password(*password, *password_file, *password_env, *use_password)
|
||||||
|
global_options.password = q
|
||||||
|
return err
|
||||||
|
},
|
||||||
})
|
})
|
||||||
root.Annotations["options_title"] = "Global options"
|
root.Annotations["options_title"] = "Global options"
|
||||||
|
|
||||||
root.PersistentFlags().String("to", "",
|
to = root.PersistentFlags().String("to", "",
|
||||||
"An address for the kitty instance to control. Corresponds to the address given"+
|
"An address for the kitty instance to control. Corresponds to the address given"+
|
||||||
" to the kitty instance via the :option:`kitty --listen-on` option or the :opt:`listen_on` setting in :file:`kitty.conf`. If not"+
|
" to the kitty instance via the :option:`kitty --listen-on` option or the :opt:`listen_on` setting in :file:`kitty.conf`. If not"+
|
||||||
" specified, the environment variable :env:`KITTY_LISTEN_ON` is checked. If that"+
|
" specified, the environment variable :envvar:`KITTY_LISTEN_ON` is checked. If that"+
|
||||||
" is also not found, messages are sent to the controlling terminal for this"+
|
" is also not found, messages are sent to the controlling terminal for this"+
|
||||||
" process, i.e. they will only work if this process is run within a kitty window.")
|
" process, i.e. they will only work if this process is run within a kitty window.")
|
||||||
|
|
||||||
root.PersistentFlags().String("password", "",
|
password = root.PersistentFlags().String("password", "",
|
||||||
"A password to use when contacting kitty. This will cause kitty to ask the user"+
|
"A password to use when contacting kitty. This will cause kitty to ask the user"+
|
||||||
" for permission to perform the specified action, unless the password has been"+
|
" for permission to perform the specified action, unless the password has been"+
|
||||||
" accepted before or is pre-configured in :file:`kitty.conf`.")
|
" accepted before or is pre-configured in :file:`kitty.conf`.")
|
||||||
|
|
||||||
root.PersistentFlags().String("password-file", "rc-pass",
|
password_file = root.PersistentFlags().String("password-file", "rc-pass",
|
||||||
"A file from which to read the password. Trailing whitespace is ignored. Relative"+
|
"A file from which to read the password. Trailing whitespace is ignored. Relative"+
|
||||||
" paths are resolved from the kitty configuration directory. Use - to read from STDIN."+
|
" paths are resolved from the kitty configuration directory. Use - to read from STDIN."+
|
||||||
" Used if no :option:`--password` is supplied. Defaults to checking for the"+
|
" Used if no :option:`--password` is supplied. Defaults to checking for the"+
|
||||||
" :file:`rc-pass` file in the kitty configuration directory.")
|
" :file:`rc-pass` file in the kitty configuration directory.")
|
||||||
|
|
||||||
root.PersistentFlags().String("password-env", "KITTY_RC_PASSWORD",
|
password_env = root.PersistentFlags().String("password-env", "KITTY_RC_PASSWORD",
|
||||||
"The name of an environment variable to read the password from."+
|
"The name of an environment variable to read the password from."+
|
||||||
" Used if no :option:`--password-file` or :option:`--password` is supplied.")
|
" Used if no :option:`--password-file` or :option:`--password` is supplied.")
|
||||||
|
|
||||||
cli.PersistentChoices(root, "use-password", "If no password is available, kitty will usually just send the remote control command without a password. This option can be used to force it to always or never use the supplied password.", "if-available", "always", "never")
|
use_password = cli.PersistentChoices(root, "use-password", "If no password is available, kitty will usually just send the remote control command without a password. This option can be used to force it to always or never use the supplied password.", "if-available", "always", "never")
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ func main() {
|
|||||||
|
|
||||||
cli.Init(root)
|
cli.Init(root)
|
||||||
if err := root.Execute(); err != nil {
|
if err := root.Execute(); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user