1 package ansiterm
2
3 import (
4 "errors"
5 "log"
6 "os"
7 )
8
9 type 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
26 type Option func(*AnsiParser)
27
28 func WithLogf(f func(string, ...interface{})) Option {
29 return func(ap *AnsiParser) {
30 ap.logf = f
31 }
32 }
33
34 func 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
87 func 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
97 func (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
107 func (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
128 func (ap *AnsiParser) changeState(newState state) error {
129 ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
130
131
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
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
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
View as plain text