check / check (push) Waiting to run
term.ReadPassword reads the terminal with a raw read that returns 0 bytes at the end of input and takes that as nothing yet, so Ctrl-D at the prompt did nothing. On the Unix systems the prompt now sets the terminal as ReadPassword does and reads the line from standard input, which reports the end of input; elsewhere, such as Windows, it still uses ReadPassword. Ctrl-D with nothing typed gives an empty mnemonic, refused with status 1, and the terminal settings are put back. A test runs the tool on a pseudo-terminal (github.com/creack/pty, at a commit past v1.1.24, whose Open on Linux can return another pty's terminal) and types Ctrl-D at the prompt. Model: opus-5-5
49 lines
1.3 KiB
Go
49 lines
1.3 KiB
Go
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
|
|
|
package mnemonic
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// readWithoutEcho reads one line from the terminal on standard input
|
|
// with echo turned off. The line ends at a newline or at the end of
|
|
// input (Ctrl-D). The terminal is set as term.ReadPassword sets it;
|
|
// ReadPassword itself is not used here because it never sees the end
|
|
// of input and goes on waiting.
|
|
func readWithoutEcho() (string, error) {
|
|
fd := int(os.Stdin.Fd())
|
|
|
|
settings, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
if err != nil {
|
|
return "", fmt.Errorf("reading the terminal settings: %w", err)
|
|
}
|
|
|
|
hidden := *settings
|
|
hidden.Lflag &^= unix.ECHO
|
|
hidden.Lflag |= unix.ICANON | unix.ISIG
|
|
hidden.Iflag |= unix.ICRNL
|
|
|
|
err = unix.IoctlSetTermios(fd, ioctlWriteTermios, &hidden)
|
|
if err != nil {
|
|
return "", fmt.Errorf("turning echo off: %w", err)
|
|
}
|
|
|
|
defer func() { _ = unix.IoctlSetTermios(fd, ioctlWriteTermios, settings) }()
|
|
|
|
// With ICANON set, a read from the terminal returns at most one
|
|
// line, so the buffer takes nothing past the newline.
|
|
typed, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
|
if errors.Is(err, io.EOF) {
|
|
return typed, nil
|
|
}
|
|
|
|
return typed, err
|
|
}
|