112 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			112 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | // Copyright 2019 The Go Authors. All rights reserved.
 | ||
|  | // Use of this source code is governed by a BSD-style
 | ||
|  | // license that can be found in the LICENSE file.
 | ||
|  | 
 | ||
|  | package term | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"io" | ||
|  | 	"syscall" | ||
|  | 
 | ||
|  | 	"golang.org/x/sys/unix" | ||
|  | ) | ||
|  | 
 | ||
|  | // State contains the state of a terminal.
 | ||
|  | type state struct { | ||
|  | 	termios unix.Termios | ||
|  | } | ||
|  | 
 | ||
|  | func isTerminal(fd int) bool { | ||
|  | 	_, err := unix.IoctlGetTermio(fd, unix.TCGETA) | ||
|  | 	return err == nil | ||
|  | } | ||
|  | 
 | ||
|  | func readPassword(fd int) ([]byte, error) { | ||
|  | 	// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
 | ||
|  | 	val, err := unix.IoctlGetTermios(fd, unix.TCGETS) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 	oldState := *val | ||
|  | 
 | ||
|  | 	newState := oldState | ||
|  | 	newState.Lflag &^= syscall.ECHO | ||
|  | 	newState.Lflag |= syscall.ICANON | syscall.ISIG | ||
|  | 	newState.Iflag |= syscall.ICRNL | ||
|  | 	err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState) | ||
|  | 
 | ||
|  | 	var buf [16]byte | ||
|  | 	var ret []byte | ||
|  | 	for { | ||
|  | 		n, err := syscall.Read(fd, buf[:]) | ||
|  | 		if err != nil { | ||
|  | 			return nil, err | ||
|  | 		} | ||
|  | 		if n == 0 { | ||
|  | 			if len(ret) == 0 { | ||
|  | 				return nil, io.EOF | ||
|  | 			} | ||
|  | 			break | ||
|  | 		} | ||
|  | 		if buf[n-1] == '\n' { | ||
|  | 			n-- | ||
|  | 		} | ||
|  | 		ret = append(ret, buf[:n]...) | ||
|  | 		if n < len(buf) { | ||
|  | 			break | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return ret, nil | ||
|  | } | ||
|  | 
 | ||
|  | func makeRaw(fd int) (*State, error) { | ||
|  | 	// see http://cr.illumos.org/~webrev/andy_js/1060/
 | ||
|  | 	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	oldState := State{state{termios: *termios}} | ||
|  | 
 | ||
|  | 	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON | ||
|  | 	termios.Oflag &^= unix.OPOST | ||
|  | 	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN | ||
|  | 	termios.Cflag &^= unix.CSIZE | unix.PARENB | ||
|  | 	termios.Cflag |= unix.CS8 | ||
|  | 	termios.Cc[unix.VMIN] = 1 | ||
|  | 	termios.Cc[unix.VTIME] = 0 | ||
|  | 
 | ||
|  | 	if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return &oldState, nil | ||
|  | } | ||
|  | 
 | ||
|  | func restore(fd int, oldState *State) error { | ||
|  | 	return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios) | ||
|  | } | ||
|  | 
 | ||
|  | func getState(fd int) (*State, error) { | ||
|  | 	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return &State{state{termios: *termios}}, nil | ||
|  | } | ||
|  | 
 | ||
|  | func getSize(fd int) (width, height int, err error) { | ||
|  | 	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) | ||
|  | 	if err != nil { | ||
|  | 		return 0, 0, err | ||
|  | 	} | ||
|  | 	return int(ws.Col), int(ws.Row), nil | ||
|  | } |