mirror of https://github.com/docker/cli.git
137 lines
3.2 KiB
Go
137 lines
3.2 KiB
Go
package ansiterm
|
|
|
|
import (
|
|
"errors"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var logger *logrus.Logger
|
|
|
|
type AnsiParser struct {
|
|
currState state
|
|
eventHandler AnsiEventHandler
|
|
context *ansiContext
|
|
csiEntry state
|
|
csiParam state
|
|
dcsEntry state
|
|
escape state
|
|
escapeIntermediate state
|
|
error state
|
|
ground state
|
|
oscString state
|
|
stateMap []state
|
|
}
|
|
|
|
func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser {
|
|
logFile := ioutil.Discard
|
|
|
|
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
|
|
logFile, _ = os.Create("ansiParser.log")
|
|
}
|
|
|
|
logger = &logrus.Logger{
|
|
Out: logFile,
|
|
Formatter: new(logrus.TextFormatter),
|
|
Level: logrus.InfoLevel,
|
|
}
|
|
|
|
parser := &AnsiParser{
|
|
eventHandler: evtHandler,
|
|
context: &ansiContext{},
|
|
}
|
|
|
|
parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}}
|
|
parser.csiParam = csiParamState{baseState{name: "CsiParam", parser: parser}}
|
|
parser.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: parser}}
|
|
parser.escape = escapeState{baseState{name: "Escape", parser: parser}}
|
|
parser.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: parser}}
|
|
parser.error = errorState{baseState{name: "Error", parser: parser}}
|
|
parser.ground = groundState{baseState{name: "Ground", parser: parser}}
|
|
parser.oscString = oscStringState{baseState{name: "OscString", parser: parser}}
|
|
|
|
parser.stateMap = []state{
|
|
parser.csiEntry,
|
|
parser.csiParam,
|
|
parser.dcsEntry,
|
|
parser.escape,
|
|
parser.escapeIntermediate,
|
|
parser.error,
|
|
parser.ground,
|
|
parser.oscString,
|
|
}
|
|
|
|
parser.currState = getState(initialState, parser.stateMap)
|
|
|
|
logger.Infof("CreateParser: parser %p", parser)
|
|
return parser
|
|
}
|
|
|
|
func getState(name string, states []state) state {
|
|
for _, el := range states {
|
|
if el.Name() == name {
|
|
return el
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ap *AnsiParser) Parse(bytes []byte) (int, error) {
|
|
for i, b := range bytes {
|
|
if err := ap.handle(b); err != nil {
|
|
return i, err
|
|
}
|
|
}
|
|
|
|
return len(bytes), ap.eventHandler.Flush()
|
|
}
|
|
|
|
func (ap *AnsiParser) handle(b byte) error {
|
|
ap.context.currentChar = b
|
|
newState, err := ap.currState.Handle(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if newState == nil {
|
|
logger.Warning("newState is nil")
|
|
return errors.New("New state of 'nil' is invalid.")
|
|
}
|
|
|
|
if newState != ap.currState {
|
|
if err := ap.changeState(newState); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ap *AnsiParser) changeState(newState state) error {
|
|
logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
|
|
|
|
// Exit old state
|
|
if err := ap.currState.Exit(); err != nil {
|
|
logger.Infof("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
|
|
return err
|
|
}
|
|
|
|
// Perform transition action
|
|
if err := ap.currState.Transition(newState); err != nil {
|
|
logger.Infof("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
|
|
return err
|
|
}
|
|
|
|
// Enter new state
|
|
if err := newState.Enter(); err != nil {
|
|
logger.Infof("Enter state '%s' failed with: '%v'", newState.Name(), err)
|
|
return err
|
|
}
|
|
|
|
ap.currState = newState
|
|
return nil
|
|
}
|