Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
		
							
								
								
									
										951
									
								
								vendor/golang.org/x/crypto/ssh/terminal/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										951
									
								
								vendor/golang.org/x/crypto/ssh/terminal/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,951 @@
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EscapeCodes contains escape sequences that can be written to the terminal in
 | 
			
		||||
// order to achieve different styles of text.
 | 
			
		||||
type EscapeCodes struct {
 | 
			
		||||
	// Foreground colors
 | 
			
		||||
	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
 | 
			
		||||
 | 
			
		||||
	// Reset all attributes
 | 
			
		||||
	Reset []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var vt100EscapeCodes = EscapeCodes{
 | 
			
		||||
	Black:   []byte{keyEscape, '[', '3', '0', 'm'},
 | 
			
		||||
	Red:     []byte{keyEscape, '[', '3', '1', 'm'},
 | 
			
		||||
	Green:   []byte{keyEscape, '[', '3', '2', 'm'},
 | 
			
		||||
	Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
 | 
			
		||||
	Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
 | 
			
		||||
	Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
 | 
			
		||||
	Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
 | 
			
		||||
	White:   []byte{keyEscape, '[', '3', '7', 'm'},
 | 
			
		||||
 | 
			
		||||
	Reset: []byte{keyEscape, '[', '0', 'm'},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Terminal contains the state for running a VT100 terminal that is capable of
 | 
			
		||||
// reading lines of input.
 | 
			
		||||
type Terminal struct {
 | 
			
		||||
	// AutoCompleteCallback, if non-null, is called for each keypress with
 | 
			
		||||
	// the full input line and the current position of the cursor (in
 | 
			
		||||
	// bytes, as an index into |line|). If it returns ok=false, the key
 | 
			
		||||
	// press is processed normally. Otherwise it returns a replacement line
 | 
			
		||||
	// and the new cursor position.
 | 
			
		||||
	AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
 | 
			
		||||
 | 
			
		||||
	// Escape contains a pointer to the escape codes for this terminal.
 | 
			
		||||
	// It's always a valid pointer, although the escape codes themselves
 | 
			
		||||
	// may be empty if the terminal doesn't support them.
 | 
			
		||||
	Escape *EscapeCodes
 | 
			
		||||
 | 
			
		||||
	// lock protects the terminal and the state in this object from
 | 
			
		||||
	// concurrent processing of a key press and a Write() call.
 | 
			
		||||
	lock sync.Mutex
 | 
			
		||||
 | 
			
		||||
	c      io.ReadWriter
 | 
			
		||||
	prompt []rune
 | 
			
		||||
 | 
			
		||||
	// line is the current line being entered.
 | 
			
		||||
	line []rune
 | 
			
		||||
	// pos is the logical position of the cursor in line
 | 
			
		||||
	pos int
 | 
			
		||||
	// echo is true if local echo is enabled
 | 
			
		||||
	echo bool
 | 
			
		||||
	// pasteActive is true iff there is a bracketed paste operation in
 | 
			
		||||
	// progress.
 | 
			
		||||
	pasteActive bool
 | 
			
		||||
 | 
			
		||||
	// cursorX contains the current X value of the cursor where the left
 | 
			
		||||
	// edge is 0. cursorY contains the row number where the first row of
 | 
			
		||||
	// the current line is 0.
 | 
			
		||||
	cursorX, cursorY int
 | 
			
		||||
	// maxLine is the greatest value of cursorY so far.
 | 
			
		||||
	maxLine int
 | 
			
		||||
 | 
			
		||||
	termWidth, termHeight int
 | 
			
		||||
 | 
			
		||||
	// outBuf contains the terminal data to be sent.
 | 
			
		||||
	outBuf []byte
 | 
			
		||||
	// remainder contains the remainder of any partial key sequences after
 | 
			
		||||
	// a read. It aliases into inBuf.
 | 
			
		||||
	remainder []byte
 | 
			
		||||
	inBuf     [256]byte
 | 
			
		||||
 | 
			
		||||
	// history contains previously entered commands so that they can be
 | 
			
		||||
	// accessed with the up and down keys.
 | 
			
		||||
	history stRingBuffer
 | 
			
		||||
	// historyIndex stores the currently accessed history entry, where zero
 | 
			
		||||
	// means the immediately previous entry.
 | 
			
		||||
	historyIndex int
 | 
			
		||||
	// When navigating up and down the history it's possible to return to
 | 
			
		||||
	// the incomplete, initial line. That value is stored in
 | 
			
		||||
	// historyPending.
 | 
			
		||||
	historyPending string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
 | 
			
		||||
// a local terminal, that terminal must first have been put into raw mode.
 | 
			
		||||
// prompt is a string that is written at the start of each input line (i.e.
 | 
			
		||||
// "> ").
 | 
			
		||||
func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
 | 
			
		||||
	return &Terminal{
 | 
			
		||||
		Escape:       &vt100EscapeCodes,
 | 
			
		||||
		c:            c,
 | 
			
		||||
		prompt:       []rune(prompt),
 | 
			
		||||
		termWidth:    80,
 | 
			
		||||
		termHeight:   24,
 | 
			
		||||
		echo:         true,
 | 
			
		||||
		historyIndex: -1,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	keyCtrlD     = 4
 | 
			
		||||
	keyCtrlU     = 21
 | 
			
		||||
	keyEnter     = '\r'
 | 
			
		||||
	keyEscape    = 27
 | 
			
		||||
	keyBackspace = 127
 | 
			
		||||
	keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
 | 
			
		||||
	keyUp
 | 
			
		||||
	keyDown
 | 
			
		||||
	keyLeft
 | 
			
		||||
	keyRight
 | 
			
		||||
	keyAltLeft
 | 
			
		||||
	keyAltRight
 | 
			
		||||
	keyHome
 | 
			
		||||
	keyEnd
 | 
			
		||||
	keyDeleteWord
 | 
			
		||||
	keyDeleteLine
 | 
			
		||||
	keyClearScreen
 | 
			
		||||
	keyPasteStart
 | 
			
		||||
	keyPasteEnd
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	crlf       = []byte{'\r', '\n'}
 | 
			
		||||
	pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
 | 
			
		||||
	pasteEnd   = []byte{keyEscape, '[', '2', '0', '1', '~'}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// bytesToKey tries to parse a key sequence from b. If successful, it returns
 | 
			
		||||
// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
 | 
			
		||||
func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
 | 
			
		||||
	if len(b) == 0 {
 | 
			
		||||
		return utf8.RuneError, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive {
 | 
			
		||||
		switch b[0] {
 | 
			
		||||
		case 1: // ^A
 | 
			
		||||
			return keyHome, b[1:]
 | 
			
		||||
		case 5: // ^E
 | 
			
		||||
			return keyEnd, b[1:]
 | 
			
		||||
		case 8: // ^H
 | 
			
		||||
			return keyBackspace, b[1:]
 | 
			
		||||
		case 11: // ^K
 | 
			
		||||
			return keyDeleteLine, b[1:]
 | 
			
		||||
		case 12: // ^L
 | 
			
		||||
			return keyClearScreen, b[1:]
 | 
			
		||||
		case 23: // ^W
 | 
			
		||||
			return keyDeleteWord, b[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b[0] != keyEscape {
 | 
			
		||||
		if !utf8.FullRune(b) {
 | 
			
		||||
			return utf8.RuneError, b
 | 
			
		||||
		}
 | 
			
		||||
		r, l := utf8.DecodeRune(b)
 | 
			
		||||
		return r, b[l:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
 | 
			
		||||
		switch b[2] {
 | 
			
		||||
		case 'A':
 | 
			
		||||
			return keyUp, b[3:]
 | 
			
		||||
		case 'B':
 | 
			
		||||
			return keyDown, b[3:]
 | 
			
		||||
		case 'C':
 | 
			
		||||
			return keyRight, b[3:]
 | 
			
		||||
		case 'D':
 | 
			
		||||
			return keyLeft, b[3:]
 | 
			
		||||
		case 'H':
 | 
			
		||||
			return keyHome, b[3:]
 | 
			
		||||
		case 'F':
 | 
			
		||||
			return keyEnd, b[3:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
 | 
			
		||||
		switch b[5] {
 | 
			
		||||
		case 'C':
 | 
			
		||||
			return keyAltRight, b[6:]
 | 
			
		||||
		case 'D':
 | 
			
		||||
			return keyAltLeft, b[6:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
 | 
			
		||||
		return keyPasteStart, b[6:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
 | 
			
		||||
		return keyPasteEnd, b[6:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get here then we have a key that we don't recognise, or a
 | 
			
		||||
	// partial sequence. It's not clear how one should find the end of a
 | 
			
		||||
	// sequence without knowing them all, but it seems that [a-zA-Z~] only
 | 
			
		||||
	// appears at the end of a sequence.
 | 
			
		||||
	for i, c := range b[0:] {
 | 
			
		||||
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
 | 
			
		||||
			return keyUnknown, b[i+1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return utf8.RuneError, b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// queue appends data to the end of t.outBuf
 | 
			
		||||
func (t *Terminal) queue(data []rune) {
 | 
			
		||||
	t.outBuf = append(t.outBuf, []byte(string(data))...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
 | 
			
		||||
var space = []rune{' '}
 | 
			
		||||
 | 
			
		||||
func isPrintable(key rune) bool {
 | 
			
		||||
	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
 | 
			
		||||
	return key >= 32 && !isInSurrogateArea
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// moveCursorToPos appends data to t.outBuf which will move the cursor to the
 | 
			
		||||
// given, logical position in the text.
 | 
			
		||||
func (t *Terminal) moveCursorToPos(pos int) {
 | 
			
		||||
	if !t.echo {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	x := visualLength(t.prompt) + pos
 | 
			
		||||
	y := x / t.termWidth
 | 
			
		||||
	x = x % t.termWidth
 | 
			
		||||
 | 
			
		||||
	up := 0
 | 
			
		||||
	if y < t.cursorY {
 | 
			
		||||
		up = t.cursorY - y
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	down := 0
 | 
			
		||||
	if y > t.cursorY {
 | 
			
		||||
		down = y - t.cursorY
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	left := 0
 | 
			
		||||
	if x < t.cursorX {
 | 
			
		||||
		left = t.cursorX - x
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	right := 0
 | 
			
		||||
	if x > t.cursorX {
 | 
			
		||||
		right = x - t.cursorX
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.cursorX = x
 | 
			
		||||
	t.cursorY = y
 | 
			
		||||
	t.move(up, down, left, right)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) move(up, down, left, right int) {
 | 
			
		||||
	movement := make([]rune, 3*(up+down+left+right))
 | 
			
		||||
	m := movement
 | 
			
		||||
	for i := 0; i < up; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'A'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < down; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'B'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < left; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'D'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < right; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'C'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.queue(movement)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) clearLineToRight() {
 | 
			
		||||
	op := []rune{keyEscape, '[', 'K'}
 | 
			
		||||
	t.queue(op)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const maxLineLength = 4096
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) setLine(newLine []rune, newPos int) {
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.moveCursorToPos(0)
 | 
			
		||||
		t.writeLine(newLine)
 | 
			
		||||
		for i := len(newLine); i < len(t.line); i++ {
 | 
			
		||||
			t.writeLine(space)
 | 
			
		||||
		}
 | 
			
		||||
		t.moveCursorToPos(newPos)
 | 
			
		||||
	}
 | 
			
		||||
	t.line = newLine
 | 
			
		||||
	t.pos = newPos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) advanceCursor(places int) {
 | 
			
		||||
	t.cursorX += places
 | 
			
		||||
	t.cursorY += t.cursorX / t.termWidth
 | 
			
		||||
	if t.cursorY > t.maxLine {
 | 
			
		||||
		t.maxLine = t.cursorY
 | 
			
		||||
	}
 | 
			
		||||
	t.cursorX = t.cursorX % t.termWidth
 | 
			
		||||
 | 
			
		||||
	if places > 0 && t.cursorX == 0 {
 | 
			
		||||
		// Normally terminals will advance the current position
 | 
			
		||||
		// when writing a character. But that doesn't happen
 | 
			
		||||
		// for the last character in a line. However, when
 | 
			
		||||
		// writing a character (except a new line) that causes
 | 
			
		||||
		// a line wrap, the position will be advanced two
 | 
			
		||||
		// places.
 | 
			
		||||
		//
 | 
			
		||||
		// So, if we are stopping at the end of a line, we
 | 
			
		||||
		// need to write a newline so that our cursor can be
 | 
			
		||||
		// advanced to the next line.
 | 
			
		||||
		t.outBuf = append(t.outBuf, '\r', '\n')
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) eraseNPreviousChars(n int) {
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.pos < n {
 | 
			
		||||
		n = t.pos
 | 
			
		||||
	}
 | 
			
		||||
	t.pos -= n
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
 | 
			
		||||
	copy(t.line[t.pos:], t.line[n+t.pos:])
 | 
			
		||||
	t.line = t.line[:len(t.line)-n]
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.writeLine(t.line[t.pos:])
 | 
			
		||||
		for i := 0; i < n; i++ {
 | 
			
		||||
			t.queue(space)
 | 
			
		||||
		}
 | 
			
		||||
		t.advanceCursor(n)
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// countToLeftWord returns then number of characters from the cursor to the
 | 
			
		||||
// start of the previous word.
 | 
			
		||||
func (t *Terminal) countToLeftWord() int {
 | 
			
		||||
	if t.pos == 0 {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pos := t.pos - 1
 | 
			
		||||
	for pos > 0 {
 | 
			
		||||
		if t.line[pos] != ' ' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos--
 | 
			
		||||
	}
 | 
			
		||||
	for pos > 0 {
 | 
			
		||||
		if t.line[pos] == ' ' {
 | 
			
		||||
			pos++
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return t.pos - pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// countToRightWord returns then number of characters from the cursor to the
 | 
			
		||||
// start of the next word.
 | 
			
		||||
func (t *Terminal) countToRightWord() int {
 | 
			
		||||
	pos := t.pos
 | 
			
		||||
	for pos < len(t.line) {
 | 
			
		||||
		if t.line[pos] == ' ' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos++
 | 
			
		||||
	}
 | 
			
		||||
	for pos < len(t.line) {
 | 
			
		||||
		if t.line[pos] != ' ' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos++
 | 
			
		||||
	}
 | 
			
		||||
	return pos - t.pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// visualLength returns the number of visible glyphs in s.
 | 
			
		||||
func visualLength(runes []rune) int {
 | 
			
		||||
	inEscapeSeq := false
 | 
			
		||||
	length := 0
 | 
			
		||||
 | 
			
		||||
	for _, r := range runes {
 | 
			
		||||
		switch {
 | 
			
		||||
		case inEscapeSeq:
 | 
			
		||||
			if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
 | 
			
		||||
				inEscapeSeq = false
 | 
			
		||||
			}
 | 
			
		||||
		case r == '\x1b':
 | 
			
		||||
			inEscapeSeq = true
 | 
			
		||||
		default:
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleKey processes the given key and, optionally, returns a line of text
 | 
			
		||||
// that the user has entered.
 | 
			
		||||
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
 | 
			
		||||
	if t.pasteActive && key != keyEnter {
 | 
			
		||||
		t.addKeyToLine(key)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch key {
 | 
			
		||||
	case keyBackspace:
 | 
			
		||||
		if t.pos == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.eraseNPreviousChars(1)
 | 
			
		||||
	case keyAltLeft:
 | 
			
		||||
		// move left by a word.
 | 
			
		||||
		t.pos -= t.countToLeftWord()
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyAltRight:
 | 
			
		||||
		// move right by a word.
 | 
			
		||||
		t.pos += t.countToRightWord()
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyLeft:
 | 
			
		||||
		if t.pos == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos--
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyRight:
 | 
			
		||||
		if t.pos == len(t.line) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos++
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyHome:
 | 
			
		||||
		if t.pos == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos = 0
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyEnd:
 | 
			
		||||
		if t.pos == len(t.line) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos = len(t.line)
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyUp:
 | 
			
		||||
		entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return "", false
 | 
			
		||||
		}
 | 
			
		||||
		if t.historyIndex == -1 {
 | 
			
		||||
			t.historyPending = string(t.line)
 | 
			
		||||
		}
 | 
			
		||||
		t.historyIndex++
 | 
			
		||||
		runes := []rune(entry)
 | 
			
		||||
		t.setLine(runes, len(runes))
 | 
			
		||||
	case keyDown:
 | 
			
		||||
		switch t.historyIndex {
 | 
			
		||||
		case -1:
 | 
			
		||||
			return
 | 
			
		||||
		case 0:
 | 
			
		||||
			runes := []rune(t.historyPending)
 | 
			
		||||
			t.setLine(runes, len(runes))
 | 
			
		||||
			t.historyIndex--
 | 
			
		||||
		default:
 | 
			
		||||
			entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
 | 
			
		||||
			if ok {
 | 
			
		||||
				t.historyIndex--
 | 
			
		||||
				runes := []rune(entry)
 | 
			
		||||
				t.setLine(runes, len(runes))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case keyEnter:
 | 
			
		||||
		t.moveCursorToPos(len(t.line))
 | 
			
		||||
		t.queue([]rune("\r\n"))
 | 
			
		||||
		line = string(t.line)
 | 
			
		||||
		ok = true
 | 
			
		||||
		t.line = t.line[:0]
 | 
			
		||||
		t.pos = 0
 | 
			
		||||
		t.cursorX = 0
 | 
			
		||||
		t.cursorY = 0
 | 
			
		||||
		t.maxLine = 0
 | 
			
		||||
	case keyDeleteWord:
 | 
			
		||||
		// Delete zero or more spaces and then one or more characters.
 | 
			
		||||
		t.eraseNPreviousChars(t.countToLeftWord())
 | 
			
		||||
	case keyDeleteLine:
 | 
			
		||||
		// Delete everything from the current cursor position to the
 | 
			
		||||
		// end of line.
 | 
			
		||||
		for i := t.pos; i < len(t.line); i++ {
 | 
			
		||||
			t.queue(space)
 | 
			
		||||
			t.advanceCursor(1)
 | 
			
		||||
		}
 | 
			
		||||
		t.line = t.line[:t.pos]
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyCtrlD:
 | 
			
		||||
		// Erase the character under the current position.
 | 
			
		||||
		// The EOF case when the line is empty is handled in
 | 
			
		||||
		// readLine().
 | 
			
		||||
		if t.pos < len(t.line) {
 | 
			
		||||
			t.pos++
 | 
			
		||||
			t.eraseNPreviousChars(1)
 | 
			
		||||
		}
 | 
			
		||||
	case keyCtrlU:
 | 
			
		||||
		t.eraseNPreviousChars(t.pos)
 | 
			
		||||
	case keyClearScreen:
 | 
			
		||||
		// Erases the screen and moves the cursor to the home position.
 | 
			
		||||
		t.queue([]rune("\x1b[2J\x1b[H"))
 | 
			
		||||
		t.queue(t.prompt)
 | 
			
		||||
		t.cursorX, t.cursorY = 0, 0
 | 
			
		||||
		t.advanceCursor(visualLength(t.prompt))
 | 
			
		||||
		t.setLine(t.line, t.pos)
 | 
			
		||||
	default:
 | 
			
		||||
		if t.AutoCompleteCallback != nil {
 | 
			
		||||
			prefix := string(t.line[:t.pos])
 | 
			
		||||
			suffix := string(t.line[t.pos:])
 | 
			
		||||
 | 
			
		||||
			t.lock.Unlock()
 | 
			
		||||
			newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
 | 
			
		||||
			t.lock.Lock()
 | 
			
		||||
 | 
			
		||||
			if completeOk {
 | 
			
		||||
				t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !isPrintable(key) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if len(t.line) == maxLineLength {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.addKeyToLine(key)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addKeyToLine inserts the given key at the current position in the current
 | 
			
		||||
// line.
 | 
			
		||||
func (t *Terminal) addKeyToLine(key rune) {
 | 
			
		||||
	if len(t.line) == cap(t.line) {
 | 
			
		||||
		newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
 | 
			
		||||
		copy(newLine, t.line)
 | 
			
		||||
		t.line = newLine
 | 
			
		||||
	}
 | 
			
		||||
	t.line = t.line[:len(t.line)+1]
 | 
			
		||||
	copy(t.line[t.pos+1:], t.line[t.pos:])
 | 
			
		||||
	t.line[t.pos] = key
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.writeLine(t.line[t.pos:])
 | 
			
		||||
	}
 | 
			
		||||
	t.pos++
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) writeLine(line []rune) {
 | 
			
		||||
	for len(line) != 0 {
 | 
			
		||||
		remainingOnLine := t.termWidth - t.cursorX
 | 
			
		||||
		todo := len(line)
 | 
			
		||||
		if todo > remainingOnLine {
 | 
			
		||||
			todo = remainingOnLine
 | 
			
		||||
		}
 | 
			
		||||
		t.queue(line[:todo])
 | 
			
		||||
		t.advanceCursor(visualLength(line[:todo]))
 | 
			
		||||
		line = line[todo:]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
 | 
			
		||||
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
 | 
			
		||||
	for len(buf) > 0 {
 | 
			
		||||
		i := bytes.IndexByte(buf, '\n')
 | 
			
		||||
		todo := len(buf)
 | 
			
		||||
		if i >= 0 {
 | 
			
		||||
			todo = i
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var nn int
 | 
			
		||||
		nn, err = w.Write(buf[:todo])
 | 
			
		||||
		n += nn
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
		buf = buf[todo:]
 | 
			
		||||
 | 
			
		||||
		if i >= 0 {
 | 
			
		||||
			if _, err = w.Write(crlf); err != nil {
 | 
			
		||||
				return n, err
 | 
			
		||||
			}
 | 
			
		||||
			n += 1
 | 
			
		||||
			buf = buf[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return n, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) Write(buf []byte) (n int, err error) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	if t.cursorX == 0 && t.cursorY == 0 {
 | 
			
		||||
		// This is the easy case: there's nothing on the screen that we
 | 
			
		||||
		// have to move out of the way.
 | 
			
		||||
		return writeWithCRLF(t.c, buf)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We have a prompt and possibly user input on the screen. We
 | 
			
		||||
	// have to clear it first.
 | 
			
		||||
	t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
 | 
			
		||||
	t.cursorX = 0
 | 
			
		||||
	t.clearLineToRight()
 | 
			
		||||
 | 
			
		||||
	for t.cursorY > 0 {
 | 
			
		||||
		t.move(1 /* up */, 0, 0, 0)
 | 
			
		||||
		t.cursorY--
 | 
			
		||||
		t.clearLineToRight()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err = t.c.Write(t.outBuf); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t.outBuf = t.outBuf[:0]
 | 
			
		||||
 | 
			
		||||
	if n, err = writeWithCRLF(t.c, buf); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.writeLine(t.prompt)
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.writeLine(t.line)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
 | 
			
		||||
	if _, err = t.c.Write(t.outBuf); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t.outBuf = t.outBuf[:0]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadPassword temporarily changes the prompt and reads a password, without
 | 
			
		||||
// echo, from the terminal.
 | 
			
		||||
func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	oldPrompt := t.prompt
 | 
			
		||||
	t.prompt = []rune(prompt)
 | 
			
		||||
	t.echo = false
 | 
			
		||||
 | 
			
		||||
	line, err = t.readLine()
 | 
			
		||||
 | 
			
		||||
	t.prompt = oldPrompt
 | 
			
		||||
	t.echo = true
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadLine returns a line of input from the terminal.
 | 
			
		||||
func (t *Terminal) ReadLine() (line string, err error) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	return t.readLine()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) readLine() (line string, err error) {
 | 
			
		||||
	// t.lock must be held at this point
 | 
			
		||||
 | 
			
		||||
	if t.cursorX == 0 && t.cursorY == 0 {
 | 
			
		||||
		t.writeLine(t.prompt)
 | 
			
		||||
		t.c.Write(t.outBuf)
 | 
			
		||||
		t.outBuf = t.outBuf[:0]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lineIsPasted := t.pasteActive
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		rest := t.remainder
 | 
			
		||||
		lineOk := false
 | 
			
		||||
		for !lineOk {
 | 
			
		||||
			var key rune
 | 
			
		||||
			key, rest = bytesToKey(rest, t.pasteActive)
 | 
			
		||||
			if key == utf8.RuneError {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if !t.pasteActive {
 | 
			
		||||
				if key == keyCtrlD {
 | 
			
		||||
					if len(t.line) == 0 {
 | 
			
		||||
						return "", io.EOF
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if key == keyPasteStart {
 | 
			
		||||
					t.pasteActive = true
 | 
			
		||||
					if len(t.line) == 0 {
 | 
			
		||||
						lineIsPasted = true
 | 
			
		||||
					}
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			} else if key == keyPasteEnd {
 | 
			
		||||
				t.pasteActive = false
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !t.pasteActive {
 | 
			
		||||
				lineIsPasted = false
 | 
			
		||||
			}
 | 
			
		||||
			line, lineOk = t.handleKey(key)
 | 
			
		||||
		}
 | 
			
		||||
		if len(rest) > 0 {
 | 
			
		||||
			n := copy(t.inBuf[:], rest)
 | 
			
		||||
			t.remainder = t.inBuf[:n]
 | 
			
		||||
		} else {
 | 
			
		||||
			t.remainder = nil
 | 
			
		||||
		}
 | 
			
		||||
		t.c.Write(t.outBuf)
 | 
			
		||||
		t.outBuf = t.outBuf[:0]
 | 
			
		||||
		if lineOk {
 | 
			
		||||
			if t.echo {
 | 
			
		||||
				t.historyIndex = -1
 | 
			
		||||
				t.history.Add(line)
 | 
			
		||||
			}
 | 
			
		||||
			if lineIsPasted {
 | 
			
		||||
				err = ErrPasteIndicator
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// t.remainder is a slice at the beginning of t.inBuf
 | 
			
		||||
		// containing a partial key sequence
 | 
			
		||||
		readBuf := t.inBuf[len(t.remainder):]
 | 
			
		||||
		var n int
 | 
			
		||||
 | 
			
		||||
		t.lock.Unlock()
 | 
			
		||||
		n, err = t.c.Read(readBuf)
 | 
			
		||||
		t.lock.Lock()
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		t.remainder = t.inBuf[:n+len(t.remainder)]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPrompt sets the prompt to be used when reading subsequent lines.
 | 
			
		||||
func (t *Terminal) SetPrompt(prompt string) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	t.prompt = []rune(prompt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
 | 
			
		||||
	// Move cursor to column zero at the start of the line.
 | 
			
		||||
	t.move(t.cursorY, 0, t.cursorX, 0)
 | 
			
		||||
	t.cursorX, t.cursorY = 0, 0
 | 
			
		||||
	t.clearLineToRight()
 | 
			
		||||
	for t.cursorY < numPrevLines {
 | 
			
		||||
		// Move down a line
 | 
			
		||||
		t.move(0, 1, 0, 0)
 | 
			
		||||
		t.cursorY++
 | 
			
		||||
		t.clearLineToRight()
 | 
			
		||||
	}
 | 
			
		||||
	// Move back to beginning.
 | 
			
		||||
	t.move(t.cursorY, 0, 0, 0)
 | 
			
		||||
	t.cursorX, t.cursorY = 0, 0
 | 
			
		||||
 | 
			
		||||
	t.queue(t.prompt)
 | 
			
		||||
	t.advanceCursor(visualLength(t.prompt))
 | 
			
		||||
	t.writeLine(t.line)
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) SetSize(width, height int) error {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	if width == 0 {
 | 
			
		||||
		width = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oldWidth := t.termWidth
 | 
			
		||||
	t.termWidth, t.termHeight = width, height
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case width == oldWidth:
 | 
			
		||||
		// If the width didn't change then nothing else needs to be
 | 
			
		||||
		// done.
 | 
			
		||||
		return nil
 | 
			
		||||
	case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
 | 
			
		||||
		// If there is nothing on current line and no prompt printed,
 | 
			
		||||
		// just do nothing
 | 
			
		||||
		return nil
 | 
			
		||||
	case width < oldWidth:
 | 
			
		||||
		// Some terminals (e.g. xterm) will truncate lines that were
 | 
			
		||||
		// too long when shinking. Others, (e.g. gnome-terminal) will
 | 
			
		||||
		// attempt to wrap them. For the former, repainting t.maxLine
 | 
			
		||||
		// works great, but that behaviour goes badly wrong in the case
 | 
			
		||||
		// of the latter because they have doubled every full line.
 | 
			
		||||
 | 
			
		||||
		// We assume that we are working on a terminal that wraps lines
 | 
			
		||||
		// and adjust the cursor position based on every previous line
 | 
			
		||||
		// wrapping and turning into two. This causes the prompt on
 | 
			
		||||
		// xterms to move upwards, which isn't great, but it avoids a
 | 
			
		||||
		// huge mess with gnome-terminal.
 | 
			
		||||
		if t.cursorX >= t.termWidth {
 | 
			
		||||
			t.cursorX = t.termWidth - 1
 | 
			
		||||
		}
 | 
			
		||||
		t.cursorY *= 2
 | 
			
		||||
		t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
 | 
			
		||||
	case width > oldWidth:
 | 
			
		||||
		// If the terminal expands then our position calculations will
 | 
			
		||||
		// be wrong in the future because we think the cursor is
 | 
			
		||||
		// |t.pos| chars into the string, but there will be a gap at
 | 
			
		||||
		// the end of any wrapped line.
 | 
			
		||||
		//
 | 
			
		||||
		// But the position will actually be correct until we move, so
 | 
			
		||||
		// we can move back to the beginning and repaint everything.
 | 
			
		||||
		t.clearAndRepaintLinePlusNPrevious(t.maxLine)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := t.c.Write(t.outBuf)
 | 
			
		||||
	t.outBuf = t.outBuf[:0]
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type pasteIndicatorError struct{}
 | 
			
		||||
 | 
			
		||||
func (pasteIndicatorError) Error() string {
 | 
			
		||||
	return "terminal: ErrPasteIndicator not correctly handled"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrPasteIndicator may be returned from ReadLine as the error, in addition
 | 
			
		||||
// to valid line data. It indicates that bracketed paste mode is enabled and
 | 
			
		||||
// that the returned line consists only of pasted data. Programs may wish to
 | 
			
		||||
// interpret pasted data more literally than typed data.
 | 
			
		||||
var ErrPasteIndicator = pasteIndicatorError{}
 | 
			
		||||
 | 
			
		||||
// SetBracketedPasteMode requests that the terminal bracket paste operations
 | 
			
		||||
// with markers. Not all terminals support this but, if it is supported, then
 | 
			
		||||
// enabling this mode will stop any autocomplete callback from running due to
 | 
			
		||||
// pastes. Additionally, any lines that are completely pasted will be returned
 | 
			
		||||
// from ReadLine with the error set to ErrPasteIndicator.
 | 
			
		||||
func (t *Terminal) SetBracketedPasteMode(on bool) {
 | 
			
		||||
	if on {
 | 
			
		||||
		io.WriteString(t.c, "\x1b[?2004h")
 | 
			
		||||
	} else {
 | 
			
		||||
		io.WriteString(t.c, "\x1b[?2004l")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stRingBuffer is a ring buffer of strings.
 | 
			
		||||
type stRingBuffer struct {
 | 
			
		||||
	// entries contains max elements.
 | 
			
		||||
	entries []string
 | 
			
		||||
	max     int
 | 
			
		||||
	// head contains the index of the element most recently added to the ring.
 | 
			
		||||
	head int
 | 
			
		||||
	// size contains the number of elements in the ring.
 | 
			
		||||
	size int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *stRingBuffer) Add(a string) {
 | 
			
		||||
	if s.entries == nil {
 | 
			
		||||
		const defaultNumEntries = 100
 | 
			
		||||
		s.entries = make([]string, defaultNumEntries)
 | 
			
		||||
		s.max = defaultNumEntries
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.head = (s.head + 1) % s.max
 | 
			
		||||
	s.entries[s.head] = a
 | 
			
		||||
	if s.size < s.max {
 | 
			
		||||
		s.size++
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NthPreviousEntry returns the value passed to the nth previous call to Add.
 | 
			
		||||
// If n is zero then the immediately prior value is returned, if one, then the
 | 
			
		||||
// next most recent, and so on. If such an element doesn't exist then ok is
 | 
			
		||||
// false.
 | 
			
		||||
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
 | 
			
		||||
	if n >= s.size {
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
	index := s.head - n
 | 
			
		||||
	if index < 0 {
 | 
			
		||||
		index += s.max
 | 
			
		||||
	}
 | 
			
		||||
	return s.entries[index], true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readPasswordLine reads from reader until it finds \n or io.EOF.
 | 
			
		||||
// The slice returned does not include the \n.
 | 
			
		||||
// readPasswordLine also ignores any \r it finds.
 | 
			
		||||
func readPasswordLine(reader io.Reader) ([]byte, error) {
 | 
			
		||||
	var buf [1]byte
 | 
			
		||||
	var ret []byte
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		n, err := reader.Read(buf[:])
 | 
			
		||||
		if n > 0 {
 | 
			
		||||
			switch buf[0] {
 | 
			
		||||
			case '\n':
 | 
			
		||||
				return ret, nil
 | 
			
		||||
			case '\r':
 | 
			
		||||
				// remove \r from passwords on Windows
 | 
			
		||||
			default:
 | 
			
		||||
				ret = append(ret, buf[0])
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == io.EOF && len(ret) > 0 {
 | 
			
		||||
				return ret, nil
 | 
			
		||||
			}
 | 
			
		||||
			return ret, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										350
									
								
								vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,350 @@
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockTerminal struct {
 | 
			
		||||
	toSend       []byte
 | 
			
		||||
	bytesPerRead int
 | 
			
		||||
	received     []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *MockTerminal) Read(data []byte) (n int, err error) {
 | 
			
		||||
	n = len(data)
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if n > len(c.toSend) {
 | 
			
		||||
		n = len(c.toSend)
 | 
			
		||||
	}
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		return 0, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	if c.bytesPerRead > 0 && n > c.bytesPerRead {
 | 
			
		||||
		n = c.bytesPerRead
 | 
			
		||||
	}
 | 
			
		||||
	copy(data, c.toSend[:n])
 | 
			
		||||
	c.toSend = c.toSend[n:]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *MockTerminal) Write(data []byte) (n int, err error) {
 | 
			
		||||
	c.received = append(c.received, data...)
 | 
			
		||||
	return len(data), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestClose(t *testing.T) {
 | 
			
		||||
	c := &MockTerminal{}
 | 
			
		||||
	ss := NewTerminal(c, "> ")
 | 
			
		||||
	line, err := ss.ReadLine()
 | 
			
		||||
	if line != "" {
 | 
			
		||||
		t.Errorf("Expected empty line but got: %s", line)
 | 
			
		||||
	}
 | 
			
		||||
	if err != io.EOF {
 | 
			
		||||
		t.Errorf("Error should have been EOF but got: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var keyPressTests = []struct {
 | 
			
		||||
	in             string
 | 
			
		||||
	line           string
 | 
			
		||||
	err            error
 | 
			
		||||
	throwAwayLines int
 | 
			
		||||
}{
 | 
			
		||||
	{
 | 
			
		||||
		err: io.EOF,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "\r",
 | 
			
		||||
		line: "",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "foo\r",
 | 
			
		||||
		line: "foo",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a\x1b[Cb\r", // right
 | 
			
		||||
		line: "ab",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a\x1b[Db\r", // left
 | 
			
		||||
		line: "ba",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a\177b\r", // backspace
 | 
			
		||||
		line: "b",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in: "\x1b[A\r", // up
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in: "\x1b[B\r", // down
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "line\x1b[A\x1b[B\r", // up then down
 | 
			
		||||
		line: "line",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:             "line1\rline2\x1b[A\r", // recall previous line.
 | 
			
		||||
		line:           "line1",
 | 
			
		||||
		throwAwayLines: 1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// recall two previous lines and append.
 | 
			
		||||
		in:             "line1\rline2\rline3\x1b[A\x1b[Axxx\r",
 | 
			
		||||
		line:           "line1xxx",
 | 
			
		||||
		throwAwayLines: 2,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Ctrl-A to move to beginning of line followed by ^K to kill
 | 
			
		||||
		// line.
 | 
			
		||||
		in:   "a b \001\013\r",
 | 
			
		||||
		line: "",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Ctrl-A to move to beginning of line, Ctrl-E to move to end,
 | 
			
		||||
		// finally ^K to kill nothing.
 | 
			
		||||
		in:   "a b \001\005\013\r",
 | 
			
		||||
		line: "a b ",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "\027\r",
 | 
			
		||||
		line: "",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a\027\r",
 | 
			
		||||
		line: "",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a \027\r",
 | 
			
		||||
		line: "",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a b\027\r",
 | 
			
		||||
		line: "a ",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a b \027\r",
 | 
			
		||||
		line: "a ",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "one two thr\x1b[D\027\r",
 | 
			
		||||
		line: "one two r",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "\013\r",
 | 
			
		||||
		line: "",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "a\013\r",
 | 
			
		||||
		line: "a",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "ab\x1b[D\013\r",
 | 
			
		||||
		line: "a",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:   "Ξεσκεπάζω\r",
 | 
			
		||||
		line: "Ξεσκεπάζω",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:             "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace.
 | 
			
		||||
		line:           "",
 | 
			
		||||
		throwAwayLines: 1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		in:             "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter.
 | 
			
		||||
		line:           "£",
 | 
			
		||||
		throwAwayLines: 1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Ctrl-D at the end of the line should be ignored.
 | 
			
		||||
		in:   "a\004\r",
 | 
			
		||||
		line: "a",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// a, b, left, Ctrl-D should erase the b.
 | 
			
		||||
		in:   "ab\x1b[D\004\r",
 | 
			
		||||
		line: "a",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// a, b, c, d, left, left, ^U should erase to the beginning of
 | 
			
		||||
		// the line.
 | 
			
		||||
		in:   "abcd\x1b[D\x1b[D\025\r",
 | 
			
		||||
		line: "cd",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Bracketed paste mode: control sequences should be returned
 | 
			
		||||
		// verbatim in paste mode.
 | 
			
		||||
		in:   "abc\x1b[200~de\177f\x1b[201~\177\r",
 | 
			
		||||
		line: "abcde\177",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Enter in bracketed paste mode should still work.
 | 
			
		||||
		in:             "abc\x1b[200~d\refg\x1b[201~h\r",
 | 
			
		||||
		line:           "efgh",
 | 
			
		||||
		throwAwayLines: 1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Lines consisting entirely of pasted data should be indicated as such.
 | 
			
		||||
		in:   "\x1b[200~a\r",
 | 
			
		||||
		line: "a",
 | 
			
		||||
		err:  ErrPasteIndicator,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKeyPresses(t *testing.T) {
 | 
			
		||||
	for i, test := range keyPressTests {
 | 
			
		||||
		for j := 1; j < len(test.in); j++ {
 | 
			
		||||
			c := &MockTerminal{
 | 
			
		||||
				toSend:       []byte(test.in),
 | 
			
		||||
				bytesPerRead: j,
 | 
			
		||||
			}
 | 
			
		||||
			ss := NewTerminal(c, "> ")
 | 
			
		||||
			for k := 0; k < test.throwAwayLines; k++ {
 | 
			
		||||
				_, err := ss.ReadLine()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			line, err := ss.ReadLine()
 | 
			
		||||
			if line != test.line {
 | 
			
		||||
				t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if err != test.err {
 | 
			
		||||
				t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPasswordNotSaved(t *testing.T) {
 | 
			
		||||
	c := &MockTerminal{
 | 
			
		||||
		toSend:       []byte("password\r\x1b[A\r"),
 | 
			
		||||
		bytesPerRead: 1,
 | 
			
		||||
	}
 | 
			
		||||
	ss := NewTerminal(c, "> ")
 | 
			
		||||
	pw, _ := ss.ReadPassword("> ")
 | 
			
		||||
	if pw != "password" {
 | 
			
		||||
		t.Fatalf("failed to read password, got %s", pw)
 | 
			
		||||
	}
 | 
			
		||||
	line, _ := ss.ReadLine()
 | 
			
		||||
	if len(line) > 0 {
 | 
			
		||||
		t.Fatalf("password was saved in history")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var setSizeTests = []struct {
 | 
			
		||||
	width, height int
 | 
			
		||||
}{
 | 
			
		||||
	{40, 13},
 | 
			
		||||
	{80, 24},
 | 
			
		||||
	{132, 43},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTerminalSetSize(t *testing.T) {
 | 
			
		||||
	for _, setSize := range setSizeTests {
 | 
			
		||||
		c := &MockTerminal{
 | 
			
		||||
			toSend:       []byte("password\r\x1b[A\r"),
 | 
			
		||||
			bytesPerRead: 1,
 | 
			
		||||
		}
 | 
			
		||||
		ss := NewTerminal(c, "> ")
 | 
			
		||||
		ss.SetSize(setSize.width, setSize.height)
 | 
			
		||||
		pw, _ := ss.ReadPassword("Password: ")
 | 
			
		||||
		if pw != "password" {
 | 
			
		||||
			t.Fatalf("failed to read password, got %s", pw)
 | 
			
		||||
		}
 | 
			
		||||
		if string(c.received) != "Password: \r\n" {
 | 
			
		||||
			t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestReadPasswordLineEnd(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		input string
 | 
			
		||||
		want  string
 | 
			
		||||
	}{
 | 
			
		||||
		{"\n", ""},
 | 
			
		||||
		{"\r\n", ""},
 | 
			
		||||
		{"test\r\n", "test"},
 | 
			
		||||
		{"testtesttesttes\n", "testtesttesttes"},
 | 
			
		||||
		{"testtesttesttes\r\n", "testtesttesttes"},
 | 
			
		||||
		{"testtesttesttesttest\n", "testtesttesttesttest"},
 | 
			
		||||
		{"testtesttesttesttest\r\n", "testtesttesttesttest"},
 | 
			
		||||
	}
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		buf := new(bytes.Buffer)
 | 
			
		||||
		if _, err := buf.WriteString(test.input); err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		have, err := readPasswordLine(buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if string(have) != test.want {
 | 
			
		||||
			t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err = buf.WriteString(test.input); err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		have, err = readPasswordLine(buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if string(have) != test.want {
 | 
			
		||||
			t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMakeRawState(t *testing.T) {
 | 
			
		||||
	fd := int(os.Stdout.Fd())
 | 
			
		||||
	if !IsTerminal(fd) {
 | 
			
		||||
		t.Skip("stdout is not a terminal; skipping test")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	st, err := GetState(fd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("failed to get terminal state from GetState: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer Restore(fd, st)
 | 
			
		||||
	raw, err := MakeRaw(fd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("failed to get terminal state from MakeRaw: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *st != *raw {
 | 
			
		||||
		t.Errorf("states do not match; was %v, expected %v", raw, st)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestOutputNewlines(t *testing.T) {
 | 
			
		||||
	// \n should be changed to \r\n in terminal output.
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	term := NewTerminal(buf, ">")
 | 
			
		||||
 | 
			
		||||
	term.Write([]byte("1\n2\n"))
 | 
			
		||||
	output := string(buf.Bytes())
 | 
			
		||||
	const expected = "1\r\n2\r\n"
 | 
			
		||||
 | 
			
		||||
	if output != expected {
 | 
			
		||||
		t.Errorf("incorrect output: was %q, expected %q", output, expected)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
// 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 "golang.org/x/crypto/ssh/terminal"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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
 | 
			
		||||
	// 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
 | 
			
		||||
	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 {
 | 
			
		||||
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// passwordReader is an io.Reader that reads from a specific file descriptor.
 | 
			
		||||
type passwordReader int
 | 
			
		||||
 | 
			
		||||
func (r passwordReader) Read(buf []byte) (int, error) {
 | 
			
		||||
	return syscall.Read(int(r), buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return readPasswordLine(passwordReader(fd))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
// Copyright 2013 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 netbsd openbsd
 | 
			
		||||
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
const ioctlReadTermios = syscall.TIOCGETA
 | 
			
		||||
const ioctlWriteTermios = syscall.TIOCSETA
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// Copyright 2013 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 terminal
 | 
			
		||||
 | 
			
		||||
// These constants are declared here, rather than importing
 | 
			
		||||
// them from the syscall package as some syscall packages, even
 | 
			
		||||
// on linux, for example gccgo, do not declare them.
 | 
			
		||||
const ioctlReadTermios = 0x5401  // syscall.TCGETS
 | 
			
		||||
const ioctlWriteTermios = 0x5402 // syscall.TCSETS
 | 
			
		||||
							
								
								
									
										58
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
// Copyright 2016 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 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 (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type State struct{}
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(fd int) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores the terminal connected to the given file descriptor to a
 | 
			
		||||
// previous state.
 | 
			
		||||
func Restore(fd int, state *State) error {
 | 
			
		||||
	return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSize returns the dimensions of the given terminal.
 | 
			
		||||
func GetSize(fd int) (width, height int, err error) {
 | 
			
		||||
	return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
// Copyright 2015 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 solaris
 | 
			
		||||
 | 
			
		||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
	"io"
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
 | 
			
		||||
	var termio unix.Termio
 | 
			
		||||
	err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio)
 | 
			
		||||
	return err == 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) {
 | 
			
		||||
	// 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										155
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,155 @@
 | 
			
		||||
// 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 windows
 | 
			
		||||
 | 
			
		||||
// 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 (
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	enableLineInput       = 2
 | 
			
		||||
	enableEchoInput       = 4
 | 
			
		||||
	enableProcessedInput  = 1
 | 
			
		||||
	enableWindowInput     = 8
 | 
			
		||||
	enableMouseInput      = 16
 | 
			
		||||
	enableInsertMode      = 32
 | 
			
		||||
	enableQuickEditMode   = 64
 | 
			
		||||
	enableExtendedFlags   = 128
 | 
			
		||||
	enableAutoPosition    = 256
 | 
			
		||||
	enableProcessedOutput = 1
 | 
			
		||||
	enableWrapAtEolOutput = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	procGetConsoleMode             = kernel32.NewProc("GetConsoleMode")
 | 
			
		||||
	procSetConsoleMode             = kernel32.NewProc("SetConsoleMode")
 | 
			
		||||
	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	short int16
 | 
			
		||||
	word  uint16
 | 
			
		||||
 | 
			
		||||
	coord struct {
 | 
			
		||||
		x short
 | 
			
		||||
		y short
 | 
			
		||||
	}
 | 
			
		||||
	smallRect struct {
 | 
			
		||||
		left   short
 | 
			
		||||
		top    short
 | 
			
		||||
		right  short
 | 
			
		||||
		bottom short
 | 
			
		||||
	}
 | 
			
		||||
	consoleScreenBufferInfo struct {
 | 
			
		||||
		size              coord
 | 
			
		||||
		cursorPosition    coord
 | 
			
		||||
		attributes        word
 | 
			
		||||
		window            smallRect
 | 
			
		||||
		maximumWindowSize coord
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type State struct {
 | 
			
		||||
	mode uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(fd int) bool {
 | 
			
		||||
	var st uint32
 | 
			
		||||
	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
 | 
			
		||||
	return r != 0 && e == 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 st uint32
 | 
			
		||||
	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return nil, error(e)
 | 
			
		||||
	}
 | 
			
		||||
	raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
 | 
			
		||||
	_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return nil, error(e)
 | 
			
		||||
	}
 | 
			
		||||
	return &State{st}, 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 st uint32
 | 
			
		||||
	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return nil, error(e)
 | 
			
		||||
	}
 | 
			
		||||
	return &State{st}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores the terminal connected to the given file descriptor to a
 | 
			
		||||
// previous state.
 | 
			
		||||
func Restore(fd int, state *State) error {
 | 
			
		||||
	_, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSize returns the dimensions of the given terminal.
 | 
			
		||||
func GetSize(fd int) (width, height int, err error) {
 | 
			
		||||
	var info consoleScreenBufferInfo
 | 
			
		||||
	_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return 0, 0, error(e)
 | 
			
		||||
	}
 | 
			
		||||
	return int(info.size.x), int(info.size.y), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// passwordReader is an io.Reader that reads from a specific Windows HANDLE.
 | 
			
		||||
type passwordReader int
 | 
			
		||||
 | 
			
		||||
func (r passwordReader) Read(buf []byte) (int, error) {
 | 
			
		||||
	return syscall.Read(syscall.Handle(r), buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 st uint32
 | 
			
		||||
	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return nil, error(e)
 | 
			
		||||
	}
 | 
			
		||||
	old := st
 | 
			
		||||
 | 
			
		||||
	st &^= (enableEchoInput)
 | 
			
		||||
	st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
 | 
			
		||||
	_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return nil, error(e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return readPasswordLine(passwordReader(fd))
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user