blob: 13bbc21997ebd98aa99363d861f282d78b8662c8 [file] [log] [blame]
Jeff Sharkey5b78a3a2013-02-19 17:28:10 -08001#include "vterm_internal.h"
2
3#include <stdio.h>
4#include <string.h>
5
6#define CSI_ARGS_MAX 16
7#define CSI_LEADER_MAX 16
8#define CSI_INTERMED_MAX 16
9
10static void do_control(VTerm *vt, unsigned char control)
11{
12 if(vt->parser_callbacks && vt->parser_callbacks->control)
13 if((*vt->parser_callbacks->control)(control, vt->cbdata))
14 return;
15
16 fprintf(stderr, "libvterm: Unhandled control 0x%02x\n", control);
17}
18
19static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char command)
20{
21 int i = 0;
22
23 int leaderlen = 0;
24 char leader[CSI_LEADER_MAX];
25
26 // Extract leader bytes 0x3c to 0x3f
27 for( ; i < arglen; i++) {
28 if(args[i] < 0x3c || args[i] > 0x3f)
29 break;
30 if(leaderlen < CSI_LEADER_MAX-1)
31 leader[leaderlen++] = args[i];
32 }
33
34 leader[leaderlen] = 0;
35
36 int argcount = 1; // Always at least 1 arg
37
38 for( ; i < arglen; i++)
39 if(args[i] == 0x3b || args[i] == 0x3a) // ; or :
40 argcount++;
41
42 /* TODO: Consider if these buffers should live in the VTerm struct itself */
43 long csi_args[CSI_ARGS_MAX];
44 if(argcount > CSI_ARGS_MAX)
45 argcount = CSI_ARGS_MAX;
46
47 int argi;
48 for(argi = 0; argi < argcount; argi++)
49 csi_args[argi] = CSI_ARG_MISSING;
50
51 argi = 0;
52 for(i = leaderlen; i < arglen && argi < argcount; i++) {
53 switch(args[i]) {
54 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
55 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
56 if(csi_args[argi] == CSI_ARG_MISSING)
57 csi_args[argi] = 0;
58 csi_args[argi] *= 10;
59 csi_args[argi] += args[i] - '0';
60 break;
61 case 0x3a:
62 csi_args[argi] |= CSI_ARG_FLAG_MORE;
63 /* FALLTHROUGH */
64 case 0x3b:
65 argi++;
66 break;
67 default:
68 goto done_leader;
69 }
70 }
71done_leader: ;
72
73 int intermedlen = 0;
74 char intermed[CSI_INTERMED_MAX];
75
76 for( ; i < arglen; i++) {
77 if((args[i] & 0xf0) != 0x20)
78 break;
79
80 if(intermedlen < CSI_INTERMED_MAX-1)
81 intermed[intermedlen++] = args[i];
82 }
83
84 intermed[intermedlen] = 0;
85
86 if(i < arglen) {
87 fprintf(stderr, "libvterm: TODO unhandled CSI bytes \"%.*s\"\n", (int)(arglen - i), args + i);
88 }
89
90 //printf("Parsed CSI args %.*s as:\n", arglen, args);
91 //printf(" leader: %s\n", leader);
92 //for(argi = 0; argi < argcount; argi++) {
93 // printf(" %lu", CSI_ARG(csi_args[argi]));
94 // if(!CSI_ARG_HAS_MORE(csi_args[argi]))
95 // printf("\n");
96 //printf(" intermed: %s\n", intermed);
97 //}
98
99 if(vt->parser_callbacks && vt->parser_callbacks->csi)
100 if((*vt->parser_callbacks->csi)(leaderlen ? leader : NULL, csi_args, argcount, intermedlen ? intermed : NULL, command, vt->cbdata))
101 return;
102
103 fprintf(stderr, "libvterm: Unhandled CSI %.*s %c\n", (int)arglen, args, command);
104}
105
106static void append_strbuffer(VTerm *vt, const char *str, size_t len)
107{
108 if(len > vt->strbuffer_len - vt->strbuffer_cur) {
109 len = vt->strbuffer_len - vt->strbuffer_cur;
110 fprintf(stderr, "Truncating strbuffer preserve to %zd bytes\n", len);
111 }
112
113 if(len > 0) {
114 strncpy(vt->strbuffer + vt->strbuffer_cur, str, len);
115 vt->strbuffer_cur += len;
116 }
117}
118
119static size_t do_string(VTerm *vt, const char *str_frag, size_t len)
120{
121 if(vt->strbuffer_cur) {
122 if(str_frag)
123 append_strbuffer(vt, str_frag, len);
124
125 str_frag = vt->strbuffer;
126 len = vt->strbuffer_cur;
127 }
128 else if(!str_frag) {
129 fprintf(stderr, "parser.c: TODO: No strbuffer _and_ no final fragment???\n");
130 len = 0;
131 }
132
133 vt->strbuffer_cur = 0;
134
135 size_t eaten;
136
137 switch(vt->parser_state) {
138 case NORMAL:
139 if(vt->parser_callbacks && vt->parser_callbacks->text)
140 if((eaten = (*vt->parser_callbacks->text)(str_frag, len, vt->cbdata)))
141 return eaten;
142
143 fprintf(stderr, "libvterm: Unhandled text (%zu chars)\n", len);
144 return 0;
145
146 case ESC:
147 if(len == 1 && str_frag[0] >= 0x40 && str_frag[0] < 0x60) {
148 // C1 emulations using 7bit clean
149 // ESC 0x40 == 0x80
150 do_control(vt, str_frag[0] + 0x40);
151 return 0;
152 }
153
154 if(vt->parser_callbacks && vt->parser_callbacks->escape)
155 if((*vt->parser_callbacks->escape)(str_frag, len, vt->cbdata))
156 return 0;
157
158 fprintf(stderr, "libvterm: Unhandled escape ESC 0x%02x\n", str_frag[len-1]);
159 return 0;
160
161 case CSI:
162 do_string_csi(vt, str_frag, len - 1, str_frag[len - 1]);
163 return 0;
164
165 case OSC:
166 if(vt->parser_callbacks && vt->parser_callbacks->osc)
167 if((*vt->parser_callbacks->osc)(str_frag, len, vt->cbdata))
168 return 0;
169
170 fprintf(stderr, "libvterm: Unhandled OSC %.*s\n", (int)len, str_frag);
171 return 0;
172
173 case DCS:
174 if(vt->parser_callbacks && vt->parser_callbacks->dcs)
175 if((*vt->parser_callbacks->dcs)(str_frag, len, vt->cbdata))
176 return 0;
177
178 fprintf(stderr, "libvterm: Unhandled DCS %.*s\n", (int)len, str_frag);
179 return 0;
180
181 case ESC_IN_OSC:
182 case ESC_IN_DCS:
183 fprintf(stderr, "libvterm: ARGH! Should never do_string() in ESC_IN_{OSC,DCS}\n");
184 return 0;
185 }
186
187 return 0;
188}
189
190void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len)
191{
192 size_t pos = 0;
193 const char *string_start;
194
195 switch(vt->parser_state) {
196 case NORMAL:
197 string_start = NULL;
198 break;
199 case ESC:
200 case ESC_IN_OSC:
201 case ESC_IN_DCS:
202 case CSI:
203 case OSC:
204 case DCS:
205 string_start = bytes;
206 break;
207 }
208
209#define ENTER_STRING_STATE(st) do { vt->parser_state = st; string_start = bytes + pos + 1; } while(0)
210#define ENTER_NORMAL_STATE() do { vt->parser_state = NORMAL; string_start = NULL; } while(0)
211
212 for( ; pos < len; pos++) {
213 unsigned char c = bytes[pos];
214
215 if(c == 0x00 || c == 0x7f) { // NUL, DEL
216 if(vt->parser_state != NORMAL) {
217 append_strbuffer(vt, string_start, bytes + pos - string_start);
218 string_start = bytes + pos + 1;
219 }
220 continue;
221 }
222 if(c == 0x18 || c == 0x1a) { // CAN, SUB
223 ENTER_NORMAL_STATE();
224 continue;
225 }
226 else if(c == 0x1b) { // ESC
227 if(vt->parser_state == OSC)
228 vt->parser_state = ESC_IN_OSC;
229 else if(vt->parser_state == DCS)
230 vt->parser_state = ESC_IN_DCS;
231 else
232 ENTER_STRING_STATE(ESC);
233 continue;
234 }
235 else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
236 (vt->parser_state == OSC || vt->parser_state == DCS)) {
237 // fallthrough
238 }
239 else if(c < 0x20) { // other C0
240 if(vt->parser_state != NORMAL)
241 append_strbuffer(vt, string_start, bytes + pos - string_start);
242 do_control(vt, c);
243 if(vt->parser_state != NORMAL)
244 string_start = bytes + pos + 1;
245 continue;
246 }
247 // else fallthrough
248
249 switch(vt->parser_state) {
250 case ESC_IN_OSC:
251 case ESC_IN_DCS:
252 if(c == 0x5c) { // ST
253 switch(vt->parser_state) {
254 case ESC_IN_OSC: vt->parser_state = OSC; break;
255 case ESC_IN_DCS: vt->parser_state = DCS; break;
256 default: break;
257 }
258 do_string(vt, string_start, bytes + pos - string_start - 1);
259 ENTER_NORMAL_STATE();
260 break;
261 }
262 vt->parser_state = ESC;
263 string_start = bytes + pos;
264 // else fallthrough
265
266 case ESC:
267 switch(c) {
268 case 0x50: // DCS
269 ENTER_STRING_STATE(DCS);
270 break;
271 case 0x5b: // CSI
272 ENTER_STRING_STATE(CSI);
273 break;
274 case 0x5d: // OSC
275 ENTER_STRING_STATE(OSC);
276 break;
277 default:
278 if(c >= 0x30 && c < 0x7f) {
279 /* +1 to pos because we want to include this command byte as well */
280 do_string(vt, string_start, bytes + pos - string_start + 1);
281 ENTER_NORMAL_STATE();
282 }
283 else if(c >= 0x20 && c < 0x30) {
284 /* intermediate byte */
285 }
286 else {
287 fprintf(stderr, "TODO: Unhandled byte %02x in Escape\n", c);
288 }
289 }
290 break;
291
292 case CSI:
293 if(c >= 0x40 && c <= 0x7f) {
294 /* +1 to pos because we want to include this command byte as well */
295 do_string(vt, string_start, bytes + pos - string_start + 1);
296 ENTER_NORMAL_STATE();
297 }
298 break;
299
300 case OSC:
301 case DCS:
302 if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
303 do_string(vt, string_start, bytes + pos - string_start);
304 ENTER_NORMAL_STATE();
305 }
306 break;
307
308 case NORMAL:
309 if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
310 switch(c) {
311 case 0x90: // DCS
312 ENTER_STRING_STATE(DCS);
313 break;
314 case 0x9b: // CSI
315 ENTER_STRING_STATE(CSI);
316 break;
317 case 0x9d: // OSC
318 ENTER_STRING_STATE(OSC);
319 break;
320 default:
321 do_control(vt, c);
322 break;
323 }
324 }
325 else {
326 size_t text_eaten = do_string(vt, bytes + pos, len - pos);
327
328 if(text_eaten == 0) {
329 string_start = bytes + pos;
330 goto pause;
331 }
332
333 pos += (text_eaten - 1); // we'll ++ it again in a moment
334 }
335 break;
336 }
337 }
338
339pause:
340 if(string_start && string_start < len + bytes) {
341 size_t remaining = len - (string_start - bytes);
342 append_strbuffer(vt, string_start, remaining);
343 }
344}