rvis | Minimal terminal colorizer. |
| download: https://git.y1.nz/archives/rvis.tar.gz | |
| README | Files | Log | Refs |
vt.c
1 /* See vt.h for documentation. */
2
3 #include <stdio.h> /* for printf in init_callbacks */
4 #include <stdlib.h> /* for malloc/free */
5
6 #include "vt.h"
7
8 /* state machine nodes */
9 #define GROUND 0
10 #define ESCAPE 1
11 #define ESCAPE_INTERMEDIATE 2
12 #define DCS_ENTRY 3
13 #define DCS_INTERMEDIATE 4
14 #define DCS_IGNORE 5
15 #define DCS_PARAM 6
16 #define DCS_PASSTHROUGH 7
17 #define CSI_IGNORE 8
18 #define CSI_PARAM 9
19 #define CSI_INTERMEDIATE 10
20 #define CSI_ENTRY 11
21 #define SOS_PM_APC_STRING 12
22 #define OSC_STRING 13
23
24 const char buffer[1000];
25
26 ParserState *
27 init_parser_state()
28 {
29 ParserState *state = malloc(sizeof(ParserState));
30 state->node = GROUND;
31 state->intermediate_char_count = 0;
32 state->intermediate_chars = buffer;
33 return state;
34 }
35
36 void
37 free_parser_state(ParserState *state)
38 {
39 free((void *) state->intermediate_chars);
40 free((void *) state);
41 }
42
43 void
44 init_parser_callbacks(ParserCallbacks *cbs, void (*pass)(ParserState *))
45 {
46 /* set all callbacks to pass by default */
47 cbs->ignore = cbs->print = cbs->execute =
48 cbs->clear = cbs->collect = cbs->param =
49 cbs->esc_dispatch = cbs->csi_dispatch = cbs->hook =
50 cbs->put = cbs->unhook = cbs->osc_start =
51 cbs->osc_put = cbs->osc_end = cbs->unhandled = pass;
52 }
53
54 /* state machine actions (just for shortening's sake) */
55 #define IGNORE callbacks->ignore(state)
56 #define PRINT callbacks->print(state)
57 #define EXECUTE callbacks->execute(state)
58 #define CLEAR callbacks->clear(state)
59 #define COLLECT callbacks->collect(state)
60 #define PARAM callbacks->param(state)
61 #define ESC_DISPATCH callbacks->esc_dispatch(state)
62 #define CSI_DISPATCH callbacks->csi_dispatch(state)
63 #define HOOK callbacks->hook(state)
64 #define PUT callbacks->put(state)
65 #define UNHOOK callbacks->unhook(state)
66 #define OSC_START callbacks->osc_start(state)
67 #define OSC_PUT callbacks->osc_put(state)
68 #define OSC_END callbacks->osc_end(state)
69 #define UNHANDLED callbacks->unhandled(state)
70
71 /* helper macros (for brevity) */
72 #define IS(value) (ch == (value))
73 #define ISR(lo, hi) ((lo) <= ch && ch <= (hi))
74 #define TO(new_node) state->node = new_node
75 #define ACTION(condition, actions) \
76 if (condition) { actions; return; }
77
78 /* special handler, for when we are moving states. */
79 /* the variadic arg should always end with a call to TO(...). */
80 #define EDGE(condition, ...) { \
81 if (condition) { \
82 /* on_exit behaviors */ \
83 switch(state->node) { \
84 case DCS_PASSTHROUGH: UNHOOK; break; \
85 case OSC_STRING: OSC_END; break; \
86 } \
87 __VA_ARGS__; \
88 /* on_entry behaviors */ \
89 switch(state->node) { \
90 case ESCAPE: CLEAR; break; \
91 case DCS_ENTRY: CLEAR; break; \
92 case CSI_ENTRY: CLEAR; break; \
93 case DCS_PASSTHROUGH: HOOK; break; \
94 case OSC_STRING: OSC_START; break; \
95 } \
96 return; \
97 } \
98 }
99
100 void
101 handle(ParserCallbacks *callbacks, ParserState *state, char ch)
102 {
103 state->current_char = ch;
104
105 /* universal transitions */
106 EDGE(IS(0x1B), TO(ESCAPE));
107 EDGE(
108 IS(0x18) || IS(0x1A) || ISR(0x80, 0x8F) ||
109 ISR(0x91, 0x97) || IS(0x99) || IS(0x9A),
110 EXECUTE, TO(GROUND)
111 );
112 EDGE(IS(0x9C), TO(GROUND));
113 EDGE(IS(0x98) || IS(0x9E) || IS(0x9F), TO(SOS_PM_APC_STRING));
114 EDGE(IS(0x90), TO(DCS_ENTRY));
115 EDGE(IS(0x9B), TO(CSI_ENTRY));
116 EDGE(IS(0x9D), TO(OSC_STRING));
117
118 /* state-specific actions + transitions (in that order) */
119 switch (state->node) {
120 case GROUND:
121 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
122 ACTION(ISR(0x20, 0x7F), PRINT);
123 break;
124
125 case ESCAPE:
126 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
127 ACTION(IS(0x7F), IGNORE);
128 EDGE(
129 ISR(0x30, 0x4F) || ISR(0x51, 0x57) ||
130 IS(0x59) || IS(0x5A) || IS(0x5C) ||
131 ISR(0x60, 0x7E),
132 ESC_DISPATCH, TO(GROUND)
133 );
134 EDGE(ISR(0x20, 0x2F), COLLECT, TO(ESCAPE_INTERMEDIATE));
135 EDGE(IS(0x58) || IS(0x5E) || IS(0x5F), TO(SOS_PM_APC_STRING));
136 EDGE(IS(0x50), TO(DCS_ENTRY));
137 EDGE(IS(0x5D), TO(OSC_STRING));
138 EDGE(IS(0x5B), TO(CSI_ENTRY));
139 break;
140
141 case ESCAPE_INTERMEDIATE:
142 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
143 ACTION(ISR(0x20, 0x2F), COLLECT);
144 ACTION(IS(0x7F), IGNORE);
145 EDGE(ISR(0x30, 0x7E), ESC_DISPATCH, TO(GROUND));
146 break;
147
148 case DCS_ENTRY:
149 ACTION(
150 ISR(0x00, 0x17) || IS(0x19) ||
151 ISR(0x1C, 0x1F) || IS(0x7F),
152 IGNORE
153 );
154 EDGE(IS(0x3A), TO(DCS_IGNORE));
155 EDGE(ISR(0x20, 0x2F), TO(DCS_INTERMEDIATE));
156 EDGE(ISR(0x30, 0x39) || IS(0x3B), PARAM, TO(DCS_PARAM));
157 EDGE(ISR(0x3C, 0x3F), COLLECT, TO(DCS_PARAM));
158 EDGE(ISR(0x40, 0x7E), TO(DCS_PASSTHROUGH));
159 break;
160
161 case DCS_INTERMEDIATE:
162 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), IGNORE);
163 ACTION(ISR(0x20, 0x2F), COLLECT);
164 ACTION(IS(0x7F), IGNORE);
165 EDGE(ISR(0x30, 0x3F), TO(DCS_IGNORE));
166 EDGE(ISR(0x40, 0x7E), TO(DCS_PASSTHROUGH));
167 break;
168
169 case DCS_PARAM:
170 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), IGNORE);
171 ACTION(ISR(0x30, 0x39) || IS(0x3B), PARAM);
172 ACTION(IS(0x7F), IGNORE);
173 EDGE(IS(0x3A) || ISR(0x3C, 0x3F), TO(DCS_IGNORE));
174 EDGE(ISR(0x20, 0x2F), COLLECT, TO(DCS_INTERMEDIATE));
175 EDGE(ISR(0x40, 0x7E), TO(DCS_PASSTHROUGH));
176 break;
177
178 case DCS_IGNORE:
179 ACTION(
180 ISR(0x00, 0x17) || IS(0x19) ||
181 ISR(0x1C, 0x1F) || ISR(0x20, 0x7F),
182 IGNORE
183 );
184 EDGE(IS(0x9C), TO(GROUND));
185 break;
186
187 case DCS_PASSTHROUGH:
188 ACTION(
189 ISR(0x00, 0x17) || IS(0x19) ||
190 ISR(0x1C, 0x1F) || ISR(0x20, 0x7E),
191 PUT
192 );
193 ACTION(IS(0x7F), IGNORE);
194 EDGE(IS(0x9C), TO(GROUND));
195 break;
196
197 case CSI_ENTRY:
198 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
199 ACTION(IS(0x7F), IGNORE);
200 EDGE(ISR(0x30, 0x39) || IS(0x3B), PARAM, TO(CSI_PARAM));
201 EDGE(ISR(0x3C, 0x3F), COLLECT, TO(CSI_PARAM));
202 EDGE(IS(0x3A), TO(CSI_IGNORE));
203 EDGE(ISR(0x20, 0x2F), COLLECT, TO(CSI_INTERMEDIATE));
204 EDGE(ISR(0x40, 0x7E), CSI_DISPATCH, TO(GROUND));
205 break;
206
207 case CSI_INTERMEDIATE:
208 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
209 ACTION(ISR(0x20, 0x2F), COLLECT);
210 ACTION(IS(0x7F), IGNORE);
211 EDGE(ISR(0x40, 0x7E), CSI_DISPATCH, TO(GROUND));
212 EDGE(ISR(0x30, 0x3F), TO(CSI_IGNORE));
213 break;
214
215 case CSI_PARAM:
216 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
217 ACTION(ISR(0x30, 0x39) || IS(0x3B), PARAM);
218 ACTION(IS(0x7F), IGNORE);
219 EDGE(ISR(0x40, 0x7E), CSI_DISPATCH, TO(GROUND));
220 EDGE(ISR(0x20, 0x2F), COLLECT, TO(CSI_INTERMEDIATE));
221 EDGE(IS(0x3A) || ISR(0x3C, 0x3F), TO(CSI_IGNORE));
222 break;
223
224 case CSI_IGNORE:
225 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
226 ACTION(ISR(0x20, 0x3F) || IS(0x7F), IGNORE);
227 EDGE(ISR(0x40, 0x7E), TO(GROUND));
228 break;
229
230 case SOS_PM_APC_STRING:
231 ACTION(
232 ISR(0x00, 0x17) || IS(0x19) ||
233 ISR(0x1C, 0x1F) || ISR(0x20, 0x7F),
234 IGNORE
235 );
236 EDGE(IS(0x9C), TO(GROUND));
237 break;
238
239 case OSC_STRING:
240 ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), IGNORE);
241 ACTION(ISR(0x20, 0x7F), OSC_PUT);
242 EDGE(IS(0x9C), TO(GROUND));
243 break;
244 }
245
246 UNHANDLED;
247 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.