1package ansiterm 2 3import ( 4 "errors" 5 "log" 6 "os" 7) 8 9type AnsiParser struct { 10 currState state 11 eventHandler AnsiEventHandler 12 context *ansiContext 13 csiEntry state 14 csiParam state 15 dcsEntry state 16 escape state 17 escapeIntermediate state 18 error state 19 ground state 20 oscString state 21 stateMap []state 22 23 logf func(string, ...interface{}) 24} 25 26type Option func(*AnsiParser) 27 28func WithLogf(f func(string, ...interface{})) Option { 29 return func(ap *AnsiParser) { 30 ap.logf = f 31 } 32} 33 34func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser { 35 ap := &AnsiParser{ 36 eventHandler: evtHandler, 37 context: &ansiContext{}, 38 } 39 for _, o := range opts { 40 o(ap) 41 } 42 43 if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { 44 logFile, _ := os.Create("ansiParser.log") 45 logger := log.New(logFile, "", log.LstdFlags) 46 if ap.logf != nil { 47 l := ap.logf 48 ap.logf = func(s string, v ...interface{}) { 49 l(s, v...) 50 logger.Printf(s, v...) 51 } 52 } else { 53 ap.logf = logger.Printf 54 } 55 } 56 57 if ap.logf == nil { 58 ap.logf = func(string, ...interface{}) {} 59 } 60 61 ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}} 62 ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}} 63 ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}} 64 ap.escape = escapeState{baseState{name: "Escape", parser: ap}} 65 ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}} 66 ap.error = errorState{baseState{name: "Error", parser: ap}} 67 ap.ground = groundState{baseState{name: "Ground", parser: ap}} 68 ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}} 69 70 ap.stateMap = []state{ 71 ap.csiEntry, 72 ap.csiParam, 73 ap.dcsEntry, 74 ap.escape, 75 ap.escapeIntermediate, 76 ap.error, 77 ap.ground, 78 ap.oscString, 79 } 80 81 ap.currState = getState(initialState, ap.stateMap) 82 83 ap.logf("CreateParser: parser %p", ap) 84 return ap 85} 86 87func getState(name string, states []state) state { 88 for _, el := range states { 89 if el.Name() == name { 90 return el 91 } 92 } 93 94 return nil 95} 96 97func (ap *AnsiParser) Parse(bytes []byte) (int, error) { 98 for i, b := range bytes { 99 if err := ap.handle(b); err != nil { 100 return i, err 101 } 102 } 103 104 return len(bytes), ap.eventHandler.Flush() 105} 106 107func (ap *AnsiParser) handle(b byte) error { 108 ap.context.currentChar = b 109 newState, err := ap.currState.Handle(b) 110 if err != nil { 111 return err 112 } 113 114 if newState == nil { 115 ap.logf("WARNING: newState is nil") 116 return errors.New("New state of 'nil' is invalid.") 117 } 118 119 if newState != ap.currState { 120 if err := ap.changeState(newState); err != nil { 121 return err 122 } 123 } 124 125 return nil 126} 127 128func (ap *AnsiParser) changeState(newState state) error { 129 ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) 130 131 // Exit old state 132 if err := ap.currState.Exit(); err != nil { 133 ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err) 134 return err 135 } 136 137 // Perform transition action 138 if err := ap.currState.Transition(newState); err != nil { 139 ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err) 140 return err 141 } 142 143 // Enter new state 144 if err := newState.Enter(); err != nil { 145 ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err) 146 return err 147 } 148 149 ap.currState = newState 150 return nil 151} 152