blob: b9da29744b7ad6bf7fc5a64ee5b88a350e7d1d36 [file] [log] [blame]
David K. Bainbridgebd6b2882021-08-26 13:31:02 +00001// 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
5package term
6
7import (
8 "io"
9 "syscall"
10
11 "golang.org/x/sys/unix"
12)
13
14// State contains the state of a terminal.
15type state struct {
16 termios unix.Termios
17}
18
19func isTerminal(fd int) bool {
20 _, err := unix.IoctlGetTermio(fd, unix.TCGETA)
21 return err == nil
22}
23
24func readPassword(fd int) ([]byte, error) {
25 // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
26 val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
27 if err != nil {
28 return nil, err
29 }
30 oldState := *val
31
32 newState := oldState
33 newState.Lflag &^= syscall.ECHO
34 newState.Lflag |= syscall.ICANON | syscall.ISIG
35 newState.Iflag |= syscall.ICRNL
36 err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
37 if err != nil {
38 return nil, err
39 }
40
41 defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
42
43 var buf [16]byte
44 var ret []byte
45 for {
46 n, err := syscall.Read(fd, buf[:])
47 if err != nil {
48 return nil, err
49 }
50 if n == 0 {
51 if len(ret) == 0 {
52 return nil, io.EOF
53 }
54 break
55 }
56 if buf[n-1] == '\n' {
57 n--
58 }
59 ret = append(ret, buf[:n]...)
60 if n < len(buf) {
61 break
62 }
63 }
64
65 return ret, nil
66}
67
68func makeRaw(fd int) (*State, error) {
69 // see http://cr.illumos.org/~webrev/andy_js/1060/
70 termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
71 if err != nil {
72 return nil, err
73 }
74
75 oldState := State{state{termios: *termios}}
76
77 termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
78 termios.Oflag &^= unix.OPOST
79 termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
80 termios.Cflag &^= unix.CSIZE | unix.PARENB
81 termios.Cflag |= unix.CS8
82 termios.Cc[unix.VMIN] = 1
83 termios.Cc[unix.VTIME] = 0
84
85 if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
86 return nil, err
87 }
88
89 return &oldState, nil
90}
91
92func restore(fd int, oldState *State) error {
93 return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
94}
95
96func getState(fd int) (*State, error) {
97 termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
98 if err != nil {
99 return nil, err
100 }
101
102 return &State{state{termios: *termios}}, nil
103}
104
105func getSize(fd int) (width, height int, err error) {
106 ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
107 if err != nil {
108 return 0, 0, err
109 }
110 return int(ws.Col), int(ws.Row), nil
111}