//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 }