|
|
// 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 darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris
// 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 readline
import ( "io" "syscall" )
// State contains the state of a terminal.
type State struct { termios Termios }
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool { _, err := getTermios(fd) return err == nil }
// 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 termios, err := getTermios(fd); err != nil { return nil, err } else { oldState.termios = *termios }
newState := oldState.termios // This attempts to replicate the behaviour documented for cfmakeraw in
// the termios(3) manpage.
newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON // newState.Oflag &^= syscall.OPOST
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN newState.Cflag &^= syscall.CSIZE | syscall.PARENB newState.Cflag |= syscall.CS8
newState.Cc[syscall.VMIN] = 1 newState.Cc[syscall.VTIME] = 0
return &oldState, setTermios(fd, &newState) }
// 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) { termios, err := getTermios(fd) if err != nil { return nil, err }
return &State{termios: *termios}, nil }
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func restoreTerm(fd int, state *State) error { return setTermios(fd, &state.termios) }
// 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) { oldState, err := getTermios(fd) if err != nil { return nil, err }
newState := oldState newState.Lflag &^= syscall.ECHO newState.Lflag |= syscall.ICANON | syscall.ISIG newState.Iflag |= syscall.ICRNL if err := setTermios(fd, newState); err != nil { return nil, err }
defer func() { setTermios(fd, 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 }
|