mirror of
https://github.com/FiloSottile/age.git
synced 2026-01-03 19:03:57 +00:00
cmd/age: ensure TUI output goes all to the terminal
This commit is contained in:
@@ -288,12 +288,10 @@ func passphrasePromptForEncryption() (string, error) {
|
|||||||
words = append(words, randomWord())
|
words = append(words, randomWord())
|
||||||
}
|
}
|
||||||
p = strings.Join(words, "-")
|
p = strings.Join(words, "-")
|
||||||
// It's somewhat unfortunate that the prompt comes through the terminal,
|
err := printfToTerminal("using autogenerated passphrase %q", p)
|
||||||
// while the autogenerated passphrase is printed to stderr. However,
|
if err != nil {
|
||||||
// thinking about the terminal as a pinentry UI, it's better for the
|
return "", fmt.Errorf("could not print passphrase: %v", err)
|
||||||
// passphrase to stick around and be copy-pastable, than to show up in
|
}
|
||||||
// ephemeral UI.
|
|
||||||
printf("using autogenerated passphrase %q", p)
|
|
||||||
} else {
|
} else {
|
||||||
confirm, err := readSecret("Confirm passphrase:")
|
confirm, err := readSecret("Confirm passphrase:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
33
cmd/age/testdata/terminal.txt
vendored
Normal file
33
cmd/age/testdata/terminal.txt
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
[windows] skip # no pty support
|
||||||
|
|
||||||
|
# controlling terminal is used instead of stdin/stderr
|
||||||
|
pty terminal
|
||||||
|
age -p -o test.age input
|
||||||
|
! stderr .
|
||||||
|
|
||||||
|
# autogenerated passphrase is printed to terminal
|
||||||
|
pty empty
|
||||||
|
age -p -o test.age input
|
||||||
|
ptyout 'autogenerated passphrase'
|
||||||
|
! stderr .
|
||||||
|
|
||||||
|
# with no controlling terminal, stdin terminal is used
|
||||||
|
# TODO: enable once https://golang.org/issue/53601 is fixed
|
||||||
|
# and Noctty is added to testscript.
|
||||||
|
# pty -stdin terminal
|
||||||
|
# age -p -o test.age input
|
||||||
|
# ! stderr .
|
||||||
|
|
||||||
|
# no terminal causes an error
|
||||||
|
# TODO: enable once https://golang.org/issue/53601 is fixed
|
||||||
|
# and Noctty is added to testscript.
|
||||||
|
# ! age -p -o test.age input
|
||||||
|
# stderr 'standard input is not a terminal'
|
||||||
|
|
||||||
|
-- input --
|
||||||
|
test
|
||||||
|
-- terminal --
|
||||||
|
password
|
||||||
|
password
|
||||||
|
-- empty --
|
||||||
|
|
||||||
@@ -88,29 +88,33 @@ func clearLine(out io.Writer) {
|
|||||||
// withTerminal does not open a non-terminal stdin, so the caller does not need
|
// withTerminal does not open a non-terminal stdin, so the caller does not need
|
||||||
// to check stdinInUse.
|
// to check stdinInUse.
|
||||||
func withTerminal(f func(in, out *os.File) error) error {
|
func withTerminal(f func(in, out *os.File) error) error {
|
||||||
var in, out *os.File
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
var err error
|
in, err := os.OpenFile("CONIN$", os.O_RDWR, 0)
|
||||||
in, err = os.OpenFile("CONIN$", os.O_RDWR, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer in.Close()
|
defer in.Close()
|
||||||
out, err = os.OpenFile("CONOUT$", os.O_WRONLY, 0)
|
out, err := os.OpenFile("CONOUT$", os.O_WRONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
return f(in, out)
|
||||||
} else if tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0); err == nil {
|
} else if tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0); err == nil {
|
||||||
defer tty.Close()
|
defer tty.Close()
|
||||||
in, out = tty, tty
|
return f(tty, tty)
|
||||||
|
} else if term.IsTerminal(int(os.Stdin.Fd())) {
|
||||||
|
return f(os.Stdin, os.Stdin)
|
||||||
} else {
|
} else {
|
||||||
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
return fmt.Errorf("standard input is not a terminal, and /dev/tty is not available: %v", err)
|
||||||
return fmt.Errorf("standard input is not a terminal, and /dev/tty is not available: %v", err)
|
|
||||||
}
|
|
||||||
in, out = os.Stdin, os.Stderr
|
|
||||||
}
|
}
|
||||||
return f(in, out)
|
}
|
||||||
|
|
||||||
|
func printfToTerminal(format string, v ...interface{}) error {
|
||||||
|
return withTerminal(func(_, out *os.File) error {
|
||||||
|
_, err := fmt.Fprintf(out, "age: "+format, v...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// readSecret reads a value from the terminal with no echo. The prompt is ephemeral.
|
// readSecret reads a value from the terminal with no echo. The prompt is ephemeral.
|
||||||
@@ -124,7 +128,7 @@ func readSecret(prompt string) (s []byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// readSecret reads a single character from the terminal with no echo. The
|
// readCharacter reads a single character from the terminal with no echo. The
|
||||||
// prompt is ephemeral.
|
// prompt is ephemeral.
|
||||||
func readCharacter(prompt string) (c byte, err error) {
|
func readCharacter(prompt string) (c byte, err error) {
|
||||||
err = withTerminal(func(in, out *os.File) error {
|
err = withTerminal(func(in, out *os.File) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user