mirror of
https://github.com/FiloSottile/age.git
synced 2025-12-26 06:55:15 +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())
|
||||
}
|
||||
p = strings.Join(words, "-")
|
||||
// It's somewhat unfortunate that the prompt comes through the terminal,
|
||||
// while the autogenerated passphrase is printed to stderr. However,
|
||||
// thinking about the terminal as a pinentry UI, it's better for the
|
||||
// passphrase to stick around and be copy-pastable, than to show up in
|
||||
// ephemeral UI.
|
||||
printf("using autogenerated passphrase %q", p)
|
||||
err := printfToTerminal("using autogenerated passphrase %q", p)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not print passphrase: %v", err)
|
||||
}
|
||||
} else {
|
||||
confirm, err := readSecret("Confirm passphrase:")
|
||||
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
|
||||
// to check stdinInUse.
|
||||
func withTerminal(f func(in, out *os.File) error) error {
|
||||
var in, out *os.File
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err = os.OpenFile("CONOUT$", os.O_WRONLY, 0)
|
||||
out, err := os.OpenFile("CONOUT$", os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
return f(in, out)
|
||||
} else if tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0); err == nil {
|
||||
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 {
|
||||
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
return fmt.Errorf("standard input is not a terminal, and /dev/tty is not available: %v", err)
|
||||
}
|
||||
in, out = os.Stdin, os.Stderr
|
||||
return fmt.Errorf("standard input is not a terminal, and /dev/tty is not available: %v", err)
|
||||
}
|
||||
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.
|
||||
@@ -124,7 +128,7 @@ func readSecret(prompt string) (s []byte, err error) {
|
||||
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.
|
||||
func readCharacter(prompt string) (c byte, err error) {
|
||||
err = withTerminal(func(in, out *os.File) error {
|
||||
|
||||
Reference in New Issue
Block a user