github.com/carlanton/docker@v1.8.0-rc1/pkg/term/winconsole/term_emulator.go (about)

     1  package winconsole
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
    11  const (
    12  	ANSI_ESCAPE_PRIMARY   = 0x1B
    13  	ANSI_ESCAPE_SECONDARY = 0x5B
    14  	ANSI_COMMAND_FIRST    = 0x40
    15  	ANSI_COMMAND_LAST     = 0x7E
    16  	ANSI_PARAMETER_SEP    = ";"
    17  	ANSI_CMD_G0           = '('
    18  	ANSI_CMD_G1           = ')'
    19  	ANSI_CMD_G2           = '*'
    20  	ANSI_CMD_G3           = '+'
    21  	ANSI_CMD_DECPNM       = '>'
    22  	ANSI_CMD_DECPAM       = '='
    23  	ANSI_CMD_OSC          = ']'
    24  	ANSI_CMD_STR_TERM     = '\\'
    25  	ANSI_BEL              = 0x07
    26  	KEY_EVENT             = 1
    27  )
    28  
    29  // Interface that implements terminal handling
    30  type terminalEmulator interface {
    31  	HandleOutputCommand(fd uintptr, command []byte) (n int, err error)
    32  	HandleInputSequence(fd uintptr, command []byte) (n int, err error)
    33  	WriteChars(fd uintptr, w io.Writer, p []byte) (n int, err error)
    34  	ReadChars(fd uintptr, w io.Reader, p []byte) (n int, err error)
    35  }
    36  
    37  type terminalWriter struct {
    38  	wrappedWriter io.Writer
    39  	emulator      terminalEmulator
    40  	command       []byte
    41  	inSequence    bool
    42  	fd            uintptr
    43  }
    44  
    45  type terminalReader struct {
    46  	wrappedReader io.ReadCloser
    47  	emulator      terminalEmulator
    48  	command       []byte
    49  	inSequence    bool
    50  	fd            uintptr
    51  }
    52  
    53  // http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
    54  func isAnsiCommandChar(b byte) bool {
    55  	switch {
    56  	case ANSI_COMMAND_FIRST <= b && b <= ANSI_COMMAND_LAST && b != ANSI_ESCAPE_SECONDARY:
    57  		return true
    58  	case b == ANSI_CMD_G1 || b == ANSI_CMD_OSC || b == ANSI_CMD_DECPAM || b == ANSI_CMD_DECPNM:
    59  		// non-CSI escape sequence terminator
    60  		return true
    61  	case b == ANSI_CMD_STR_TERM || b == ANSI_BEL:
    62  		// String escape sequence terminator
    63  		return true
    64  	}
    65  	return false
    66  }
    67  
    68  func isCharacterSelectionCmdChar(b byte) bool {
    69  	return (b == ANSI_CMD_G0 || b == ANSI_CMD_G1 || b == ANSI_CMD_G2 || b == ANSI_CMD_G3)
    70  }
    71  
    72  func isXtermOscSequence(command []byte, current byte) bool {
    73  	return (len(command) >= 2 && command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_CMD_OSC && current != ANSI_BEL)
    74  }
    75  
    76  // Write writes len(p) bytes from p to the underlying data stream.
    77  // http://golang.org/pkg/io/#Writer
    78  func (tw *terminalWriter) Write(p []byte) (n int, err error) {
    79  	if len(p) == 0 {
    80  		return 0, nil
    81  	}
    82  	if tw.emulator == nil {
    83  		return tw.wrappedWriter.Write(p)
    84  	}
    85  	// Emulate terminal by extracting commands and executing them
    86  	totalWritten := 0
    87  	start := 0 // indicates start of the next chunk
    88  	end := len(p)
    89  	for current := 0; current < end; current++ {
    90  		if tw.inSequence {
    91  			// inside escape sequence
    92  			tw.command = append(tw.command, p[current])
    93  			if isAnsiCommandChar(p[current]) {
    94  				if !isXtermOscSequence(tw.command, p[current]) {
    95  					// found the last command character.
    96  					// Now we have a complete command.
    97  					nchar, err := tw.emulator.HandleOutputCommand(tw.fd, tw.command)
    98  					totalWritten += nchar
    99  					if err != nil {
   100  						return totalWritten, err
   101  					}
   102  
   103  					// clear the command
   104  					// don't include current character again
   105  					tw.command = tw.command[:0]
   106  					start = current + 1
   107  					tw.inSequence = false
   108  				}
   109  			}
   110  		} else {
   111  			if p[current] == ANSI_ESCAPE_PRIMARY {
   112  				// entering escape sequnce
   113  				tw.inSequence = true
   114  				// indicates end of "normal sequence", write whatever you have so far
   115  				if len(p[start:current]) > 0 {
   116  					nw, err := tw.emulator.WriteChars(tw.fd, tw.wrappedWriter, p[start:current])
   117  					totalWritten += nw
   118  					if err != nil {
   119  						return totalWritten, err
   120  					}
   121  				}
   122  				// include the current character as part of the next sequence
   123  				tw.command = append(tw.command, p[current])
   124  			}
   125  		}
   126  	}
   127  	// note that so far, start of the escape sequence triggers writing out of bytes to console.
   128  	// For the part _after_ the end of last escape sequence, it is not written out yet. So write it out
   129  	if !tw.inSequence {
   130  		// assumption is that we can't be inside sequence and therefore command should be empty
   131  		if len(p[start:]) > 0 {
   132  			nw, err := tw.emulator.WriteChars(tw.fd, tw.wrappedWriter, p[start:])
   133  			totalWritten += nw
   134  			if err != nil {
   135  				return totalWritten, err
   136  			}
   137  		}
   138  	}
   139  	return totalWritten, nil
   140  
   141  }
   142  
   143  // Read reads up to len(p) bytes into p.
   144  // http://golang.org/pkg/io/#Reader
   145  func (tr *terminalReader) Read(p []byte) (n int, err error) {
   146  	//Implementations of Read are discouraged from returning a zero byte count
   147  	// with a nil error, except when len(p) == 0.
   148  	if len(p) == 0 {
   149  		return 0, nil
   150  	}
   151  	if nil == tr.emulator {
   152  		return tr.readFromWrappedReader(p)
   153  	}
   154  	return tr.emulator.ReadChars(tr.fd, tr.wrappedReader, p)
   155  }
   156  
   157  // Close the underlying stream
   158  func (tr *terminalReader) Close() (err error) {
   159  	return tr.wrappedReader.Close()
   160  }
   161  
   162  func (tr *terminalReader) readFromWrappedReader(p []byte) (n int, err error) {
   163  	return tr.wrappedReader.Read(p)
   164  }
   165  
   166  type ansiCommand struct {
   167  	CommandBytes []byte
   168  	Command      string
   169  	Parameters   []string
   170  	IsSpecial    bool
   171  }
   172  
   173  func parseAnsiCommand(command []byte) *ansiCommand {
   174  	if isCharacterSelectionCmdChar(command[1]) {
   175  		// Is Character Set Selection commands
   176  		return &ansiCommand{
   177  			CommandBytes: command,
   178  			Command:      string(command),
   179  			IsSpecial:    true,
   180  		}
   181  	}
   182  	// last char is command character
   183  	lastCharIndex := len(command) - 1
   184  
   185  	retValue := &ansiCommand{
   186  		CommandBytes: command,
   187  		Command:      string(command[lastCharIndex]),
   188  		IsSpecial:    false,
   189  	}
   190  	// more than a single escape
   191  	if lastCharIndex != 0 {
   192  		start := 1
   193  		// skip if double char escape sequence
   194  		if command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_ESCAPE_SECONDARY {
   195  			start++
   196  		}
   197  		// convert this to GetNextParam method
   198  		retValue.Parameters = strings.Split(string(command[start:lastCharIndex]), ANSI_PARAMETER_SEP)
   199  	}
   200  	return retValue
   201  }
   202  
   203  func (c *ansiCommand) getParam(index int) string {
   204  	if len(c.Parameters) > index {
   205  		return c.Parameters[index]
   206  	}
   207  	return ""
   208  }
   209  
   210  func (ac *ansiCommand) String() string {
   211  	return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
   212  		bytesToHex(ac.CommandBytes),
   213  		ac.Command,
   214  		strings.Join(ac.Parameters, "\",\""))
   215  }
   216  
   217  func bytesToHex(b []byte) string {
   218  	hex := make([]string, len(b))
   219  	for i, ch := range b {
   220  		hex[i] = fmt.Sprintf("%X", ch)
   221  	}
   222  	return strings.Join(hex, "")
   223  }
   224  
   225  func parseInt16OrDefault(s string, defaultValue int16) (n int16, err error) {
   226  	if s == "" {
   227  		return defaultValue, nil
   228  	}
   229  	parsedValue, err := strconv.ParseInt(s, 10, 16)
   230  	if err != nil {
   231  		return defaultValue, err
   232  	}
   233  	return int16(parsedValue), nil
   234  }