forked from toolshed/abra
		
	
		
			
				
	
	
		
			89 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			89 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 |