forked from lug/matterbridge
		
	Add sshchat dependencies in vendor
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++ | ||||
| 			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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										116
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| // 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 ( | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // State contains the state of a terminal. | ||||
| type State struct { | ||||
| 	termios unix.Termios | ||||
| } | ||||
|  | ||||
| // IsTerminal returns true if the given file descriptor is a terminal. | ||||
| func IsTerminal(fd int) bool { | ||||
| 	_, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | ||||
| 	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) { | ||||
| 	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	oldState := State{termios: *termios} | ||||
|  | ||||
| 	// This attempts to replicate the behaviour documented for cfmakeraw in | ||||
| 	// the termios(3) manpage. | ||||
| 	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, ioctlWriteTermios, termios); err != nil { | ||||
| 		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) { | ||||
| 	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | ||||
| 	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 Restore(fd int, state *State) error { | ||||
| 	return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios) | ||||
| } | ||||
|  | ||||
| // GetSize returns the dimensions of the given terminal. | ||||
| func GetSize(fd int) (width, height int, err error) { | ||||
| 	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) | ||||
| 	if err != nil { | ||||
| 		return -1, -1, err | ||||
| 	} | ||||
| 	return int(ws.Col), int(ws.Row), 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 unix.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) { | ||||
| 	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	newState := *termios | ||||
| 	newState.Lflag &^= unix.ECHO | ||||
| 	newState.Lflag |= unix.ICANON | unix.ISIG | ||||
| 	newState.Iflag |= unix.ICRNL | ||||
| 	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) | ||||
| 	}() | ||||
|  | ||||
| 	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 "golang.org/x/sys/unix" | ||||
|  | ||||
| const ioctlReadTermios = unix.TIOCGETA | ||||
| const ioctlWriteTermios = unix.TIOCSETA | ||||
							
								
								
									
										10
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| // 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 | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| const ioctlReadTermios = unix.TCGETS | ||||
| const ioctlWriteTermios = unix.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) | ||||
| } | ||||
							
								
								
									
										128
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| // 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 { | ||||
| 	state *unix.Termios | ||||
| } | ||||
|  | ||||
| // IsTerminal returns true if the given file descriptor is a terminal. | ||||
| func IsTerminal(fd int) bool { | ||||
| 	_, err := unix.IoctlGetTermio(fd, unix.TCGETA) | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| // MakeRaw puts 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. | ||||
| // see http://cr.illumos.org/~webrev/andy_js/1060/ | ||||
| func MakeRaw(fd int) (*State, error) { | ||||
| 	oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	oldTermios := *oldTermiosPtr | ||||
|  | ||||
| 	newTermios := oldTermios | ||||
| 	newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON | ||||
| 	newTermios.Oflag &^= syscall.OPOST | ||||
| 	newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN | ||||
| 	newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB | ||||
| 	newTermios.Cflag |= syscall.CS8 | ||||
| 	newTermios.Cc[unix.VMIN] = 1 | ||||
| 	newTermios.Cc[unix.VTIME] = 0 | ||||
|  | ||||
| 	if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &State{ | ||||
| 		state: oldTermiosPtr, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Restore restores the terminal connected to the given file descriptor to a | ||||
| // previous state. | ||||
| func Restore(fd int, oldState *State) error { | ||||
| 	return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state) | ||||
| } | ||||
|  | ||||
| // 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) { | ||||
| 	oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &State{ | ||||
| 		state: oldTermiosPtr, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // GetSize returns the dimensions of the given terminal. | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| // 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 ( | ||||
| 	"os" | ||||
|  | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
|  | ||||
| type State struct { | ||||
| 	mode uint32 | ||||
| } | ||||
|  | ||||
| // IsTerminal returns true if the given file descriptor is a terminal. | ||||
| func IsTerminal(fd int) bool { | ||||
| 	var st uint32 | ||||
| 	err := windows.GetConsoleMode(windows.Handle(fd), &st) | ||||
| 	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 st uint32 | ||||
| 	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) | ||||
| 	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	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 | ||||
| 	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	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 { | ||||
| 	return windows.SetConsoleMode(windows.Handle(fd), state.mode) | ||||
| } | ||||
|  | ||||
| // GetSize returns the dimensions of the given terminal. | ||||
| func GetSize(fd int) (width, height int, err error) { | ||||
| 	var info windows.ConsoleScreenBufferInfo | ||||
| 	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
| 	return int(info.Size.X), int(info.Size.Y), 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 st uint32 | ||||
| 	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	old := st | ||||
|  | ||||
| 	st &^= (windows.ENABLE_ECHO_INPUT) | ||||
| 	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) | ||||
| 	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		windows.SetConsoleMode(windows.Handle(fd), old) | ||||
| 	}() | ||||
|  | ||||
| 	return readPasswordLine(os.NewFile(uintptr(fd), "stdin")) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Wim
					Wim