package ansiterm import ( "errors" "fmt" "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(fmt.Sprintf("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 }