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