proxy.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. package term
  2. import (
  3. "io"
  4. )
  5. // EscapeError is special error which returned by a TTY proxy reader's Read()
  6. // method in case its detach escape sequence is read.
  7. type EscapeError struct{}
  8. func (EscapeError) Error() string {
  9. return "read escape sequence"
  10. }
  11. // escapeProxy is used only for attaches with a TTY. It is used to proxy
  12. // stdin keypresses from the underlying reader and look for the passed in
  13. // escape key sequence to signal a detach.
  14. type escapeProxy struct {
  15. escapeKeys []byte
  16. escapeKeyPos int
  17. r io.Reader
  18. }
  19. // NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
  20. // and detects when the specified escape keys are read, in which case the Read
  21. // method will return an error of type EscapeError.
  22. func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
  23. return &escapeProxy{
  24. escapeKeys: escapeKeys,
  25. r: r,
  26. }
  27. }
  28. func (r *escapeProxy) Read(buf []byte) (int, error) {
  29. nr, err := r.r.Read(buf)
  30. preserve := func() {
  31. // this preserves the original key presses in the passed in buffer
  32. nr += r.escapeKeyPos
  33. preserve := make([]byte, 0, r.escapeKeyPos+len(buf))
  34. preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
  35. preserve = append(preserve, buf...)
  36. r.escapeKeyPos = 0
  37. copy(buf[0:nr], preserve)
  38. }
  39. if nr != 1 || err != nil {
  40. if r.escapeKeyPos > 0 {
  41. preserve()
  42. }
  43. return nr, err
  44. }
  45. if buf[0] != r.escapeKeys[r.escapeKeyPos] {
  46. if r.escapeKeyPos > 0 {
  47. preserve()
  48. }
  49. return nr, nil
  50. }
  51. if r.escapeKeyPos == len(r.escapeKeys)-1 {
  52. return 0, EscapeError{}
  53. }
  54. // Looks like we've got an escape key, but we need to match again on the next
  55. // read.
  56. // Store the current escape key we found so we can look for the next one on
  57. // the next read.
  58. // Since this is an escape key, make sure we don't let the caller read it
  59. // If later on we find that this is not the escape sequence, we'll add the
  60. // keys back
  61. r.escapeKeyPos++
  62. return nr - r.escapeKeyPos, nil
  63. }