1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
// +build linux darwin freebsd openbsd netbsd dragonfly
// Copyright 2013-2015 Bowery, Inc.
package prompt
import (
"bufio"
"os"
"syscall"
"unsafe"
)
var unsupported = []string{"", "dumb", "cons25"}
// supportedTerminal checks if the terminal supports ansi escapes.
func supportedTerminal() bool {
term := os.Getenv("TERM")
for _, t := range unsupported {
if t == term {
return false
}
}
return true
}
// winsize contains the size for the terminal.
type winsize struct {
rows uint16
cols uint16
xpixel uint16
ypixel uint16
}
// TerminalSize retrieves the cols/rows for the terminal connected to out.
func TerminalSize(out *os.File) (int, int, error) {
ws := new(winsize)
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, out.Fd(),
uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
if err != 0 {
return 0, 0, err
}
return int(ws.cols), int(ws.rows), nil
}
// IsNotTerminal checks if an error is related to io not being a terminal.
func IsNotTerminal(err error) bool {
if err == syscall.ENOTTY {
return true
}
return false
}
// terminal contains the private fields for a Unix terminal.
type terminal struct {
supported bool
simpleReader *bufio.Reader
origMode syscall.Termios
}
// NewTerminal creates a terminal and sets it to raw input mode.
func NewTerminal() (*Terminal, error) {
term := &Terminal{
In: os.Stdin,
Out: os.Stdout,
History: make([]string, 0, 10),
histIdx: -1,
terminal: new(terminal),
}
if !supportedTerminal() {
return term, nil
}
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, term.In.Fd(),
uintptr(tcgets), uintptr(unsafe.Pointer(&term.origMode)))
if err != 0 {
if IsNotTerminal(err) {
return term, nil
}
return nil, err
}
mode := term.origMode
term.supported = true
// Set new mode flags, for reference see cfmakeraw(3).
mode.Iflag &^= (syscall.BRKINT | syscall.IGNBRK | syscall.ICRNL |
syscall.INLCR | syscall.IGNCR | syscall.ISTRIP | syscall.IXON |
syscall.PARMRK)
mode.Oflag &^= syscall.OPOST
mode.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON |
syscall.ISIG | syscall.IEXTEN)
mode.Cflag &^= (syscall.CSIZE | syscall.PARENB)
mode.Cflag |= syscall.CS8
// Set controls; min num of bytes, and timeouts.
mode.Cc[syscall.VMIN] = 1
mode.Cc[syscall.VTIME] = 0
_, _, err = syscall.Syscall(syscall.SYS_IOCTL, term.In.Fd(),
uintptr(tcsetsf), uintptr(unsafe.Pointer(&mode)))
if err != 0 {
return nil, err
}
return term, nil
}
// GetPrompt gets a line with the prefix and echos input.
func (term *Terminal) GetPrompt(prefix string) (string, error) {
if !term.supported {
return term.simplePrompt(prefix)
}
buf := NewBuffer(prefix, term.Out, true)
return term.prompt(buf, term.In)
}
// GetPassword gets a line with the prefix and doesn't echo input.
func (term *Terminal) GetPassword(prefix string) (string, error) {
if !term.supported {
return term.simplePrompt(prefix)
}
buf := NewBuffer(prefix, term.Out, false)
return term.password(buf, term.In)
}
// Close disables the terminals raw input.
func (term *Terminal) Close() error {
if term.supported {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, term.In.Fd(),
uintptr(tcsets), uintptr(unsafe.Pointer(&term.origMode)))
if err != 0 {
return err
}
}
return nil
}
|