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 int 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_LOG1("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.v.csi.leader);
27 for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
28 printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
29 if(!CSI_ARG_HAS_MORE(vt->parser.v.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.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
38 vt->parser.v.csi.args,
39 vt->parser.v.csi.argi,
40 vt->parser.intermedlen ? vt->parser.intermed : NULL,
41 command,
42 vt->parser.cbdata))
43 return;
44
45 DEBUG_LOG1("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_LOG1("libvterm: Unhandled escape ESC 0x%02x\n", command);
62 }
63
string_fragment(VTerm * vt,const char * str,size_t len,int final)64 static void string_fragment(VTerm *vt, const char *str, size_t len, int final)
65 {
66 VTermStringFragment frag;
67
68 frag.str = str;
69 frag.len = len;
70 frag.initial = vt->parser.string_initial;
71 frag.final = final;
72
73 switch(vt->parser.state) {
74 case OSC:
75 if(vt->parser.callbacks && vt->parser.callbacks->osc)
76 (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
77 break;
78
79 case DCS:
80 if(vt->parser.callbacks && vt->parser.callbacks->dcs)
81 (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
82 break;
83
84 case APC:
85 if(vt->parser.callbacks && vt->parser.callbacks->apc)
86 (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
87 break;
88
89 case PM:
90 if(vt->parser.callbacks && vt->parser.callbacks->pm)
91 (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
92 break;
93
94 case SOS:
95 if(vt->parser.callbacks && vt->parser.callbacks->sos)
96 (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
97 break;
98
99 case NORMAL:
100 case CSI_LEADER:
101 case CSI_ARGS:
102 case CSI_INTERMED:
103 case OSC_COMMAND:
104 case DCS_COMMAND:
105 break;
106 }
107
108 vt->parser.string_initial = FALSE;
109 }
110
vterm_input_write(VTerm * vt,const char * bytes,size_t len)111 size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
112 {
113 size_t pos = 0;
114 const char *string_start = NULL; // init to avoid gcc warning
115
116 vt->in_backspace = 0; // Count down with BS key and activate when
117 // it reaches 1
118
119 switch(vt->parser.state) {
120 case NORMAL:
121 case CSI_LEADER:
122 case CSI_ARGS:
123 case CSI_INTERMED:
124 case OSC_COMMAND:
125 case DCS_COMMAND:
126 string_start = NULL;
127 break;
128 case OSC:
129 case DCS:
130 case APC:
131 case PM:
132 case SOS:
133 string_start = bytes;
134 break;
135 }
136
137 #define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
138 #define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
139
140 #define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
141
142 for( ; pos < len; pos++) {
143 unsigned char c = bytes[pos];
144 int c1_allowed = !vt->mode.utf8;
145 size_t string_len;
146
147 if(c == 0x00 || c == 0x7f) { // NUL, DEL
148 if(IS_STRING_STATE()) {
149 string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
150 string_start = bytes + pos + 1;
151 }
152 continue;
153 }
154 if(c == 0x18 || c == 0x1a) { // CAN, SUB
155 vt->parser.in_esc = FALSE;
156 ENTER_NORMAL_STATE();
157 continue;
158 }
159 else if(c == 0x1b) { // ESC
160 vt->parser.intermedlen = 0;
161 if(!IS_STRING_STATE())
162 vt->parser.state = NORMAL;
163 vt->parser.in_esc = TRUE;
164 continue;
165 }
166 else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
167 IS_STRING_STATE()) {
168 // fallthrough
169 }
170 else if(c < 0x20) { // other C0
171 if(vt->parser.state == SOS)
172 continue; // All other C0s permitted in SOS
173
174 if(vterm_get_special_pty_type() == 2) {
175 if(c == 0x08) // BS
176 // Set the trick for BS output after a sequence, to delay backspace
177 // activation
178 if(pos + 2 < len && bytes[pos + 1] == 0x20 && bytes[pos + 2] == 0x08)
179 vt->in_backspace = 2; // Trigger when count down to 1
180 }
181 if(IS_STRING_STATE())
182 string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
183 do_control(vt, c);
184 if(IS_STRING_STATE())
185 string_start = bytes + pos + 1;
186 continue;
187 }
188 // else fallthrough
189
190 string_len = bytes + pos - string_start;
191
192 if(vt->parser.in_esc) {
193 // Hoist an ESC letter into a C1 if we're not in a string mode
194 // Always accept ESC \ == ST even in string mode
195 if(!vt->parser.intermedlen &&
196 c >= 0x40 && c < 0x60 &&
197 ((!IS_STRING_STATE() || c == 0x5c))) {
198 c += 0x40;
199 c1_allowed = TRUE;
200 if(string_len)
201 string_len -= 1;
202 vt->parser.in_esc = FALSE;
203 }
204 else {
205 string_start = NULL;
206 vt->parser.state = NORMAL;
207 }
208 }
209
210 switch(vt->parser.state) {
211 case CSI_LEADER:
212 /* Extract leader bytes 0x3c to 0x3f */
213 if(c >= 0x3c && c <= 0x3f) {
214 if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
215 vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
216 break;
217 }
218
219 /* else fallthrough */
220 vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
221
222 vt->parser.v.csi.argi = 0;
223 vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
224 vt->parser.state = CSI_ARGS;
225
226 /* fallthrough */
227 case CSI_ARGS:
228 /* Numerical value of argument */
229 if(c >= '0' && c <= '9') {
230 if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
231 vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
232 vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
233 vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
234 break;
235 }
236 if(c == ':') {
237 vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
238 c = ';';
239 }
240 if(c == ';') {
241 vt->parser.v.csi.argi++;
242 vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
243 break;
244 }
245
246 /* else fallthrough */
247 vt->parser.v.csi.argi++;
248 vt->parser.intermedlen = 0;
249 vt->parser.state = CSI_INTERMED;
250 // fallthrough
251 case CSI_INTERMED:
252 if(is_intermed(c)) {
253 if(vt->parser.intermedlen < INTERMED_MAX-1)
254 vt->parser.intermed[vt->parser.intermedlen++] = c;
255 break;
256 }
257 else if(c == 0x1b) {
258 /* ESC in CSI cancels */
259 }
260 else if(c >= 0x40 && c <= 0x7e) {
261 vt->parser.intermed[vt->parser.intermedlen] = 0;
262 do_csi(vt, c);
263 }
264 /* else was invalid CSI */
265
266 ENTER_NORMAL_STATE();
267 break;
268
269 case OSC_COMMAND:
270 /* Numerical value of command */
271 if(c >= '0' && c <= '9') {
272 if(vt->parser.v.osc.command == -1)
273 vt->parser.v.osc.command = 0;
274 else
275 vt->parser.v.osc.command *= 10;
276 vt->parser.v.osc.command += c - '0';
277 break;
278 }
279 if(c == ';') {
280 vt->parser.state = OSC;
281 string_start = bytes + pos + 1;
282 break;
283 }
284
285 /* else fallthrough */
286 string_start = bytes + pos;
287 string_len = 0;
288 vt->parser.state = OSC;
289 goto string_state;
290
291 case DCS_COMMAND:
292 if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
293 vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
294
295 if(c >= 0x40 && c<= 0x7e) {
296 string_start = bytes + pos + 1;
297 vt->parser.state = DCS;
298 }
299 break;
300
301 string_state:
302 case OSC:
303 case DCS:
304 case APC:
305 case PM:
306 case SOS:
307 if(c == 0x07 || (c1_allowed && c == 0x9c)) {
308 string_fragment(vt, string_start, string_len, TRUE);
309 ENTER_NORMAL_STATE();
310 }
311 break;
312
313 case NORMAL:
314 if(vt->parser.in_esc) {
315 if(is_intermed(c)) {
316 if(vt->parser.intermedlen < INTERMED_MAX-1)
317 vt->parser.intermed[vt->parser.intermedlen++] = c;
318 }
319 else if(c >= 0x30 && c < 0x7f) {
320 do_escape(vt, c);
321 vt->parser.in_esc = 0;
322 ENTER_NORMAL_STATE();
323 }
324 else {
325 DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c);
326 }
327 break;
328 }
329 if(c1_allowed && c >= 0x80 && c < 0xa0) {
330 switch(c) {
331 case 0x90: // DCS
332 vt->parser.string_initial = TRUE;
333 vt->parser.v.dcs.commandlen = 0;
334 ENTER_STATE(DCS_COMMAND);
335 break;
336 case 0x98: // SOS
337 vt->parser.string_initial = TRUE;
338 ENTER_STATE(SOS);
339 string_start = bytes + pos + 1;
340 string_len = 0;
341 break;
342 case 0x9b: // CSI
343 vt->parser.v.csi.leaderlen = 0;
344 ENTER_STATE(CSI_LEADER);
345 break;
346 case 0x9d: // OSC
347 vt->parser.v.osc.command = -1;
348 vt->parser.string_initial = TRUE;
349 string_start = bytes + pos + 1;
350 ENTER_STATE(OSC_COMMAND);
351 break;
352 case 0x9e: // PM
353 vt->parser.string_initial = TRUE;
354 ENTER_STATE(PM);
355 string_start = bytes + pos + 1;
356 string_len = 0;
357 break;
358 case 0x9f: // APC
359 vt->parser.string_initial = TRUE;
360 ENTER_STATE(APC);
361 string_start = bytes + pos + 1;
362 string_len = 0;
363 break;
364 default:
365 do_control(vt, c);
366 break;
367 }
368 }
369 else {
370 size_t eaten = 0;
371 if(vt->parser.callbacks && vt->parser.callbacks->text)
372 eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
373
374 if(!eaten) {
375 DEBUG_LOG("libvterm: Text callback did not consume any input\n");
376 /* force it to make progress */
377 eaten = 1;
378 }
379
380 pos += (eaten - 1); // we'll ++ it again in a moment
381 }
382 break;
383 }
384 }
385
386 if(string_start) {
387 size_t string_len = bytes + pos - string_start;
388 if(vt->parser.in_esc)
389 string_len -= 1;
390 string_fragment(vt, string_start, string_len, FALSE);
391 }
392
393 return len;
394 }
395
vterm_parser_set_callbacks(VTerm * vt,const VTermParserCallbacks * callbacks,void * user)396 void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
397 {
398 vt->parser.callbacks = callbacks;
399 vt->parser.cbdata = user;
400 }
401
vterm_parser_get_cbdata(VTerm * vt)402 void *vterm_parser_get_cbdata(VTerm *vt)
403 {
404 return vt->parser.cbdata;
405 }
406