1 #include "vterm_internal.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 6 #undef DEBUG_PARSER 7 8 static bool is_intermed(unsigned char c) 9 { 10 return c >= 0x20 && c <= 0x2f; 11 } 12 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 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 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 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 77 static void start_string(VTerm *vt, VTermParserStringType type) 78 { 79 vt->parser.stringtype = type; 80 81 vt->parser.strbuffer_cur = 0; 82 } 83 84 static void more_string(VTerm *vt, const char *str, size_t len) 85 { 86 append_strbuffer(vt, str, len); 87 } 88 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 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 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 337 void *vterm_parser_get_cbdata(VTerm *vt) 338 { 339 return vt->parser.cbdata; 340 } 341