term_unix.go

 1//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
 2// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 3
 4package term
 5
 6import (
 7	"golang.org/x/sys/unix"
 8)
 9
10type state struct {
11	unix.Termios
12}
13
14func isTerminal(fd uintptr) bool {
15	_, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
16	return err == nil
17}
18
19func makeRaw(fd uintptr) (*State, error) {
20	termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
21	if err != nil {
22		return nil, err
23	}
24
25	oldState := State{state{Termios: *termios}}
26
27	// This attempts to replicate the behaviour documented for cfmakeraw in
28	// the termios(3) manpage.
29	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
30	termios.Oflag &^= unix.OPOST
31	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
32	termios.Cflag &^= unix.CSIZE | unix.PARENB
33	termios.Cflag |= unix.CS8
34	termios.Cc[unix.VMIN] = 1
35	termios.Cc[unix.VTIME] = 0
36	if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
37		return nil, err
38	}
39
40	return &oldState, nil
41}
42
43func setState(fd uintptr, state *State) error {
44	var termios *unix.Termios
45	if state != nil {
46		termios = &state.Termios
47	}
48	return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
49}
50
51func getState(fd uintptr) (*State, error) {
52	termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
53	if err != nil {
54		return nil, err
55	}
56
57	return &State{state{Termios: *termios}}, nil
58}
59
60func restore(fd uintptr, state *State) error {
61	return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios)
62}
63
64func getSize(fd uintptr) (width, height int, err error) {
65	ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
66	if err != nil {
67		return 0, 0, err
68	}
69	return int(ws.Col), int(ws.Row), nil
70}
71
72// passwordReader is an io.Reader that reads from a specific file descriptor.
73type passwordReader int
74
75func (r passwordReader) Read(buf []byte) (int, error) {
76	return unix.Read(int(r), buf)
77}
78
79func readPassword(fd uintptr) ([]byte, error) {
80	termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
81	if err != nil {
82		return nil, err
83	}
84
85	newState := *termios
86	newState.Lflag &^= unix.ECHO
87	newState.Lflag |= unix.ICANON | unix.ISIG
88	newState.Iflag |= unix.ICRNL
89	if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
90		return nil, err
91	}
92
93	defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
94
95	return readPasswordLine(passwordReader(fd))
96}