| package term |
| |
| import ( |
| "io" |
| ) |
| |
| // EscapeError is special error which returned by a TTY proxy reader's Read() |
| // method in case its detach escape sequence is read. |
| type EscapeError struct{} |
| |
| func (EscapeError) Error() string { |
| return "read escape sequence" |
| } |
| |
| // escapeProxy is used only for attaches with a TTY. It is used to proxy |
| // stdin keypresses from the underlying reader and look for the passed in |
| // escape key sequence to signal a detach. |
| type escapeProxy struct { |
| escapeKeys []byte |
| escapeKeyPos int |
| r io.Reader |
| buf []byte |
| } |
| |
| // NewEscapeProxy returns a new TTY proxy reader which wraps the given reader |
| // and detects when the specified escape keys are read, in which case the Read |
| // method will return an error of type EscapeError. |
| func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader { |
| return &escapeProxy{ |
| escapeKeys: escapeKeys, |
| r: r, |
| } |
| } |
| |
| func (r *escapeProxy) Read(buf []byte) (n int, err error) { |
| if len(r.escapeKeys) > 0 && r.escapeKeyPos == len(r.escapeKeys) { |
| return 0, EscapeError{} |
| } |
| |
| if len(r.buf) > 0 { |
| n = copy(buf, r.buf) |
| r.buf = r.buf[n:] |
| } |
| |
| nr, err := r.r.Read(buf[n:]) |
| n += nr |
| if len(r.escapeKeys) == 0 { |
| return n, err |
| } |
| |
| for i := 0; i < n; i++ { |
| if buf[i] == r.escapeKeys[r.escapeKeyPos] { |
| r.escapeKeyPos++ |
| |
| // Check if the full escape sequence is matched. |
| if r.escapeKeyPos == len(r.escapeKeys) { |
| n = i + 1 - r.escapeKeyPos |
| if n < 0 { |
| n = 0 |
| } |
| return n, EscapeError{} |
| } |
| continue |
| } |
| |
| // If we need to prepend a partial escape sequence from the previous |
| // read, make sure the new buffer size doesn't exceed len(buf). |
| // Otherwise, preserve any extra data in a buffer for the next read. |
| if i < r.escapeKeyPos { |
| preserve := make([]byte, 0, r.escapeKeyPos+n) |
| preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) |
| preserve = append(preserve, buf[:n]...) |
| n = copy(buf, preserve) |
| i += r.escapeKeyPos |
| r.buf = append(r.buf, preserve[n:]...) |
| } |
| r.escapeKeyPos = 0 |
| } |
| |
| // If we're in the middle of reading an escape sequence, make sure we don't |
| // let the caller read it. If later on we find that this is not the escape |
| // sequence, we'll prepend it back to buf. |
| n -= r.escapeKeyPos |
| if n < 0 { |
| n = 0 |
| } |
| return n, err |
| } |