1 #include "vterm_internal.h"
2
3 #include <stdio.h>
4 #include <string.h>
5
6 #undef DEBUG_PARSER
7
is_intermed(unsigned char c)8 static bool is_intermed(unsigned char c)
9 {
10 return c >= 0x20 && c <= 0x2f;
11 }
12
do_control(VTerm * vt,unsigned char control)13 static void do_control(VTerm *vt, unsigned char control)
14 {
15 if(vt->parser.callbacks && vt->parser.callbacks->control)
16 if((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
17 return;
18
19 DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
20 }
21
do_csi(VTerm * vt,char command)22 static void do_csi(VTerm *vt, char command)
23 {
24 #ifdef DEBUG_PARSER
25 printf("Parsed CSI args as:\n", arglen, args);
26 printf(" leader: %s\n", vt->parser.csi_leader);
27 for(int argi = 0; argi < vt->parser.csi_argi; argi++) {
28 printf(" %lu", CSI_ARG(vt->parser.csi_args[argi]));
29 if(!CSI_ARG_HAS_MORE(vt->parser.csi_args[argi]))
30 printf("\n");
31 printf(" intermed: %s\n", vt->parser.intermed);
32 }
33 #endif
34
35 if(vt->parser.callbacks && vt->parser.callbacks->csi)
36 if((*vt->parser.callbacks->csi)(
37 vt->parser.csi_leaderlen ? vt->parser.csi_leader : NULL,
38 vt->parser.csi_args,
39 vt->parser.csi_argi,
40 vt->parser.intermedlen ? vt->parser.intermed : NULL,
41 command,
42 vt->parser.cbdata))
43 return;
44
45 DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
46 }
47
do_escape(VTerm * vt,char command)48 static void do_escape(VTerm *vt, char command)
49 {
50 char seq[INTERMED_MAX+1];
51
52 size_t len = vt->parser.intermedlen;
53 strncpy(seq, vt->parser.intermed, len);
54 seq[len++] = command;
55 seq[len] = 0;
56
57 if(vt->parser.callbacks && vt->parser.callbacks->escape)
58 if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
59 return;
60
61 DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
62 }
63
append_strbuffer(VTerm * vt,const char * str,size_t len)64 static void append_strbuffer(VTerm *vt, const char *str, size_t len)
65 {
66 if(len > vt->parser.strbuffer_len - vt->parser.strbuffer_cur) {
67 len = vt->parser.strbuffer_len - vt->parser.strbuffer_cur;
68 DEBUG_LOG("Truncating strbuffer preserve to %zd bytes\n", len);
69 }
70
71 if(len > 0) {
72 strncpy(vt->parser.strbuffer + vt->parser.strbuffer_cur, str, len);
73 vt->parser.strbuffer_cur += len;
74 }
75 }
76
start_string(VTerm * vt,VTermParserStringType type)77 static void start_string(VTerm *vt, VTermParserStringType type)
78 {
79 vt->parser.stringtype = type;
80
81 vt->parser.strbuffer_cur = 0;
82 }
83
more_string(VTerm * vt,const char * str,size_t len)84 static void more_string(VTerm *vt, const char *str, size_t len)
85 {
86 append_strbuffer(vt, str, len);
87 }
88
done_string(VTerm * vt,const char * str,size_t len)89 static void done_string(VTerm *vt, const char *str, size_t len)
90 {
91 if(vt->parser.strbuffer_cur) {
92 if(str)
93 append_strbuffer(vt, str, len);
94
95 str = vt->parser.strbuffer;
96 len = vt->parser.strbuffer_cur;
97 }
98 else if(!str) {
99 DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n");
100 len = 0;
101 }
102
103 switch(vt->parser.stringtype) {
104 case VTERM_PARSER_OSC:
105 if(vt->parser.callbacks && vt->parser.callbacks->osc)
106 if((*vt->parser.callbacks->osc)(str, len, vt->parser.cbdata))
107 return;
108
109 DEBUG_LOG("libvterm: Unhandled OSC %.*s\n", (int)len, str);
110 return;
111
112 case VTERM_PARSER_DCS:
113 if(vt->parser.callbacks && vt->parser.callbacks->dcs)
114 if((*vt->parser.callbacks->dcs)(str, len, vt->parser.cbdata))
115 return;
116
117 DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)len, str);
118 return;
119
120 case VTERM_N_PARSER_TYPES:
121 return;
122 }
123 }
124
vterm_input_write(VTerm * vt,const char * bytes,size_t len)125 size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
126 {
127 size_t pos = 0;
128 const char *string_start;
129
130 switch(vt->parser.state) {
131 case NORMAL:
132 case CSI_LEADER:
133 case CSI_ARGS:
134 case CSI_INTERMED:
135 case ESC:
136 string_start = NULL;
137 break;
138 case STRING:
139 case ESC_IN_STRING:
140 string_start = bytes;
141 break;
142 }
143
144 #define ENTER_STRING_STATE(st) do { vt->parser.state = STRING; string_start = bytes + pos + 1; } while(0)
145 #define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
146 #define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
147
148 for( ; pos < len; pos++) {
149 unsigned char c = bytes[pos];
150
151 if(c == 0x00 || c == 0x7f) { // NUL, DEL
152 if(vt->parser.state >= STRING) {
153 more_string(vt, string_start, bytes + pos - string_start);
154 string_start = bytes + pos + 1;
155 }
156 continue;
157 }
158 if(c == 0x18 || c == 0x1a) { // CAN, SUB
159 ENTER_NORMAL_STATE();
160 continue;
161 }
162 else if(c == 0x1b) { // ESC
163 vt->parser.intermedlen = 0;
164 if(vt->parser.state == STRING)
165 vt->parser.state = ESC_IN_STRING;
166 else
167 ENTER_STATE(ESC);
168 continue;
169 }
170 else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
171 vt->parser.state == STRING) {
172 // fallthrough
173 }
174 else if(c < 0x20) { // other C0
175 if(vt->parser.state >= STRING)
176 more_string(vt, string_start, bytes + pos - string_start);
177 do_control(vt, c);
178 if(vt->parser.state >= STRING)
179 string_start = bytes + pos + 1;
180 continue;
181 }
182 // else fallthrough
183
184 switch(vt->parser.state) {
185 case ESC_IN_STRING:
186 if(c == 0x5c) { // ST
187 vt->parser.state = STRING;
188 done_string(vt, string_start, bytes + pos - string_start - 1);
189 ENTER_NORMAL_STATE();
190 break;
191 }
192 vt->parser.state = ESC;
193 // else fallthrough
194
195 case ESC:
196 switch(c) {
197 case 0x50: // DCS
198 start_string(vt, VTERM_PARSER_DCS);
199 ENTER_STRING_STATE();
200 break;
201 case 0x5b: // CSI
202 vt->parser.csi_leaderlen = 0;
203 ENTER_STATE(CSI_LEADER);
204 break;
205 case 0x5d: // OSC
206 start_string(vt, VTERM_PARSER_OSC);
207 ENTER_STRING_STATE();
208 break;
209 default:
210 if(is_intermed(c)) {
211 if(vt->parser.intermedlen < INTERMED_MAX-1)
212 vt->parser.intermed[vt->parser.intermedlen++] = c;
213 }
214 else if(!vt->parser.intermedlen && c >= 0x40 && c < 0x60) {
215 do_control(vt, c + 0x40);
216 ENTER_NORMAL_STATE();
217 }
218 else if(c >= 0x30 && c < 0x7f) {
219 do_escape(vt, c);
220 ENTER_NORMAL_STATE();
221 }
222 else {
223 DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
224 }
225 }
226 break;
227
228 case CSI_LEADER:
229 /* Extract leader bytes 0x3c to 0x3f */
230 if(c >= 0x3c && c <= 0x3f) {
231 if(vt->parser.csi_leaderlen < CSI_LEADER_MAX-1)
232 vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c;
233 break;
234 }
235
236 /* else fallthrough */
237 vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0;
238
239 vt->parser.csi_argi = 0;
240 vt->parser.csi_args[0] = CSI_ARG_MISSING;
241 vt->parser.state = CSI_ARGS;
242
243 /* fallthrough */
244 case CSI_ARGS:
245 /* Numerical value of argument */
246 if(c >= '0' && c <= '9') {
247 if(vt->parser.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING)
248 vt->parser.csi_args[vt->parser.csi_argi] = 0;
249 vt->parser.csi_args[vt->parser.csi_argi] *= 10;
250 vt->parser.csi_args[vt->parser.csi_argi] += c - '0';
251 break;
252 }
253 if(c == ':') {
254 vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE;
255 c = ';';
256 }
257 if(c == ';') {
258 vt->parser.csi_argi++;
259 vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING;
260 break;
261 }
262
263 /* else fallthrough */
264 vt->parser.csi_argi++;
265 vt->parser.intermedlen = 0;
266 vt->parser.state = CSI_INTERMED;
267 case CSI_INTERMED:
268 if(is_intermed(c)) {
269 if(vt->parser.intermedlen < INTERMED_MAX-1)
270 vt->parser.intermed[vt->parser.intermedlen++] = c;
271 break;
272 }
273 else if(c == 0x1b) {
274 /* ESC in CSI cancels */
275 }
276 else if(c >= 0x40 && c <= 0x7e) {
277 vt->parser.intermed[vt->parser.intermedlen] = 0;
278 do_csi(vt, c);
279 }
280 /* else was invalid CSI */
281
282 ENTER_NORMAL_STATE();
283 break;
284
285 case STRING:
286 if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
287 done_string(vt, string_start, bytes + pos - string_start);
288 ENTER_NORMAL_STATE();
289 }
290 break;
291
292 case NORMAL:
293 if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
294 switch(c) {
295 case 0x90: // DCS
296 start_string(vt, VTERM_PARSER_DCS);
297 ENTER_STRING_STATE();
298 break;
299 case 0x9b: // CSI
300 ENTER_STATE(CSI_LEADER);
301 break;
302 case 0x9d: // OSC
303 start_string(vt, VTERM_PARSER_OSC);
304 ENTER_STRING_STATE();
305 break;
306 default:
307 do_control(vt, c);
308 break;
309 }
310 }
311 else {
312 size_t eaten = 0;
313 if(vt->parser.callbacks && vt->parser.callbacks->text)
314 eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
315
316 if(!eaten) {
317 DEBUG_LOG("libvterm: Text callback did not consume any input\n");
318 /* force it to make progress */
319 eaten = 1;
320 }
321
322 pos += (eaten - 1); // we'll ++ it again in a moment
323 }
324 break;
325 }
326 }
327
328 return len;
329 }
330
vterm_parser_set_callbacks(VTerm * vt,const VTermParserCallbacks * callbacks,void * user)331 void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
332 {
333 vt->parser.callbacks = callbacks;
334 vt->parser.cbdata = user;
335 }
336
vterm_parser_get_cbdata(VTerm * vt)337 void *vterm_parser_get_cbdata(VTerm *vt)
338 {
339 return vt->parser.cbdata;
340 }
341