Mercurial > vim
comparison src/libvterm/src/parser.c @ 11621:b8299e742f41 v8.0.0693
patch 8.0.0693: no terminal emulator support
commit https://github.com/vim/vim/commit/e4f25e4a8db2c8a8a71a4ba2a68540b3ab341e42
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri Jul 7 11:54:15 2017 +0200
patch 8.0.0693: no terminal emulator support
Problem: No terminal emulator support. Cannot properly run commands in the
GUI. Cannot run a job interactively with an ssh connection.
Solution: Very early implementation of the :terminal command. Includes
libvterm converted to ANSI C. Many parts still missing.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Fri, 07 Jul 2017 12:00:04 +0200 |
parents | |
children | 7846efd291d7 |
comparison
equal
deleted
inserted
replaced
11620:fb788b3997c1 | 11621:b8299e742f41 |
---|---|
1 #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 | |
10 static 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 DEBUG_LOG1("libvterm: Unhandled control 0x%02x\n", control); | |
17 } | |
18 | |
19 static 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 int argcount = 1; /* Always at least 1 arg */ | |
26 long csi_args[CSI_ARGS_MAX]; | |
27 int argi; | |
28 int intermedlen = 0; | |
29 char intermed[CSI_INTERMED_MAX]; | |
30 | |
31 /* Extract leader bytes 0x3c to 0x3f */ | |
32 for( ; i < (int)arglen; i++) { | |
33 if(args[i] < 0x3c || args[i] > 0x3f) | |
34 break; | |
35 if(leaderlen < CSI_LEADER_MAX-1) | |
36 leader[leaderlen++] = args[i]; | |
37 } | |
38 | |
39 leader[leaderlen] = 0; | |
40 | |
41 for( ; i < (int)arglen; i++) | |
42 if(args[i] == 0x3b || args[i] == 0x3a) /* ; or : */ | |
43 argcount++; | |
44 | |
45 /* TODO: Consider if these buffers should live in the VTerm struct itself */ | |
46 if(argcount > CSI_ARGS_MAX) | |
47 argcount = CSI_ARGS_MAX; | |
48 | |
49 for(argi = 0; argi < argcount; argi++) | |
50 csi_args[argi] = CSI_ARG_MISSING; | |
51 | |
52 argi = 0; | |
53 for(i = leaderlen; i < (int)arglen && argi < argcount; i++) { | |
54 switch(args[i]) { | |
55 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: | |
56 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: | |
57 if(csi_args[argi] == CSI_ARG_MISSING) | |
58 csi_args[argi] = 0; | |
59 csi_args[argi] *= 10; | |
60 csi_args[argi] += args[i] - '0'; | |
61 break; | |
62 case 0x3a: | |
63 csi_args[argi] |= CSI_ARG_FLAG_MORE; | |
64 /* FALLTHROUGH */ | |
65 case 0x3b: | |
66 argi++; | |
67 break; | |
68 default: | |
69 goto done_leader; | |
70 } | |
71 } | |
72 done_leader: ; | |
73 | |
74 for( ; i < (int)arglen; i++) { | |
75 if((args[i] & 0xf0) != 0x20) | |
76 break; | |
77 | |
78 if(intermedlen < CSI_INTERMED_MAX-1) | |
79 intermed[intermedlen++] = args[i]; | |
80 } | |
81 | |
82 intermed[intermedlen] = 0; | |
83 | |
84 if(i < (int)arglen) { | |
85 DEBUG_LOG2("libvterm: TODO unhandled CSI bytes \"%.*s\"\n", (int)(arglen - i), args + i); | |
86 } | |
87 | |
88 #if 0 | |
89 printf("Parsed CSI args %.*s as:\n", arglen, args); | |
90 printf(" leader: %s\n", leader); | |
91 for(argi = 0; argi < argcount; argi++) { | |
92 printf(" %lu", CSI_ARG(csi_args[argi])); | |
93 if(!CSI_ARG_HAS_MORE(csi_args[argi])) | |
94 printf("\n"); | |
95 printf(" intermed: %s\n", intermed); | |
96 } | |
97 #endif | |
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 DEBUG_LOG3("libvterm: Unhandled CSI %.*s %c\n", (int)arglen, args, command); | |
104 } | |
105 | |
106 static 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 DEBUG_LOG1("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 | |
119 static size_t do_string(VTerm *vt, const char *str_frag, size_t len) | |
120 { | |
121 size_t eaten; | |
122 | |
123 if(vt->strbuffer_cur) { | |
124 if(str_frag) | |
125 append_strbuffer(vt, str_frag, len); | |
126 | |
127 str_frag = vt->strbuffer; | |
128 len = vt->strbuffer_cur; | |
129 } | |
130 else if(!str_frag) { | |
131 DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n"); | |
132 len = 0; | |
133 } | |
134 | |
135 vt->strbuffer_cur = 0; | |
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 DEBUG_LOG1("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 DEBUG_LOG1("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 DEBUG_LOG2("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 DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)len, str_frag); | |
179 return 0; | |
180 | |
181 case ESC_IN_OSC: | |
182 case ESC_IN_DCS: | |
183 DEBUG_LOG("libvterm: ARGH! Should never do_string() in ESC_IN_{OSC,DCS}\n"); | |
184 return 0; | |
185 } | |
186 | |
187 return 0; | |
188 } | |
189 | |
190 size_t vterm_input_write(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 DEBUG_LOG1("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 | |
339 pause: | |
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 | |
345 return len; | |
346 } |