LF OS
Hobby operating system for amd64 with high ambitions
Loading...
Searching...
No Matches
parser.c
Go to the documentation of this file.
1#include "vterm_internal.h"
2
3#include <stdio.h>
4#include <string.h>
5
6#undef DEBUG_PARSER
7
8static bool is_intermed(unsigned char c)
9{
10 return c >= 0x20 && c <= 0x2f;
11}
12
13static 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
22static 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_LOG("libvterm: Unhandled CSI %c\n", command);
46}
47
48static 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
64static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
65{
66 VTermStringFragment frag = {
67 .str = str,
68 .len = len,
69 .initial = vt->parser.string_initial,
70 .final = final,
71 };
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
111size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
112{
113 size_t pos = 0;
114 const char *string_start;
115
116 switch(vt->parser.state) {
117 case NORMAL:
118 case CSI_LEADER:
119 case CSI_ARGS:
120 case CSI_INTERMED:
121 case OSC_COMMAND:
122 case DCS_COMMAND:
123 string_start = NULL;
124 break;
125 case OSC:
126 case DCS:
127 case APC:
128 case PM:
129 case SOS:
130 string_start = bytes;
131 break;
132 }
133
134#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
135#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
136
137#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
138
139 for( ; pos < len; pos++) {
140 unsigned char c = bytes[pos];
141 bool c1_allowed = !vt->mode.utf8;
142
143 if(c == 0x00 || c == 0x7f) { // NUL, DEL
144 if(IS_STRING_STATE()) {
145 string_fragment(vt, string_start, bytes + pos - string_start, false);
146 string_start = bytes + pos + 1;
147 }
148 if(vt->parser.emit_nul)
149 do_control(vt, c);
150 continue;
151 }
152 if(c == 0x18 || c == 0x1a) { // CAN, SUB
153 vt->parser.in_esc = false;
155 if(vt->parser.emit_nul)
156 do_control(vt, c);
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(IS_STRING_STATE())
175 string_fragment(vt, string_start, bytes + pos - string_start, false);
176 do_control(vt, c);
177 if(IS_STRING_STATE())
178 string_start = bytes + pos + 1;
179 continue;
180 }
181 // else fallthrough
182
183 size_t string_len = bytes + pos - string_start;
184
185 if(vt->parser.in_esc) {
186 // Hoist an ESC letter into a C1 if we're not in a string mode
187 // Always accept ESC \ == ST even in string mode
188 if(!vt->parser.intermedlen &&
189 c >= 0x40 && c < 0x60 &&
190 ((!IS_STRING_STATE() || c == 0x5c))) {
191 c += 0x40;
192 c1_allowed = true;
193 if(string_len)
194 string_len -= 1;
195 vt->parser.in_esc = false;
196 }
197 else {
198 string_start = NULL;
199 vt->parser.state = NORMAL;
200 }
201 }
202
203 switch(vt->parser.state) {
204 case CSI_LEADER:
205 /* Extract leader bytes 0x3c to 0x3f */
206 if(c >= 0x3c && c <= 0x3f) {
207 if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
208 vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
209 break;
210 }
211
212 /* else fallthrough */
213 vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
214
215 vt->parser.v.csi.argi = 0;
216 vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
217 vt->parser.state = CSI_ARGS;
218
219 /* fallthrough */
220 case CSI_ARGS:
221 /* Numerical value of argument */
222 if(c >= '0' && c <= '9') {
223 if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
224 vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
225 vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
226 vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
227 break;
228 }
229 if(c == ':') {
230 vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
231 c = ';';
232 }
233 if(c == ';') {
234 vt->parser.v.csi.argi++;
235 vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
236 break;
237 }
238
239 /* else fallthrough */
240 vt->parser.v.csi.argi++;
241 vt->parser.intermedlen = 0;
242 vt->parser.state = CSI_INTERMED;
243 case CSI_INTERMED:
244 if(is_intermed(c)) {
245 if(vt->parser.intermedlen < INTERMED_MAX-1)
246 vt->parser.intermed[vt->parser.intermedlen++] = c;
247 break;
248 }
249 else if(c == 0x1b) {
250 /* ESC in CSI cancels */
251 }
252 else if(c >= 0x40 && c <= 0x7e) {
253 vt->parser.intermed[vt->parser.intermedlen] = 0;
254 do_csi(vt, c);
255 }
256 /* else was invalid CSI */
257
259 break;
260
261 case OSC_COMMAND:
262 /* Numerical value of command */
263 if(c >= '0' && c <= '9') {
264 if(vt->parser.v.osc.command == -1)
265 vt->parser.v.osc.command = 0;
266 else
267 vt->parser.v.osc.command *= 10;
268 vt->parser.v.osc.command += c - '0';
269 break;
270 }
271 if(c == ';') {
272 vt->parser.state = OSC;
273 string_start = bytes + pos + 1;
274 break;
275 }
276
277 /* else fallthrough */
278 string_start = bytes + pos;
279 string_len = 0;
280 vt->parser.state = OSC;
281 goto string_state;
282
283 case DCS_COMMAND:
284 if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
285 vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
286
287 if(c >= 0x40 && c<= 0x7e) {
288 string_start = bytes + pos + 1;
289 vt->parser.state = DCS;
290 }
291 break;
292
293string_state:
294 case OSC:
295 case DCS:
296 case APC:
297 case PM:
298 case SOS:
299 if(c == 0x07 || (c1_allowed && c == 0x9c)) {
300 string_fragment(vt, string_start, string_len, true);
302 }
303 break;
304
305 case NORMAL:
306 if(vt->parser.in_esc) {
307 if(is_intermed(c)) {
308 if(vt->parser.intermedlen < INTERMED_MAX-1)
309 vt->parser.intermed[vt->parser.intermedlen++] = c;
310 }
311 else if(c >= 0x30 && c < 0x7f) {
312 do_escape(vt, c);
313 vt->parser.in_esc = 0;
315 }
316 else {
317 DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
318 }
319 break;
320 }
321 if(c1_allowed && c >= 0x80 && c < 0xa0) {
322 switch(c) {
323 case 0x90: // DCS
324 vt->parser.string_initial = true;
325 vt->parser.v.dcs.commandlen = 0;
326 ENTER_STATE(DCS_COMMAND);
327 break;
328 case 0x98: // SOS
329 vt->parser.string_initial = true;
330 ENTER_STATE(SOS);
331 string_start = bytes + pos + 1;
332 string_len = 0;
333 break;
334 case 0x9b: // CSI
335 vt->parser.v.csi.leaderlen = 0;
336 ENTER_STATE(CSI_LEADER);
337 break;
338 case 0x9d: // OSC
339 vt->parser.v.osc.command = -1;
340 vt->parser.string_initial = true;
341 string_start = bytes + pos + 1;
342 ENTER_STATE(OSC_COMMAND);
343 break;
344 case 0x9e: // PM
345 vt->parser.string_initial = true;
346 ENTER_STATE(PM);
347 string_start = bytes + pos + 1;
348 string_len = 0;
349 break;
350 case 0x9f: // APC
351 vt->parser.string_initial = true;
352 ENTER_STATE(APC);
353 string_start = bytes + pos + 1;
354 string_len = 0;
355 break;
356 default:
357 do_control(vt, c);
358 break;
359 }
360 }
361 else {
362 size_t eaten = 0;
363 if(vt->parser.callbacks && vt->parser.callbacks->text)
364 eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
365
366 if(!eaten) {
367 DEBUG_LOG("libvterm: Text callback did not consume any input\n");
368 /* force it to make progress */
369 eaten = 1;
370 }
371
372 pos += (eaten - 1); // we'll ++ it again in a moment
373 }
374 break;
375 }
376 }
377
378 if(string_start) {
379 size_t string_len = bytes + pos - string_start;
380 if(vt->parser.in_esc)
381 string_len -= 1;
382 string_fragment(vt, string_start, string_len, false);
383 }
384
385 return len;
386}
387
388void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
389{
390 vt->parser.callbacks = callbacks;
391 vt->parser.cbdata = user;
392}
393
395{
396 return vt->parser.cbdata;
397}
398
400{
401 vt->parser.emit_nul = emit;
402}
char * strncpy(char *s1, const char *s2, size_t n)
Definition string.c:42
void vterm_parser_set_emit_nul(VTerm *vt, bool emit)
Definition parser.c:399
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
Definition parser.c:388
#define ENTER_NORMAL_STATE()
static bool is_intermed(unsigned char c)
Definition parser.c:8
#define ENTER_STATE(st)
static void do_escape(VTerm *vt, char command)
Definition parser.c:48
static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
Definition parser.c:64
void * vterm_parser_get_cbdata(VTerm *vt)
Definition parser.c:394
#define IS_STRING_STATE()
static void do_csi(VTerm *vt, char command)
Definition parser.c:22
static void do_control(VTerm *vt, unsigned char control)
Definition parser.c:13
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
Definition parser.c:111
static VTerm * vt
Definition unterm.c:27
const char * str
Definition vterm.h:221
#define CSI_ARG_HAS_MORE(a)
Definition vterm.h:392
#define CSI_ARG_MISSING
Definition vterm.h:396
#define CSI_ARG(a)
Definition vterm.h:393
#define CSI_ARG_FLAG_MORE
Definition vterm.h:389
#define DEBUG_LOG(...)
VTermState * state
struct VTerm::@24 parser
#define CSI_LEADER_MAX
struct VTerm::@23 mode
#define INTERMED_MAX