go-ethereum/vendor/github.com/peterh/liner/input.go
Péter Szilágyi 289b30715d Godeps, vendor: convert dependency management to trash (#3198)
This commit converts the dependency management from Godeps to the vendor
folder, also switching the tool from godep to trash. Since the upstream tool
lacks a few features proposed via a few PRs, until those PRs are merged in
(if), use github.com/karalabe/trash.

You can update dependencies via trash --update.

All dependencies have been updated to their latest version.

Parts of the build system are reworked to drop old notions of Godeps and
invocation of the go vet command so that it doesn't run against the vendor
folder, as that will just blow up during vetting.

The conversion drops OpenCL (and hence GPU mining support) from ethash and our
codebase. The short reasoning is that there's noone to maintain and having
opencl libs in our deps messes up builds as go install ./... tries to build
them, failing with unsatisfied link errors for the C OpenCL deps.

golang.org/x/net/context is not vendored in. We expect it to be fetched by the
user (i.e. using go get). To keep ci.go builds reproducible the package is
"vendored" in build/_vendor.
2016-10-28 19:05:01 +02:00

368 lines
7.9 KiB
Go

// +build linux darwin openbsd freebsd netbsd
package liner
import (
"bufio"
"errors"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
)
type nexter struct {
r rune
err error
}
// State represents an open terminal
type State struct {
commonState
origMode termios
defaultMode termios
next <-chan nexter
winch chan os.Signal
pending []rune
useCHA bool
}
// NewLiner initializes a new *State, and sets the terminal into raw mode. To
// restore the terminal to its previous state, call State.Close().
//
// Note if you are still using Go 1.0: NewLiner handles SIGWINCH, so it will
// leak a channel every time you call it. Therefore, it is recommened that you
// upgrade to a newer release of Go, or ensure that NewLiner is only called
// once.
func NewLiner() *State {
var s State
s.r = bufio.NewReader(os.Stdin)
s.terminalSupported = TerminalSupported()
if m, err := TerminalMode(); err == nil {
s.origMode = *m.(*termios)
} else {
s.inputRedirected = true
}
if _, err := getMode(syscall.Stdout); err != 0 {
s.outputRedirected = true
}
if s.inputRedirected && s.outputRedirected {
s.terminalSupported = false
}
if s.terminalSupported && !s.inputRedirected && !s.outputRedirected {
mode := s.origMode
mode.Iflag &^= icrnl | inpck | istrip | ixon
mode.Cflag |= cs8
mode.Lflag &^= syscall.ECHO | icanon | iexten
mode.ApplyMode()
winch := make(chan os.Signal, 1)
signal.Notify(winch, syscall.SIGWINCH)
s.winch = winch
s.checkOutput()
}
if !s.outputRedirected {
s.outputRedirected = !s.getColumns()
}
return &s
}
var errTimedOut = errors.New("timeout")
func (s *State) startPrompt() {
if s.terminalSupported {
if m, err := TerminalMode(); err == nil {
s.defaultMode = *m.(*termios)
mode := s.defaultMode
mode.Lflag &^= isig
mode.ApplyMode()
}
}
s.restartPrompt()
}
func (s *State) restartPrompt() {
next := make(chan nexter)
go func() {
for {
var n nexter
n.r, _, n.err = s.r.ReadRune()
next <- n
// Shut down nexter loop when an end condition has been reached
if n.err != nil || n.r == '\n' || n.r == '\r' || n.r == ctrlC || n.r == ctrlD {
close(next)
return
}
}
}()
s.next = next
}
func (s *State) stopPrompt() {
if s.terminalSupported {
s.defaultMode.ApplyMode()
}
}
func (s *State) nextPending(timeout <-chan time.Time) (rune, error) {
select {
case thing, ok := <-s.next:
if !ok {
return 0, errors.New("liner: internal error")
}
if thing.err != nil {
return 0, thing.err
}
s.pending = append(s.pending, thing.r)
return thing.r, nil
case <-timeout:
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, errTimedOut
}
// not reached
return 0, nil
}
func (s *State) readNext() (interface{}, error) {
if len(s.pending) > 0 {
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
var r rune
select {
case thing, ok := <-s.next:
if !ok {
return 0, errors.New("liner: internal error")
}
if thing.err != nil {
return nil, thing.err
}
r = thing.r
case <-s.winch:
s.getColumns()
return winch, nil
}
if r != esc {
return r, nil
}
s.pending = append(s.pending, r)
// Wait at most 50 ms for the rest of the escape sequence
// If nothing else arrives, it was an actual press of the esc key
timeout := time.After(50 * time.Millisecond)
flag, err := s.nextPending(timeout)
if err != nil {
if err == errTimedOut {
return flag, nil
}
return unknown, err
}
switch flag {
case '[':
code, err := s.nextPending(timeout)
if err != nil {
if err == errTimedOut {
return code, nil
}
return unknown, err
}
switch code {
case 'A':
s.pending = s.pending[:0] // escape code complete
return up, nil
case 'B':
s.pending = s.pending[:0] // escape code complete
return down, nil
case 'C':
s.pending = s.pending[:0] // escape code complete
return right, nil
case 'D':
s.pending = s.pending[:0] // escape code complete
return left, nil
case 'F':
s.pending = s.pending[:0] // escape code complete
return end, nil
case 'H':
s.pending = s.pending[:0] // escape code complete
return home, nil
case 'Z':
s.pending = s.pending[:0] // escape code complete
return shiftTab, nil
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
num := []rune{code}
for {
code, err := s.nextPending(timeout)
if err != nil {
if err == errTimedOut {
return code, nil
}
return nil, err
}
switch code {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
num = append(num, code)
case ';':
// Modifier code to follow
// This only supports Ctrl-left and Ctrl-right for now
x, _ := strconv.ParseInt(string(num), 10, 32)
if x != 1 {
// Can't be left or right
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
num = num[:0]
for {
code, err = s.nextPending(timeout)
if err != nil {
if err == errTimedOut {
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
return nil, err
}
switch code {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
num = append(num, code)
case 'C', 'D':
// right, left
mod, _ := strconv.ParseInt(string(num), 10, 32)
if mod != 5 {
// Not bare Ctrl
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
s.pending = s.pending[:0] // escape code complete
if code == 'C' {
return wordRight, nil
}
return wordLeft, nil
default:
// Not left or right
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
}
case '~':
s.pending = s.pending[:0] // escape code complete
x, _ := strconv.ParseInt(string(num), 10, 32)
switch x {
case 2:
return insert, nil
case 3:
return del, nil
case 5:
return pageUp, nil
case 6:
return pageDown, nil
case 7:
return home, nil
case 8:
return end, nil
case 15:
return f5, nil
case 17:
return f6, nil
case 18:
return f7, nil
case 19:
return f8, nil
case 20:
return f9, nil
case 21:
return f10, nil
case 23:
return f11, nil
case 24:
return f12, nil
default:
return unknown, nil
}
default:
// unrecognized escape code
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
}
}
case 'O':
code, err := s.nextPending(timeout)
if err != nil {
if err == errTimedOut {
return code, nil
}
return nil, err
}
s.pending = s.pending[:0] // escape code complete
switch code {
case 'c':
return wordRight, nil
case 'd':
return wordLeft, nil
case 'H':
return home, nil
case 'F':
return end, nil
case 'P':
return f1, nil
case 'Q':
return f2, nil
case 'R':
return f3, nil
case 'S':
return f4, nil
default:
return unknown, nil
}
case 'b':
s.pending = s.pending[:0] // escape code complete
return altB, nil
case 'f':
s.pending = s.pending[:0] // escape code complete
return altF, nil
case 'y':
s.pending = s.pending[:0] // escape code complete
return altY, nil
default:
rv := s.pending[0]
s.pending = s.pending[1:]
return rv, nil
}
// not reached
return r, nil
}
// Close returns the terminal to its previous mode
func (s *State) Close() error {
stopSignal(s.winch)
if !s.inputRedirected {
s.origMode.ApplyMode()
}
return nil
}
// TerminalSupported returns true if the current terminal supports
// line editing features, and false if liner will use the 'dumb'
// fallback for input.
// Note that TerminalSupported does not check all factors that may
// cause liner to not fully support the terminal (such as stdin redirection)
func TerminalSupported() bool {
bad := map[string]bool{"": true, "dumb": true, "cons25": true}
return !bad[strings.ToLower(os.Getenv("TERM"))]
}