...

Source file src/pkg/cmd/vendor/golang.org/x/crypto/ssh/terminal/terminal.go

     1	// Copyright 2011 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package terminal
     6	
     7	import (
     8		"bytes"
     9		"io"
    10		"strconv"
    11		"sync"
    12		"unicode/utf8"
    13	)
    14	
    15	// EscapeCodes contains escape sequences that can be written to the terminal in
    16	// order to achieve different styles of text.
    17	type EscapeCodes struct {
    18		// Foreground colors
    19		Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
    20	
    21		// Reset all attributes
    22		Reset []byte
    23	}
    24	
    25	var vt100EscapeCodes = EscapeCodes{
    26		Black:   []byte{keyEscape, '[', '3', '0', 'm'},
    27		Red:     []byte{keyEscape, '[', '3', '1', 'm'},
    28		Green:   []byte{keyEscape, '[', '3', '2', 'm'},
    29		Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
    30		Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
    31		Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
    32		Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
    33		White:   []byte{keyEscape, '[', '3', '7', 'm'},
    34	
    35		Reset: []byte{keyEscape, '[', '0', 'm'},
    36	}
    37	
    38	// Terminal contains the state for running a VT100 terminal that is capable of
    39	// reading lines of input.
    40	type Terminal struct {
    41		// AutoCompleteCallback, if non-null, is called for each keypress with
    42		// the full input line and the current position of the cursor (in
    43		// bytes, as an index into |line|). If it returns ok=false, the key
    44		// press is processed normally. Otherwise it returns a replacement line
    45		// and the new cursor position.
    46		AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
    47	
    48		// Escape contains a pointer to the escape codes for this terminal.
    49		// It's always a valid pointer, although the escape codes themselves
    50		// may be empty if the terminal doesn't support them.
    51		Escape *EscapeCodes
    52	
    53		// lock protects the terminal and the state in this object from
    54		// concurrent processing of a key press and a Write() call.
    55		lock sync.Mutex
    56	
    57		c      io.ReadWriter
    58		prompt []rune
    59	
    60		// line is the current line being entered.
    61		line []rune
    62		// pos is the logical position of the cursor in line
    63		pos int
    64		// echo is true if local echo is enabled
    65		echo bool
    66		// pasteActive is true iff there is a bracketed paste operation in
    67		// progress.
    68		pasteActive bool
    69	
    70		// cursorX contains the current X value of the cursor where the left
    71		// edge is 0. cursorY contains the row number where the first row of
    72		// the current line is 0.
    73		cursorX, cursorY int
    74		// maxLine is the greatest value of cursorY so far.
    75		maxLine int
    76	
    77		termWidth, termHeight int
    78	
    79		// outBuf contains the terminal data to be sent.
    80		outBuf []byte
    81		// remainder contains the remainder of any partial key sequences after
    82		// a read. It aliases into inBuf.
    83		remainder []byte
    84		inBuf     [256]byte
    85	
    86		// history contains previously entered commands so that they can be
    87		// accessed with the up and down keys.
    88		history stRingBuffer
    89		// historyIndex stores the currently accessed history entry, where zero
    90		// means the immediately previous entry.
    91		historyIndex int
    92		// When navigating up and down the history it's possible to return to
    93		// the incomplete, initial line. That value is stored in
    94		// historyPending.
    95		historyPending string
    96	}
    97	
    98	// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
    99	// a local terminal, that terminal must first have been put into raw mode.
   100	// prompt is a string that is written at the start of each input line (i.e.
   101	// "> ").
   102	func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
   103		return &Terminal{
   104			Escape:       &vt100EscapeCodes,
   105			c:            c,
   106			prompt:       []rune(prompt),
   107			termWidth:    80,
   108			termHeight:   24,
   109			echo:         true,
   110			historyIndex: -1,
   111		}
   112	}
   113	
   114	const (
   115		keyCtrlD     = 4
   116		keyCtrlU     = 21
   117		keyEnter     = '\r'
   118		keyEscape    = 27
   119		keyBackspace = 127
   120		keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
   121		keyUp
   122		keyDown
   123		keyLeft
   124		keyRight
   125		keyAltLeft
   126		keyAltRight
   127		keyHome
   128		keyEnd
   129		keyDeleteWord
   130		keyDeleteLine
   131		keyClearScreen
   132		keyPasteStart
   133		keyPasteEnd
   134	)
   135	
   136	var (
   137		crlf       = []byte{'\r', '\n'}
   138		pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
   139		pasteEnd   = []byte{keyEscape, '[', '2', '0', '1', '~'}
   140	)
   141	
   142	// bytesToKey tries to parse a key sequence from b. If successful, it returns
   143	// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
   144	func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
   145		if len(b) == 0 {
   146			return utf8.RuneError, nil
   147		}
   148	
   149		if !pasteActive {
   150			switch b[0] {
   151			case 1: // ^A
   152				return keyHome, b[1:]
   153			case 5: // ^E
   154				return keyEnd, b[1:]
   155			case 8: // ^H
   156				return keyBackspace, b[1:]
   157			case 11: // ^K
   158				return keyDeleteLine, b[1:]
   159			case 12: // ^L
   160				return keyClearScreen, b[1:]
   161			case 23: // ^W
   162				return keyDeleteWord, b[1:]
   163			case 14: // ^N
   164				return keyDown, b[1:]
   165			case 16: // ^P
   166				return keyUp, b[1:]
   167			}
   168		}
   169	
   170		if b[0] != keyEscape {
   171			if !utf8.FullRune(b) {
   172				return utf8.RuneError, b
   173			}
   174			r, l := utf8.DecodeRune(b)
   175			return r, b[l:]
   176		}
   177	
   178		if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
   179			switch b[2] {
   180			case 'A':
   181				return keyUp, b[3:]
   182			case 'B':
   183				return keyDown, b[3:]
   184			case 'C':
   185				return keyRight, b[3:]
   186			case 'D':
   187				return keyLeft, b[3:]
   188			case 'H':
   189				return keyHome, b[3:]
   190			case 'F':
   191				return keyEnd, b[3:]
   192			}
   193		}
   194	
   195		if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
   196			switch b[5] {
   197			case 'C':
   198				return keyAltRight, b[6:]
   199			case 'D':
   200				return keyAltLeft, b[6:]
   201			}
   202		}
   203	
   204		if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
   205			return keyPasteStart, b[6:]
   206		}
   207	
   208		if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
   209			return keyPasteEnd, b[6:]
   210		}
   211	
   212		// If we get here then we have a key that we don't recognise, or a
   213		// partial sequence. It's not clear how one should find the end of a
   214		// sequence without knowing them all, but it seems that [a-zA-Z~] only
   215		// appears at the end of a sequence.
   216		for i, c := range b[0:] {
   217			if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
   218				return keyUnknown, b[i+1:]
   219			}
   220		}
   221	
   222		return utf8.RuneError, b
   223	}
   224	
   225	// queue appends data to the end of t.outBuf
   226	func (t *Terminal) queue(data []rune) {
   227		t.outBuf = append(t.outBuf, []byte(string(data))...)
   228	}
   229	
   230	var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
   231	var space = []rune{' '}
   232	
   233	func isPrintable(key rune) bool {
   234		isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
   235		return key >= 32 && !isInSurrogateArea
   236	}
   237	
   238	// moveCursorToPos appends data to t.outBuf which will move the cursor to the
   239	// given, logical position in the text.
   240	func (t *Terminal) moveCursorToPos(pos int) {
   241		if !t.echo {
   242			return
   243		}
   244	
   245		x := visualLength(t.prompt) + pos
   246		y := x / t.termWidth
   247		x = x % t.termWidth
   248	
   249		up := 0
   250		if y < t.cursorY {
   251			up = t.cursorY - y
   252		}
   253	
   254		down := 0
   255		if y > t.cursorY {
   256			down = y - t.cursorY
   257		}
   258	
   259		left := 0
   260		if x < t.cursorX {
   261			left = t.cursorX - x
   262		}
   263	
   264		right := 0
   265		if x > t.cursorX {
   266			right = x - t.cursorX
   267		}
   268	
   269		t.cursorX = x
   270		t.cursorY = y
   271		t.move(up, down, left, right)
   272	}
   273	
   274	func (t *Terminal) move(up, down, left, right int) {
   275		m := []rune{}
   276	
   277		// 1 unit up can be expressed as ^[[A or ^[A
   278		// 5 units up can be expressed as ^[[5A
   279	
   280		if up == 1 {
   281			m = append(m, keyEscape, '[', 'A')
   282		} else if up > 1 {
   283			m = append(m, keyEscape, '[')
   284			m = append(m, []rune(strconv.Itoa(up))...)
   285			m = append(m, 'A')
   286		}
   287	
   288		if down == 1 {
   289			m = append(m, keyEscape, '[', 'B')
   290		} else if down > 1 {
   291			m = append(m, keyEscape, '[')
   292			m = append(m, []rune(strconv.Itoa(down))...)
   293			m = append(m, 'B')
   294		}
   295	
   296		if right == 1 {
   297			m = append(m, keyEscape, '[', 'C')
   298		} else if right > 1 {
   299			m = append(m, keyEscape, '[')
   300			m = append(m, []rune(strconv.Itoa(right))...)
   301			m = append(m, 'C')
   302		}
   303	
   304		if left == 1 {
   305			m = append(m, keyEscape, '[', 'D')
   306		} else if left > 1 {
   307			m = append(m, keyEscape, '[')
   308			m = append(m, []rune(strconv.Itoa(left))...)
   309			m = append(m, 'D')
   310		}
   311	
   312		t.queue(m)
   313	}
   314	
   315	func (t *Terminal) clearLineToRight() {
   316		op := []rune{keyEscape, '[', 'K'}
   317		t.queue(op)
   318	}
   319	
   320	const maxLineLength = 4096
   321	
   322	func (t *Terminal) setLine(newLine []rune, newPos int) {
   323		if t.echo {
   324			t.moveCursorToPos(0)
   325			t.writeLine(newLine)
   326			for i := len(newLine); i < len(t.line); i++ {
   327				t.writeLine(space)
   328			}
   329			t.moveCursorToPos(newPos)
   330		}
   331		t.line = newLine
   332		t.pos = newPos
   333	}
   334	
   335	func (t *Terminal) advanceCursor(places int) {
   336		t.cursorX += places
   337		t.cursorY += t.cursorX / t.termWidth
   338		if t.cursorY > t.maxLine {
   339			t.maxLine = t.cursorY
   340		}
   341		t.cursorX = t.cursorX % t.termWidth
   342	
   343		if places > 0 && t.cursorX == 0 {
   344			// Normally terminals will advance the current position
   345			// when writing a character. But that doesn't happen
   346			// for the last character in a line. However, when
   347			// writing a character (except a new line) that causes
   348			// a line wrap, the position will be advanced two
   349			// places.
   350			//
   351			// So, if we are stopping at the end of a line, we
   352			// need to write a newline so that our cursor can be
   353			// advanced to the next line.
   354			t.outBuf = append(t.outBuf, '\r', '\n')
   355		}
   356	}
   357	
   358	func (t *Terminal) eraseNPreviousChars(n int) {
   359		if n == 0 {
   360			return
   361		}
   362	
   363		if t.pos < n {
   364			n = t.pos
   365		}
   366		t.pos -= n
   367		t.moveCursorToPos(t.pos)
   368	
   369		copy(t.line[t.pos:], t.line[n+t.pos:])
   370		t.line = t.line[:len(t.line)-n]
   371		if t.echo {
   372			t.writeLine(t.line[t.pos:])
   373			for i := 0; i < n; i++ {
   374				t.queue(space)
   375			}
   376			t.advanceCursor(n)
   377			t.moveCursorToPos(t.pos)
   378		}
   379	}
   380	
   381	// countToLeftWord returns then number of characters from the cursor to the
   382	// start of the previous word.
   383	func (t *Terminal) countToLeftWord() int {
   384		if t.pos == 0 {
   385			return 0
   386		}
   387	
   388		pos := t.pos - 1
   389		for pos > 0 {
   390			if t.line[pos] != ' ' {
   391				break
   392			}
   393			pos--
   394		}
   395		for pos > 0 {
   396			if t.line[pos] == ' ' {
   397				pos++
   398				break
   399			}
   400			pos--
   401		}
   402	
   403		return t.pos - pos
   404	}
   405	
   406	// countToRightWord returns then number of characters from the cursor to the
   407	// start of the next word.
   408	func (t *Terminal) countToRightWord() int {
   409		pos := t.pos
   410		for pos < len(t.line) {
   411			if t.line[pos] == ' ' {
   412				break
   413			}
   414			pos++
   415		}
   416		for pos < len(t.line) {
   417			if t.line[pos] != ' ' {
   418				break
   419			}
   420			pos++
   421		}
   422		return pos - t.pos
   423	}
   424	
   425	// visualLength returns the number of visible glyphs in s.
   426	func visualLength(runes []rune) int {
   427		inEscapeSeq := false
   428		length := 0
   429	
   430		for _, r := range runes {
   431			switch {
   432			case inEscapeSeq:
   433				if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
   434					inEscapeSeq = false
   435				}
   436			case r == '\x1b':
   437				inEscapeSeq = true
   438			default:
   439				length++
   440			}
   441		}
   442	
   443		return length
   444	}
   445	
   446	// handleKey processes the given key and, optionally, returns a line of text
   447	// that the user has entered.
   448	func (t *Terminal) handleKey(key rune) (line string, ok bool) {
   449		if t.pasteActive && key != keyEnter {
   450			t.addKeyToLine(key)
   451			return
   452		}
   453	
   454		switch key {
   455		case keyBackspace:
   456			if t.pos == 0 {
   457				return
   458			}
   459			t.eraseNPreviousChars(1)
   460		case keyAltLeft:
   461			// move left by a word.
   462			t.pos -= t.countToLeftWord()
   463			t.moveCursorToPos(t.pos)
   464		case keyAltRight:
   465			// move right by a word.
   466			t.pos += t.countToRightWord()
   467			t.moveCursorToPos(t.pos)
   468		case keyLeft:
   469			if t.pos == 0 {
   470				return
   471			}
   472			t.pos--
   473			t.moveCursorToPos(t.pos)
   474		case keyRight:
   475			if t.pos == len(t.line) {
   476				return
   477			}
   478			t.pos++
   479			t.moveCursorToPos(t.pos)
   480		case keyHome:
   481			if t.pos == 0 {
   482				return
   483			}
   484			t.pos = 0
   485			t.moveCursorToPos(t.pos)
   486		case keyEnd:
   487			if t.pos == len(t.line) {
   488				return
   489			}
   490			t.pos = len(t.line)
   491			t.moveCursorToPos(t.pos)
   492		case keyUp:
   493			entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
   494			if !ok {
   495				return "", false
   496			}
   497			if t.historyIndex == -1 {
   498				t.historyPending = string(t.line)
   499			}
   500			t.historyIndex++
   501			runes := []rune(entry)
   502			t.setLine(runes, len(runes))
   503		case keyDown:
   504			switch t.historyIndex {
   505			case -1:
   506				return
   507			case 0:
   508				runes := []rune(t.historyPending)
   509				t.setLine(runes, len(runes))
   510				t.historyIndex--
   511			default:
   512				entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
   513				if ok {
   514					t.historyIndex--
   515					runes := []rune(entry)
   516					t.setLine(runes, len(runes))
   517				}
   518			}
   519		case keyEnter:
   520			t.moveCursorToPos(len(t.line))
   521			t.queue([]rune("\r\n"))
   522			line = string(t.line)
   523			ok = true
   524			t.line = t.line[:0]
   525			t.pos = 0
   526			t.cursorX = 0
   527			t.cursorY = 0
   528			t.maxLine = 0
   529		case keyDeleteWord:
   530			// Delete zero or more spaces and then one or more characters.
   531			t.eraseNPreviousChars(t.countToLeftWord())
   532		case keyDeleteLine:
   533			// Delete everything from the current cursor position to the
   534			// end of line.
   535			for i := t.pos; i < len(t.line); i++ {
   536				t.queue(space)
   537				t.advanceCursor(1)
   538			}
   539			t.line = t.line[:t.pos]
   540			t.moveCursorToPos(t.pos)
   541		case keyCtrlD:
   542			// Erase the character under the current position.
   543			// The EOF case when the line is empty is handled in
   544			// readLine().
   545			if t.pos < len(t.line) {
   546				t.pos++
   547				t.eraseNPreviousChars(1)
   548			}
   549		case keyCtrlU:
   550			t.eraseNPreviousChars(t.pos)
   551		case keyClearScreen:
   552			// Erases the screen and moves the cursor to the home position.
   553			t.queue([]rune("\x1b[2J\x1b[H"))
   554			t.queue(t.prompt)
   555			t.cursorX, t.cursorY = 0, 0
   556			t.advanceCursor(visualLength(t.prompt))
   557			t.setLine(t.line, t.pos)
   558		default:
   559			if t.AutoCompleteCallback != nil {
   560				prefix := string(t.line[:t.pos])
   561				suffix := string(t.line[t.pos:])
   562	
   563				t.lock.Unlock()
   564				newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
   565				t.lock.Lock()
   566	
   567				if completeOk {
   568					t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
   569					return
   570				}
   571			}
   572			if !isPrintable(key) {
   573				return
   574			}
   575			if len(t.line) == maxLineLength {
   576				return
   577			}
   578			t.addKeyToLine(key)
   579		}
   580		return
   581	}
   582	
   583	// addKeyToLine inserts the given key at the current position in the current
   584	// line.
   585	func (t *Terminal) addKeyToLine(key rune) {
   586		if len(t.line) == cap(t.line) {
   587			newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
   588			copy(newLine, t.line)
   589			t.line = newLine
   590		}
   591		t.line = t.line[:len(t.line)+1]
   592		copy(t.line[t.pos+1:], t.line[t.pos:])
   593		t.line[t.pos] = key
   594		if t.echo {
   595			t.writeLine(t.line[t.pos:])
   596		}
   597		t.pos++
   598		t.moveCursorToPos(t.pos)
   599	}
   600	
   601	func (t *Terminal) writeLine(line []rune) {
   602		for len(line) != 0 {
   603			remainingOnLine := t.termWidth - t.cursorX
   604			todo := len(line)
   605			if todo > remainingOnLine {
   606				todo = remainingOnLine
   607			}
   608			t.queue(line[:todo])
   609			t.advanceCursor(visualLength(line[:todo]))
   610			line = line[todo:]
   611		}
   612	}
   613	
   614	// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
   615	func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
   616		for len(buf) > 0 {
   617			i := bytes.IndexByte(buf, '\n')
   618			todo := len(buf)
   619			if i >= 0 {
   620				todo = i
   621			}
   622	
   623			var nn int
   624			nn, err = w.Write(buf[:todo])
   625			n += nn
   626			if err != nil {
   627				return n, err
   628			}
   629			buf = buf[todo:]
   630	
   631			if i >= 0 {
   632				if _, err = w.Write(crlf); err != nil {
   633					return n, err
   634				}
   635				n++
   636				buf = buf[1:]
   637			}
   638		}
   639	
   640		return n, nil
   641	}
   642	
   643	func (t *Terminal) Write(buf []byte) (n int, err error) {
   644		t.lock.Lock()
   645		defer t.lock.Unlock()
   646	
   647		if t.cursorX == 0 && t.cursorY == 0 {
   648			// This is the easy case: there's nothing on the screen that we
   649			// have to move out of the way.
   650			return writeWithCRLF(t.c, buf)
   651		}
   652	
   653		// We have a prompt and possibly user input on the screen. We
   654		// have to clear it first.
   655		t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
   656		t.cursorX = 0
   657		t.clearLineToRight()
   658	
   659		for t.cursorY > 0 {
   660			t.move(1 /* up */, 0, 0, 0)
   661			t.cursorY--
   662			t.clearLineToRight()
   663		}
   664	
   665		if _, err = t.c.Write(t.outBuf); err != nil {
   666			return
   667		}
   668		t.outBuf = t.outBuf[:0]
   669	
   670		if n, err = writeWithCRLF(t.c, buf); err != nil {
   671			return
   672		}
   673	
   674		t.writeLine(t.prompt)
   675		if t.echo {
   676			t.writeLine(t.line)
   677		}
   678	
   679		t.moveCursorToPos(t.pos)
   680	
   681		if _, err = t.c.Write(t.outBuf); err != nil {
   682			return
   683		}
   684		t.outBuf = t.outBuf[:0]
   685		return
   686	}
   687	
   688	// ReadPassword temporarily changes the prompt and reads a password, without
   689	// echo, from the terminal.
   690	func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
   691		t.lock.Lock()
   692		defer t.lock.Unlock()
   693	
   694		oldPrompt := t.prompt
   695		t.prompt = []rune(prompt)
   696		t.echo = false
   697	
   698		line, err = t.readLine()
   699	
   700		t.prompt = oldPrompt
   701		t.echo = true
   702	
   703		return
   704	}
   705	
   706	// ReadLine returns a line of input from the terminal.
   707	func (t *Terminal) ReadLine() (line string, err error) {
   708		t.lock.Lock()
   709		defer t.lock.Unlock()
   710	
   711		return t.readLine()
   712	}
   713	
   714	func (t *Terminal) readLine() (line string, err error) {
   715		// t.lock must be held at this point
   716	
   717		if t.cursorX == 0 && t.cursorY == 0 {
   718			t.writeLine(t.prompt)
   719			t.c.Write(t.outBuf)
   720			t.outBuf = t.outBuf[:0]
   721		}
   722	
   723		lineIsPasted := t.pasteActive
   724	
   725		for {
   726			rest := t.remainder
   727			lineOk := false
   728			for !lineOk {
   729				var key rune
   730				key, rest = bytesToKey(rest, t.pasteActive)
   731				if key == utf8.RuneError {
   732					break
   733				}
   734				if !t.pasteActive {
   735					if key == keyCtrlD {
   736						if len(t.line) == 0 {
   737							return "", io.EOF
   738						}
   739					}
   740					if key == keyPasteStart {
   741						t.pasteActive = true
   742						if len(t.line) == 0 {
   743							lineIsPasted = true
   744						}
   745						continue
   746					}
   747				} else if key == keyPasteEnd {
   748					t.pasteActive = false
   749					continue
   750				}
   751				if !t.pasteActive {
   752					lineIsPasted = false
   753				}
   754				line, lineOk = t.handleKey(key)
   755			}
   756			if len(rest) > 0 {
   757				n := copy(t.inBuf[:], rest)
   758				t.remainder = t.inBuf[:n]
   759			} else {
   760				t.remainder = nil
   761			}
   762			t.c.Write(t.outBuf)
   763			t.outBuf = t.outBuf[:0]
   764			if lineOk {
   765				if t.echo {
   766					t.historyIndex = -1
   767					t.history.Add(line)
   768				}
   769				if lineIsPasted {
   770					err = ErrPasteIndicator
   771				}
   772				return
   773			}
   774	
   775			// t.remainder is a slice at the beginning of t.inBuf
   776			// containing a partial key sequence
   777			readBuf := t.inBuf[len(t.remainder):]
   778			var n int
   779	
   780			t.lock.Unlock()
   781			n, err = t.c.Read(readBuf)
   782			t.lock.Lock()
   783	
   784			if err != nil {
   785				return
   786			}
   787	
   788			t.remainder = t.inBuf[:n+len(t.remainder)]
   789		}
   790	}
   791	
   792	// SetPrompt sets the prompt to be used when reading subsequent lines.
   793	func (t *Terminal) SetPrompt(prompt string) {
   794		t.lock.Lock()
   795		defer t.lock.Unlock()
   796	
   797		t.prompt = []rune(prompt)
   798	}
   799	
   800	func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
   801		// Move cursor to column zero at the start of the line.
   802		t.move(t.cursorY, 0, t.cursorX, 0)
   803		t.cursorX, t.cursorY = 0, 0
   804		t.clearLineToRight()
   805		for t.cursorY < numPrevLines {
   806			// Move down a line
   807			t.move(0, 1, 0, 0)
   808			t.cursorY++
   809			t.clearLineToRight()
   810		}
   811		// Move back to beginning.
   812		t.move(t.cursorY, 0, 0, 0)
   813		t.cursorX, t.cursorY = 0, 0
   814	
   815		t.queue(t.prompt)
   816		t.advanceCursor(visualLength(t.prompt))
   817		t.writeLine(t.line)
   818		t.moveCursorToPos(t.pos)
   819	}
   820	
   821	func (t *Terminal) SetSize(width, height int) error {
   822		t.lock.Lock()
   823		defer t.lock.Unlock()
   824	
   825		if width == 0 {
   826			width = 1
   827		}
   828	
   829		oldWidth := t.termWidth
   830		t.termWidth, t.termHeight = width, height
   831	
   832		switch {
   833		case width == oldWidth:
   834			// If the width didn't change then nothing else needs to be
   835			// done.
   836			return nil
   837		case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
   838			// If there is nothing on current line and no prompt printed,
   839			// just do nothing
   840			return nil
   841		case width < oldWidth:
   842			// Some terminals (e.g. xterm) will truncate lines that were
   843			// too long when shinking. Others, (e.g. gnome-terminal) will
   844			// attempt to wrap them. For the former, repainting t.maxLine
   845			// works great, but that behaviour goes badly wrong in the case
   846			// of the latter because they have doubled every full line.
   847	
   848			// We assume that we are working on a terminal that wraps lines
   849			// and adjust the cursor position based on every previous line
   850			// wrapping and turning into two. This causes the prompt on
   851			// xterms to move upwards, which isn't great, but it avoids a
   852			// huge mess with gnome-terminal.
   853			if t.cursorX >= t.termWidth {
   854				t.cursorX = t.termWidth - 1
   855			}
   856			t.cursorY *= 2
   857			t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
   858		case width > oldWidth:
   859			// If the terminal expands then our position calculations will
   860			// be wrong in the future because we think the cursor is
   861			// |t.pos| chars into the string, but there will be a gap at
   862			// the end of any wrapped line.
   863			//
   864			// But the position will actually be correct until we move, so
   865			// we can move back to the beginning and repaint everything.
   866			t.clearAndRepaintLinePlusNPrevious(t.maxLine)
   867		}
   868	
   869		_, err := t.c.Write(t.outBuf)
   870		t.outBuf = t.outBuf[:0]
   871		return err
   872	}
   873	
   874	type pasteIndicatorError struct{}
   875	
   876	func (pasteIndicatorError) Error() string {
   877		return "terminal: ErrPasteIndicator not correctly handled"
   878	}
   879	
   880	// ErrPasteIndicator may be returned from ReadLine as the error, in addition
   881	// to valid line data. It indicates that bracketed paste mode is enabled and
   882	// that the returned line consists only of pasted data. Programs may wish to
   883	// interpret pasted data more literally than typed data.
   884	var ErrPasteIndicator = pasteIndicatorError{}
   885	
   886	// SetBracketedPasteMode requests that the terminal bracket paste operations
   887	// with markers. Not all terminals support this but, if it is supported, then
   888	// enabling this mode will stop any autocomplete callback from running due to
   889	// pastes. Additionally, any lines that are completely pasted will be returned
   890	// from ReadLine with the error set to ErrPasteIndicator.
   891	func (t *Terminal) SetBracketedPasteMode(on bool) {
   892		if on {
   893			io.WriteString(t.c, "\x1b[?2004h")
   894		} else {
   895			io.WriteString(t.c, "\x1b[?2004l")
   896		}
   897	}
   898	
   899	// stRingBuffer is a ring buffer of strings.
   900	type stRingBuffer struct {
   901		// entries contains max elements.
   902		entries []string
   903		max     int
   904		// head contains the index of the element most recently added to the ring.
   905		head int
   906		// size contains the number of elements in the ring.
   907		size int
   908	}
   909	
   910	func (s *stRingBuffer) Add(a string) {
   911		if s.entries == nil {
   912			const defaultNumEntries = 100
   913			s.entries = make([]string, defaultNumEntries)
   914			s.max = defaultNumEntries
   915		}
   916	
   917		s.head = (s.head + 1) % s.max
   918		s.entries[s.head] = a
   919		if s.size < s.max {
   920			s.size++
   921		}
   922	}
   923	
   924	// NthPreviousEntry returns the value passed to the nth previous call to Add.
   925	// If n is zero then the immediately prior value is returned, if one, then the
   926	// next most recent, and so on. If such an element doesn't exist then ok is
   927	// false.
   928	func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
   929		if n >= s.size {
   930			return "", false
   931		}
   932		index := s.head - n
   933		if index < 0 {
   934			index += s.max
   935		}
   936		return s.entries[index], true
   937	}
   938	
   939	// readPasswordLine reads from reader until it finds \n or io.EOF.
   940	// The slice returned does not include the \n.
   941	// readPasswordLine also ignores any \r it finds.
   942	func readPasswordLine(reader io.Reader) ([]byte, error) {
   943		var buf [1]byte
   944		var ret []byte
   945	
   946		for {
   947			n, err := reader.Read(buf[:])
   948			if n > 0 {
   949				switch buf[0] {
   950				case '\n':
   951					return ret, nil
   952				case '\r':
   953					// remove \r from passwords on Windows
   954				default:
   955					ret = append(ret, buf[0])
   956				}
   957				continue
   958			}
   959			if err != nil {
   960				if err == io.EOF && len(ret) > 0 {
   961					return ret, nil
   962				}
   963				return ret, err
   964			}
   965		}
   966	}
   967	

View as plain text