Source file src/pkg/cmd/vendor/golang.org/x/crypto/ssh/terminal/terminal.go
1
2
3
4
5 package terminal
6
7 import (
8 "bytes"
9 "io"
10 "strconv"
11 "sync"
12 "unicode/utf8"
13 )
14
15
16
17 type EscapeCodes struct {
18
19 Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
20
21
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
39
40 type Terminal struct {
41
42
43
44
45
46 AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
47
48
49
50
51 Escape *EscapeCodes
52
53
54
55 lock sync.Mutex
56
57 c io.ReadWriter
58 prompt []rune
59
60
61 line []rune
62
63 pos int
64
65 echo bool
66
67
68 pasteActive bool
69
70
71
72
73 cursorX, cursorY int
74
75 maxLine int
76
77 termWidth, termHeight int
78
79
80 outBuf []byte
81
82
83 remainder []byte
84 inBuf [256]byte
85
86
87
88 history stRingBuffer
89
90
91 historyIndex int
92
93
94
95 historyPending string
96 }
97
98
99
100
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 + 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
143
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:
152 return keyHome, b[1:]
153 case 5:
154 return keyEnd, b[1:]
155 case 8:
156 return keyBackspace, b[1:]
157 case 11:
158 return keyDeleteLine, b[1:]
159 case 12:
160 return keyClearScreen, b[1:]
161 case 23:
162 return keyDeleteWord, b[1:]
163 case 14:
164 return keyDown, b[1:]
165 case 16:
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
213
214
215
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
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
239
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
278
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
345
346
347
348
349
350
351
352
353
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
382
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
407
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
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
447
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
462 t.pos -= t.countToLeftWord()
463 t.moveCursorToPos(t.pos)
464 case keyAltRight:
465
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
531 t.eraseNPreviousChars(t.countToLeftWord())
532 case keyDeleteLine:
533
534
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
543
544
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
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
584
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
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
649
650 return writeWithCRLF(t.c, buf)
651 }
652
653
654
655 t.move(0 , 0 , t.cursorX , 0 )
656 t.cursorX = 0
657 t.clearLineToRight()
658
659 for t.cursorY > 0 {
660 t.move(1 , 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
689
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
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
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
776
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
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
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
807 t.move(0, 1, 0, 0)
808 t.cursorY++
809 t.clearLineToRight()
810 }
811
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
835
836 return nil
837 case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
838
839
840 return nil
841 case width < oldWidth:
842
843
844
845
846
847
848
849
850
851
852
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
860
861
862
863
864
865
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
881
882
883
884 var ErrPasteIndicator = pasteIndicatorError{}
885
886
887
888
889
890
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
900 type stRingBuffer struct {
901
902 entries []string
903 max int
904
905 head int
906
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
925
926
927
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
940
941
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
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