mirror of
https://github.com/golang/term.git
synced 2026-02-08 11:46:05 +03:00
terminal contains a number of utility functions that are currently only implemented for Linux. Darwin uses different named constants for getting and setting the terminal state so this change splits them off as constants and defines them for each arch. R=golang-dev, minux.ma CC=golang-dev https://golang.org/cl/7286043
129 lines
3.9 KiB
Go
129 lines
3.9 KiB
Go
// Copyright 2011 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.
|
|
|
|
// +build linux,!appengine darwin
|
|
|
|
// Package terminal provides support functions for dealing with terminals, as
|
|
// commonly found on UNIX systems.
|
|
//
|
|
// Putting a terminal into raw mode is the most common requirement:
|
|
//
|
|
// oldState, err := terminal.MakeRaw(0)
|
|
// if err != nil {
|
|
// panic(err)
|
|
// }
|
|
// defer terminal.Restore(0, oldState)
|
|
package terminal
|
|
|
|
import (
|
|
"io"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// State contains the state of a terminal.
|
|
type State struct {
|
|
termios syscall.Termios
|
|
}
|
|
|
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
func IsTerminal(fd int) bool {
|
|
var termios syscall.Termios
|
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
|
return err == 0
|
|
}
|
|
|
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
|
// mode and returns the previous state of the terminal so that it can be
|
|
// restored.
|
|
func MakeRaw(fd int) (*State, error) {
|
|
var oldState State
|
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
|
return nil, err
|
|
}
|
|
|
|
newState := oldState.termios
|
|
newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
|
|
newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
|
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
|
return nil, err
|
|
}
|
|
|
|
return &oldState, nil
|
|
}
|
|
|
|
// GetState returns the current state of a terminal which may be useful to
|
|
// restore the terminal after a signal.
|
|
func GetState(fd int) (*State, error) {
|
|
var oldState State
|
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
|
return nil, err
|
|
}
|
|
|
|
return &oldState, nil
|
|
}
|
|
|
|
// Restore restores the terminal connected to the given file descriptor to a
|
|
// previous state.
|
|
func Restore(fd int, state *State) error {
|
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
|
|
return err
|
|
}
|
|
|
|
// GetSize returns the dimensions of the given terminal.
|
|
func GetSize(fd int) (width, height int, err error) {
|
|
var dimensions [4]uint16
|
|
|
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 {
|
|
return -1, -1, err
|
|
}
|
|
return int(dimensions[1]), int(dimensions[0]), nil
|
|
}
|
|
|
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
|
// returned does not include the \n.
|
|
func ReadPassword(fd int) ([]byte, error) {
|
|
var oldState syscall.Termios
|
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 {
|
|
return nil, err
|
|
}
|
|
|
|
newState := oldState
|
|
newState.Lflag &^= syscall.ECHO
|
|
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
|
newState.Iflag |= syscall.ICRNL
|
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
|
return nil, err
|
|
}
|
|
|
|
defer func() {
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0)
|
|
}()
|
|
|
|
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
|
|
}
|